Skip to content

feat: Add resource role assignments methods#1580

Draft
atainter wants to merge 2 commits intomainfrom
at-list-resource-role-assignments
Draft

feat: Add resource role assignments methods#1580
atainter wants to merge 2 commits intomainfrom
at-list-resource-role-assignments

Conversation

@atainter
Copy link
Copy Markdown
Contributor

@atainter atainter commented May 1, 2026

Description

Adds two new methods on Authorization for listing every role assignment granted on a resource.

  • GET /authorization/resources/:resource_id/role_assignments
  • GET /authorization/organizations/:organization_id/resources/:resource_type_slug/:external_id/role_assignments
    Both return the same shape as listOrganizationMembershipRoleAssignments. The RoleAssignment response object now also includes organizationMembershipId so callers can identify the membership a given assignment belongs to without an extra lookup.

Changes

  • Authorization.listRoleAssignmentsForResource(options) — lists role assignments by internal resourceId, supports pagination.
  • Authorization.listResourceRoleAssignments(options) — lists role assignments by organizationId + resourceTypeSlug + externalId, supports pagination.
  • Added organizationMembershipId to RoleAssignment (and organization_membership_id to RoleAssignmentResponse) and updated deserializeRoleAssignment to map it.
  • Added ListRoleAssignmentsForResourceOptions and ListRoleAssignmentsForResourceByExternalIdOptions interfaces.
  • Updated role-assignment.json and list-role-assignments.json fixtures with the new field.

Open in Devin Review

Summary by CodeRabbit

  • New Features

    • Added two new role-assignment listing methods: by resource ID and by organization + external resource ID. Both support cursor-based pagination.
    • Role assignment objects now include organization membership ID.
  • Tests

    • Expanded test coverage for the new listing methods, validating returned list structure, organization membership ID presence, and pagination behavior.

@atainter atainter requested review from a team as code owners May 1, 2026 21:37
@atainter atainter requested a review from nicknisi May 1, 2026 21:37
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bc530538-684e-4b1e-a643-dbb3a946fb60

📥 Commits

Reviewing files that changed from the base of the PR and between cf7219f and 20f918a.

📒 Files selected for processing (3)
  • src/authorization/fixtures/list-role-assignments.json
  • src/authorization/fixtures/role-assignment.json
  • src/authorization/interfaces/list-role-assignments-for-resource-by-external-id-options.interface.ts
✅ Files skipped from review due to trivial changes (1)
  • src/authorization/fixtures/list-role-assignments.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/authorization/interfaces/list-role-assignments-for-resource-by-external-id-options.interface.ts
  • src/authorization/fixtures/role-assignment.json

📝 Walkthrough

Walkthrough

Adds two Authorization methods to list role assignments (by resourceId and by organizationId/resourceTypeSlug/externalId), extends RoleAssignment types/serializer to include organizationMembershipId, updates fixtures and tests to assert the new field and pagination behavior.

Changes

Cohort / File(s) Summary
Core API Methods
src/authorization/authorization.ts
Adds listRoleAssignmentsForResource() and listResourceRoleAssignments() that build /role_assignments queries and return paginated RoleAssignment results.
Type Definitions
src/authorization/interfaces/index.ts, src/authorization/interfaces/list-role-assignments-for-resource-options.interface.ts, src/authorization/interfaces/list-role-assignments-for-resource-by-external-id-options.interface.ts, src/authorization/interfaces/role-assignment.interface.ts
Exports new list-options interfaces; adds organizationMembershipId to RoleAssignment and organization_membership_id to API response type.
Data Serialization
src/authorization/serializers/role-assignment.serializer.ts
Deserializer now maps organization_membership_id -> organizationMembershipId.
Tests & Fixtures
src/authorization/authorization.spec.ts, src/authorization/fixtures/...
Tests extended to cover the two new listing methods, assert endpoint URLs, pagination query params (limit, after, before, order with default desc), response list structure and presence of organizationMembershipId; fixtures updated to include organization_membership_id.

Possibly related PRs

Suggested reviewers

  • cmatheson
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding two new methods for listing resource role assignments, which is the core feature of this PR.
Description check ✅ Passed The PR description provides a clear explanation of the changes, including the new methods, API endpoints, model updates, and fixtures modified. However, the documentation template section is not filled out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch at-list-resource-role-assignments

Review rate limit: 3/5 reviews remaining, refill in 13 minutes and 7 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 1, 2026

Greptile Summary

This PR adds two new Authorization methods — listRoleAssignmentsForResource (by internal resource ID) and listResourceRoleAssignments (by org + resource type + external ID) — and extends RoleAssignment with organizationMembershipId. Both methods follow the existing AutoPaginatable pattern and are covered by tests. Open issues from prior review threads (nullable typing of organizationMembershipId and URL-encoding of path parameters) remain unaddressed.

Confidence Score: 4/5

Safe to merge once the open prior-thread issues (nullable organizationMembershipId typing, unencoded path parameters) are resolved or consciously accepted.

No new P0/P1 issues were found beyond what existing review threads already flagged. The implementation is consistent with existing patterns and is well-tested. Score capped at 4 due to the unresolved P1-level concerns in previous threads.

src/authorization/interfaces/role-assignment.interface.ts (organizationMembershipId nullability) and src/authorization/authorization.ts (unencoded path parameters)

Important Files Changed

Filename Overview
src/authorization/authorization.ts Adds listRoleAssignmentsForResource and listResourceRoleAssignments methods; path parameters are interpolated without URL encoding (noted in prior threads)
src/authorization/interfaces/role-assignment.interface.ts organizationMembershipId added as required string on both RoleAssignment and RoleAssignmentResponse; optionality concerns flagged in prior threads
src/authorization/serializers/role-assignment.serializer.ts Correctly maps organization_membership_id to organizationMembershipId in the deserializer
src/authorization/interfaces/list-role-assignments-for-resource-options.interface.ts New interface extending PaginationOptions with a required resourceId field; clean addition
src/authorization/interfaces/list-role-assignments-for-resource-by-external-id-options.interface.ts New interface extending PaginationOptions with organizationId, resourceTypeSlug, externalId; clean addition
src/authorization/authorization.spec.ts Test coverage added for both new methods including pagination parameters and default order; looks complete
src/authorization/fixtures/list-role-assignments.json Fixture updated to include organization_membership_id; formatting also cleaned up
src/authorization/fixtures/role-assignment.json Fixture updated to include organization_membership_id; formatting also cleaned up
src/authorization/interfaces/index.ts Two new interface exports added correctly

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Authorization
    participant WorkOS API

    Note over Caller,WorkOS API: listRoleAssignmentsForResource (by internal ID)
    Caller->>Authorization: listRoleAssignmentsForResource({ resourceId, ...pagination })
    Authorization->>WorkOS API: GET /authorization/resources/{resourceId}/role_assignments
    WorkOS API-->>Authorization: { data: [RoleAssignmentResponse], list_metadata }
    Authorization-->>Caller: AutoPaginatable<RoleAssignment>

    Note over Caller,WorkOS API: listResourceRoleAssignments (by external ID)
    Caller->>Authorization: listResourceRoleAssignments({ organizationId, resourceTypeSlug, externalId, ...pagination })
    Authorization->>WorkOS API: GET /authorization/organizations/{orgId}/resources/{typeSlug}/{externalId}/role_assignments
    WorkOS API-->>Authorization: { data: [RoleAssignmentResponse], list_metadata }
    Authorization-->>Caller: AutoPaginatable<RoleAssignment>
Loading

Reviews (2): Last reviewed commit: "prettier" | Re-trigger Greptile

Comment on lines +22 to +23
/** The ID of the organization membership the role is assigned to. */
organizationMembershipId: string;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 organizationMembershipId typed as required but may be nullable

organizationMembershipId is declared as a non-optional string, but the new resource-level listing endpoints return assignments across all org memberships. If the API can ever return a null organization_membership_id (e.g., for roles granted at an org level without a specific membership, or for legacy records), callers reading assignment.organizationMembershipId will get undefined at runtime while TypeScript guarantees it's a string. Consider typing it as string | null to match the JSON wire type accurately.

Suggested change
/** The ID of the organization membership the role is assigned to. */
organizationMembershipId: string;
/** The ID of the organization membership the role is assigned to. */
organizationMembershipId: string | null;

export interface RoleAssignmentResponse {
object: 'role_assignment';
id: string;
organization_membership_id: string;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Response interface also typed as required

RoleAssignmentResponse.organization_membership_id is declared as a non-optional string. If the API omits or nulls the field on any existing or future record, the deserializer will silently propagate undefined while TypeScript believes the field is always a string. This should stay in sync with whatever optionality is chosen for organizationMembershipId on RoleAssignment.

Suggested change
organization_membership_id: string;
organization_membership_id: string | null;

Comment on lines +951 to +952
),
(params) =>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 externalId is not URL-encoded in the path

externalId is interpolated directly into the URL path. If a caller passes a value containing /, ?, #, or other URL-special characters (e.g. "docs/2024", "proj #5"), the resulting URL will be malformed and the request will hit the wrong endpoint or fail. The same pattern exists in getOrganizationResource and other methods, but this new endpoint exposes the same pre-existing gap. Consider wrapping each path segment with encodeURIComponent.

Suggested change
),
(params) =>
const endpoint = `/authorization/organizations/${encodeURIComponent(organizationId)}/resources/${encodeURIComponent(resourceTypeSlug)}/${encodeURIComponent(externalId)}/role_assignments`;

coderabbitai[bot]

This comment was marked as resolved.

@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 1, 2026

@atainter atainter marked this pull request as draft May 1, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant