diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 2d700f31d..49a7c469b 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -17,6 +17,7 @@ import listResourcesFixture from './fixtures/list-resources.json'; import roleAssignmentFixture from './fixtures/role-assignment.json'; import listRoleAssignmentsFixture from './fixtures/list-role-assignments.json'; import listOrganizationMembershipsForResourceFixture from './fixtures/list-organization-memberships-for-resource.json'; +import listEffectivePermissionsFixture from './fixtures/list-effective-permissions.json'; const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); const testOrgId = 'org_01HXYZ123ABC456DEF789ABC'; @@ -568,13 +569,12 @@ describe('Authorization', () => { it('returns permissions', async () => { fetchOnce(listPermissionsFixture); - const { data, object, listMetadata } = - await workos.authorization.listPermissions(); + const result = await workos.authorization.listPermissions(); expect(fetchURL()).toContain('/authorization/permissions'); - expect(object).toEqual('list'); - expect(data).toHaveLength(2); - expect(data).toEqual( + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(2); + expect(result.data).toEqual( expect.arrayContaining([ expect.objectContaining({ object: 'permission', @@ -592,7 +592,7 @@ describe('Authorization', () => { }), ]), ); - expect(listMetadata).toEqual({ + expect(result.listMetadata).toEqual({ before: null, after: 'perm_01HXYZ123ABC456DEF789GHJ', }); @@ -613,6 +613,36 @@ describe('Authorization', () => { order: 'desc', }); }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listPermissionsFixture); + + await workos.authorization.listPermissions(); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + + it('passes order asc when explicitly set', async () => { + fetchOnce(listPermissionsFixture); + + await workos.authorization.listPermissions({ order: 'asc' }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'asc', + }); + }); + + it('passes order desc when explicitly set', async () => { + fetchOnce(listPermissionsFixture); + + await workos.authorization.listPermissions({ order: 'desc' }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); }); describe('getPermission', () => { @@ -1146,13 +1176,12 @@ describe('Authorization', () => { it('returns a paginated list of resources', async () => { fetchOnce(listResourcesFixture); - const { data, object, listMetadata } = - await workos.authorization.listResources(); + const result = await workos.authorization.listResources(); expect(fetchURL()).toContain('/authorization/resources'); - expect(object).toEqual('list'); - expect(data).toHaveLength(2); - expect(data).toEqual( + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(2); + expect(result.data).toEqual( expect.arrayContaining([ expect.objectContaining({ object: 'authorization_resource', @@ -1170,7 +1199,7 @@ describe('Authorization', () => { }), ]), ); - expect(listMetadata).toEqual({ + expect(result.listMetadata).toEqual({ before: null, after: 'authz_resource_01HXYZ123ABC456DEF789DEF', }); @@ -1217,6 +1246,7 @@ describe('Authorization', () => { expect(fetchSearchParams()).toEqual({ parent_resource_id: 'resource_01HXYZ123ABC456DEF789XYZ', + order: 'desc', }); }); @@ -1231,6 +1261,7 @@ describe('Authorization', () => { expect(fetchSearchParams()).toEqual({ parent_resource_type_slug: 'folder', parent_external_id: 'folder-123', + order: 'desc', }); }); @@ -1243,6 +1274,37 @@ describe('Authorization', () => { expect(fetchSearchParams()).toEqual({ search: 'Budget', + order: 'desc', + }); + }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources(); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + + it('passes order asc when explicitly set', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ order: 'asc' }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'asc', + }); + }); + + it('passes order desc when explicitly set', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ order: 'desc' }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', }); }); }); @@ -1578,17 +1640,16 @@ describe('Authorization', () => { it('lists role assignments for an organization membership', async () => { fetchOnce(listRoleAssignmentsFixture); - const { data, object, listMetadata } = - await workos.authorization.listRoleAssignments({ - organizationMembershipId: testOrgMembershipId, - }); + const result = await workos.authorization.listRoleAssignments({ + organizationMembershipId: testOrgMembershipId, + }); expect(fetchURL()).toContain( `/authorization/organization_memberships/${testOrgMembershipId}/role_assignments`, ); - expect(object).toEqual('list'); - expect(data).toHaveLength(1); - expect(data[0]).toMatchObject({ + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(1); + expect(result.data[0]).toMatchObject({ object: 'role_assignment', id: 'role_assignment_01HXYZ123ABC456DEF789ABC', role: { slug: 'editor' }, @@ -1600,7 +1661,7 @@ describe('Authorization', () => { createdAt: '2024-01-15T09:30:00.000Z', updatedAt: '2024-01-15T09:30:00.000Z', }); - expect(listMetadata).toEqual({ + expect(result.listMetadata).toEqual({ before: null, after: 'role_assignment_01HXYZ123ABC456DEF789ABC', }); @@ -1639,6 +1700,44 @@ describe('Authorization', () => { order: 'asc', }); }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listRoleAssignmentsFixture); + + await workos.authorization.listRoleAssignments({ + organizationMembershipId: testOrgMembershipId, + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + + it('passes order asc when explicitly set', async () => { + fetchOnce(listRoleAssignmentsFixture); + + await workos.authorization.listRoleAssignments({ + organizationMembershipId: testOrgMembershipId, + order: 'asc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'asc', + }); + }); + + it('passes order desc when explicitly set', async () => { + fetchOnce(listRoleAssignmentsFixture); + + await workos.authorization.listRoleAssignments({ + organizationMembershipId: testOrgMembershipId, + order: 'desc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); }); describe('assignRole', () => { @@ -1811,12 +1910,11 @@ describe('Authorization', () => { it('lists resources with parentResourceId', async () => { fetchOnce(listResourcesFixture); - const { data, object, listMetadata } = - await workos.authorization.listResourcesForMembership({ - organizationMembershipId: testOrgMembershipId, - permissionSlug: 'document:read', - parentResourceId: testResourceId, - }); + const result = await workos.authorization.listResourcesForMembership({ + organizationMembershipId: testOrgMembershipId, + permissionSlug: 'document:read', + parentResourceId: testResourceId, + }); expect(fetchURL()).toContain( `/authorization/organization_memberships/${testOrgMembershipId}/resources`, @@ -1825,13 +1923,13 @@ describe('Authorization', () => { permission_slug: 'document:read', parent_resource_id: testResourceId, }); - expect(object).toEqual('list'); - expect(data).toHaveLength(2); - expect(data[0]).toMatchObject({ + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(2); + expect(result.data[0]).toMatchObject({ object: 'authorization_resource', id: 'authz_resource_01HXYZ123ABC456DEF789ABC', }); - expect(listMetadata).toEqual({ + expect(result.listMetadata).toEqual({ before: null, after: 'authz_resource_01HXYZ123ABC456DEF789DEF', }); @@ -1895,17 +1993,60 @@ describe('Authorization', () => { order: 'asc', }); }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResourcesForMembership({ + organizationMembershipId: testOrgMembershipId, + permissionSlug: 'document:read', + parentResourceId: testResourceId, + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + + it('passes order asc when explicitly set', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResourcesForMembership({ + organizationMembershipId: testOrgMembershipId, + permissionSlug: 'document:read', + parentResourceId: testResourceId, + order: 'asc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'asc', + }); + }); + + it('passes order desc when explicitly set', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResourcesForMembership({ + organizationMembershipId: testOrgMembershipId, + permissionSlug: 'document:read', + parentResourceId: testResourceId, + order: 'desc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); }); describe('listMembershipsForResource', () => { it('lists organization memberships for a resource by internal ID', async () => { fetchOnce(listOrganizationMembershipsForResourceFixture); - const { data, object, listMetadata } = - await workos.authorization.listMembershipsForResource({ - resourceId: testResourceId, - permissionSlug: 'documents:read', - }); + const result = await workos.authorization.listMembershipsForResource({ + resourceId: testResourceId, + permissionSlug: 'documents:read', + }); expect(fetchURL()).toContain( `/authorization/resources/${testResourceId}/organization_memberships`, @@ -1913,9 +2054,9 @@ describe('Authorization', () => { expect(fetchSearchParams()).toMatchObject({ permission_slug: 'documents:read', }); - expect(object).toEqual('list'); - expect(data).toHaveLength(1); - expect(data[0]).toMatchObject({ + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(1); + expect(result.data[0]).toMatchObject({ object: 'organization_membership', id: 'om_01HXYZ123ABC456DEF789ABC', userId: 'user_01HXYZ123ABC456DEF789XYZ', @@ -1923,7 +2064,7 @@ describe('Authorization', () => { status: 'active', customAttributes: { department: 'Engineering', level: 'senior' }, }); - expect(listMetadata).toEqual({ + expect(result.listMetadata).toEqual({ before: null, after: 'om_01HXYZ123ABC456DEF789ABC', }); @@ -1937,15 +2078,14 @@ describe('Authorization', () => { }; fetchOnce(emptyFixture); - const { data, object, listMetadata } = - await workos.authorization.listMembershipsForResource({ - resourceId: testResourceId, - permissionSlug: 'documents:read', - }); + const result = await workos.authorization.listMembershipsForResource({ + resourceId: testResourceId, + permissionSlug: 'documents:read', + }); - expect(object).toEqual('list'); - expect(data).toHaveLength(0); - expect(listMetadata).toEqual({ before: null, after: null }); + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(0); + expect(result.listMetadata).toEqual({ before: null, after: null }); }); it('passes pagination parameters', async () => { @@ -1996,12 +2136,53 @@ describe('Authorization', () => { }; fetchOnce(fixtureWithoutCustomAttrs); - const { data } = await workos.authorization.listMembershipsForResource({ + const result = await workos.authorization.listMembershipsForResource({ + resourceId: testResourceId, + permissionSlug: 'documents:read', + }); + + expect(result.data[0].customAttributes).toEqual({}); + }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listOrganizationMembershipsForResourceFixture); + + await workos.authorization.listMembershipsForResource({ resourceId: testResourceId, permissionSlug: 'documents:read', }); - expect(data[0].customAttributes).toEqual({}); + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + + it('passes order asc when explicitly set', async () => { + fetchOnce(listOrganizationMembershipsForResourceFixture); + + await workos.authorization.listMembershipsForResource({ + resourceId: testResourceId, + permissionSlug: 'documents:read', + order: 'asc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'asc', + }); + }); + + it('passes order desc when explicitly set', async () => { + fetchOnce(listOrganizationMembershipsForResourceFixture); + + await workos.authorization.listMembershipsForResource({ + resourceId: testResourceId, + permissionSlug: 'documents:read', + order: 'desc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); }); }); @@ -2009,7 +2190,7 @@ describe('Authorization', () => { it('lists organization memberships for a resource by external ID', async () => { fetchOnce(listOrganizationMembershipsForResourceFixture); - const { data, object, listMetadata } = + const result = await workos.authorization.listMembershipsForResourceByExternalId({ organizationId: testOrgId, resourceTypeSlug: 'document', @@ -2023,9 +2204,9 @@ describe('Authorization', () => { expect(fetchSearchParams()).toMatchObject({ permission_slug: 'documents:read', }); - expect(object).toEqual('list'); - expect(data).toHaveLength(1); - expect(data[0]).toMatchObject({ + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(1); + expect(result.data[0]).toMatchObject({ object: 'organization_membership', id: 'om_01HXYZ123ABC456DEF789ABC', userId: 'user_01HXYZ123ABC456DEF789XYZ', @@ -2033,7 +2214,7 @@ describe('Authorization', () => { status: 'active', customAttributes: { department: 'Engineering', level: 'senior' }, }); - expect(listMetadata).toEqual({ + expect(result.listMetadata).toEqual({ before: null, after: 'om_01HXYZ123ABC456DEF789ABC', }); @@ -2095,5 +2276,229 @@ describe('Authorization', () => { assignment: 'direct', }); }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listOrganizationMembershipsForResourceFixture); + + await workos.authorization.listMembershipsForResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + permissionSlug: 'documents:read', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + + it('passes order asc when explicitly set', async () => { + fetchOnce(listOrganizationMembershipsForResourceFixture); + + await workos.authorization.listMembershipsForResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + permissionSlug: 'documents:read', + order: 'asc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'asc', + }); + }); + + it('passes order desc when explicitly set', async () => { + fetchOnce(listOrganizationMembershipsForResourceFixture); + + await workos.authorization.listMembershipsForResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + permissionSlug: 'documents:read', + order: 'desc', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + }); + + describe('listEffectivePermissions', () => { + it('lists effective permissions for a membership on a resource', async () => { + fetchOnce(listEffectivePermissionsFixture); + + const result = await workos.authorization.listEffectivePermissions({ + organizationMembershipId: testOrgMembershipId, + resourceId: testResourceId, + }); + + expect(fetchURL()).toContain( + `/authorization/resources/${testResourceId}/organization_memberships/${testOrgMembershipId}/permissions`, + ); + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(2); + expect(result.data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + object: 'permission', + id: 'perm_01HXYZ123ABC456DEF789GHI', + slug: 'documents:read', + name: 'Read Documents', + resourceTypeSlug: 'document', + }), + expect.objectContaining({ + object: 'permission', + id: 'perm_01HXYZ123ABC456DEF789GHJ', + slug: 'documents:edit', + name: 'Edit Documents', + resourceTypeSlug: 'document', + }), + ]), + ); + expect(result.listMetadata).toEqual({ + before: null, + after: 'perm_01HXYZ123ABC456DEF789GHJ', + }); + }); + + it('passes pagination parameters', async () => { + fetchOnce(listEffectivePermissionsFixture); + + await workos.authorization.listEffectivePermissions({ + organizationMembershipId: testOrgMembershipId, + resourceId: testResourceId, + limit: 10, + after: 'perm_cursor123', + order: 'desc', + }); + + expect(fetchSearchParams()).toMatchObject({ + limit: '10', + after: 'perm_cursor123', + order: 'desc', + }); + }); + + it('passes before cursor for backward pagination', async () => { + fetchOnce(listEffectivePermissionsFixture); + + await workos.authorization.listEffectivePermissions({ + organizationMembershipId: testOrgMembershipId, + resourceId: testResourceId, + before: 'perm_cursor789', + order: 'asc', + }); + + expect(fetchSearchParams()).toMatchObject({ + before: 'perm_cursor789', + order: 'asc', + }); + }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listEffectivePermissionsFixture); + + await workos.authorization.listEffectivePermissions({ + organizationMembershipId: testOrgMembershipId, + resourceId: testResourceId, + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); + }); + + describe('listEffectivePermissionsByExternalId', () => { + it('lists effective permissions for a membership on a resource by external ID', async () => { + fetchOnce(listEffectivePermissionsFixture); + + const result = + await workos.authorization.listEffectivePermissionsByExternalId({ + organizationMembershipId: testOrgMembershipId, + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(fetchURL()).toContain( + `/authorization/organizations/${testOrgId}/resources/document/doc-456/organization_memberships/${testOrgMembershipId}/permissions`, + ); + expect(result.object).toEqual('list'); + expect(result.data).toHaveLength(2); + expect(result.data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + object: 'permission', + slug: 'documents:read', + resourceTypeSlug: 'document', + }), + expect.objectContaining({ + object: 'permission', + slug: 'documents:edit', + resourceTypeSlug: 'document', + }), + ]), + ); + expect(result.listMetadata).toEqual({ + before: null, + after: 'perm_01HXYZ123ABC456DEF789GHJ', + }); + }); + + it('passes pagination parameters', async () => { + fetchOnce(listEffectivePermissionsFixture); + + await workos.authorization.listEffectivePermissionsByExternalId({ + organizationMembershipId: testOrgMembershipId, + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + limit: 10, + after: 'perm_cursor123', + order: 'desc', + }); + + expect(fetchSearchParams()).toMatchObject({ + limit: '10', + after: 'perm_cursor123', + order: 'desc', + }); + }); + + it('passes before cursor for backward pagination', async () => { + fetchOnce(listEffectivePermissionsFixture); + + await workos.authorization.listEffectivePermissionsByExternalId({ + organizationMembershipId: testOrgMembershipId, + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + before: 'perm_cursor789', + order: 'asc', + }); + + expect(fetchSearchParams()).toMatchObject({ + before: 'perm_cursor789', + order: 'asc', + }); + }); + + it('defaults to desc order when order is not specified', async () => { + fetchOnce(listEffectivePermissionsFixture); + + await workos.authorization.listEffectivePermissionsByExternalId({ + organizationMembershipId: testOrgMembershipId, + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(fetchSearchParams()).toMatchObject({ + order: 'desc', + }); + }); }); }); diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 534a31e71..fe394d610 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -1,4 +1,6 @@ import { WorkOS } from '../workos'; +import { AutoPaginatable } from '../common/utils/pagination'; +import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize'; import { Role, RoleList, @@ -22,15 +24,11 @@ import { RemoveOrganizationRolePermissionOptions, Permission, PermissionResponse, - PermissionList, - PermissionListResponse, CreatePermissionOptions, UpdatePermissionOptions, ListPermissionsOptions, AuthorizationResource, AuthorizationResourceResponse, - AuthorizationResourceList, - AuthorizationResourceListResponse, ListAuthorizationResourcesOptions, GetAuthorizationResourceByExternalIdOptions, UpdateAuthorizationResourceByExternalIdOptions, @@ -45,12 +43,12 @@ import { RemoveRoleAssignmentOptions, RemoveRoleOptions, RoleAssignment, - RoleAssignmentList, - RoleAssignmentListResponse, RoleAssignmentResponse, ListMembershipsForResourceByExternalIdOptions, ListMembershipsForResourceOptions, ListResourcesForMembershipOptions, + ListEffectivePermissionsOptions, + ListEffectivePermissionsByExternalIdOptions, } from './interfaces'; import { deserializeEnvironmentRole, @@ -74,10 +72,11 @@ import { serializeRemoveRoleOptions, serializeListMembershipsForResourceOptions, serializeListResourcesForMembershipOptions, + serializeListEffectivePermissionsOptions, } from './serializers'; import { - AuthorizationOrganizationMembershipList, - AuthorizationOrganizationMembershipListResponse, + AuthorizationOrganizationMembership, + AuthorizationOrganizationMembershipResponse, } from '../user-management/interfaces/organization-membership.interface'; import { deserializeAuthorizationOrganizationMembership } from '../user-management/serializers/organization-membership.serializer'; @@ -242,19 +241,23 @@ export class Authorization { async listPermissions( options?: ListPermissionsOptions, - ): Promise { - const { data } = await this.workos.get( - '/authorization/permissions', - { query: options }, + ): Promise> { + return new AutoPaginatable( + await fetchAndDeserialize( + this.workos, + '/authorization/permissions', + deserializePermission, + options, + ), + (params) => + fetchAndDeserialize( + this.workos, + '/authorization/permissions', + deserializePermission, + params, + ), + options, ); - return { - object: 'list', - data: data.data.map(deserializePermission), - listMetadata: { - before: data.list_metadata.before, - after: data.list_metadata.after, - }, - }; } async getPermission(slug: string): Promise { @@ -321,19 +324,31 @@ export class Authorization { async listResources( options: ListAuthorizationResourcesOptions = {}, - ): Promise { - const { data } = await this.workos.get( - '/authorization/resources', - { query: serializeListAuthorizationResourcesOptions(options) }, + ): Promise> { + const serializedOptions = + serializeListAuthorizationResourcesOptions(options); + return new AutoPaginatable( + await fetchAndDeserialize< + AuthorizationResourceResponse, + AuthorizationResource + >( + this.workos, + '/authorization/resources', + deserializeAuthorizationResource, + serializedOptions, + ), + (params) => + fetchAndDeserialize< + AuthorizationResourceResponse, + AuthorizationResource + >( + this.workos, + '/authorization/resources', + deserializeAuthorizationResource, + params, + ), + serializedOptions, ); - return { - object: 'list', - data: data.data.map(deserializeAuthorizationResource), - listMetadata: { - before: data.list_metadata.before, - after: data.list_metadata.after, - }, - }; } async getResourceByExternalId( @@ -385,20 +400,25 @@ export class Authorization { async listRoleAssignments( options: ListRoleAssignmentsOptions, - ): Promise { + ): Promise> { const { organizationMembershipId, ...queryOptions } = options; - const { data } = await this.workos.get( - `/authorization/organization_memberships/${organizationMembershipId}/role_assignments`, - { query: queryOptions }, + const endpoint = `/authorization/organization_memberships/${organizationMembershipId}/role_assignments`; + return new AutoPaginatable( + await fetchAndDeserialize( + this.workos, + endpoint, + deserializeRoleAssignment, + queryOptions, + ), + (params) => + fetchAndDeserialize( + this.workos, + endpoint, + deserializeRoleAssignment, + params, + ), + queryOptions, ); - return { - object: 'list', - data: data.data.map(deserializeRoleAssignment), - listMetadata: { - before: data.list_metadata.before, - after: data.list_metadata.after, - }, - }; } async assignRole(options: AssignRoleOptions): Promise { @@ -426,63 +446,142 @@ export class Authorization { async listResourcesForMembership( options: ListResourcesForMembershipOptions, - ): Promise { + ): Promise> { const { organizationMembershipId } = options; - const { data } = await this.workos.get( - `/authorization/organization_memberships/${organizationMembershipId}/resources`, - { - query: serializeListResourcesForMembershipOptions(options), - }, + const endpoint = `/authorization/organization_memberships/${organizationMembershipId}/resources`; + const serializedOptions = + serializeListResourcesForMembershipOptions(options); + return new AutoPaginatable( + await fetchAndDeserialize< + AuthorizationResourceResponse, + AuthorizationResource + >( + this.workos, + endpoint, + deserializeAuthorizationResource, + serializedOptions, + ), + (params) => + fetchAndDeserialize< + AuthorizationResourceResponse, + AuthorizationResource + >(this.workos, endpoint, deserializeAuthorizationResource, params), + serializedOptions, ); - return { - object: 'list', - data: data.data.map(deserializeAuthorizationResource), - listMetadata: { - before: data.list_metadata.before, - after: data.list_metadata.after, - }, - }; } async listMembershipsForResource( options: ListMembershipsForResourceOptions, - ): Promise { + ): Promise> { const { resourceId } = options; - const { data } = - await this.workos.get( - `/authorization/resources/${resourceId}/organization_memberships`, - { - query: serializeListMembershipsForResourceOptions(options), - }, - ); - return { - object: 'list', - data: data.data.map(deserializeAuthorizationOrganizationMembership), - listMetadata: { - before: data.list_metadata.before, - after: data.list_metadata.after, - }, - }; + const endpoint = `/authorization/resources/${resourceId}/organization_memberships`; + const serializedOptions = + serializeListMembershipsForResourceOptions(options); + return new AutoPaginatable( + await fetchAndDeserialize< + AuthorizationOrganizationMembershipResponse, + AuthorizationOrganizationMembership + >( + this.workos, + endpoint, + deserializeAuthorizationOrganizationMembership, + serializedOptions, + ), + (params) => + fetchAndDeserialize< + AuthorizationOrganizationMembershipResponse, + AuthorizationOrganizationMembership + >( + this.workos, + endpoint, + deserializeAuthorizationOrganizationMembership, + params, + ), + serializedOptions, + ); } async listMembershipsForResourceByExternalId( options: ListMembershipsForResourceByExternalIdOptions, - ): Promise { + ): Promise> { const { organizationId, resourceTypeSlug, externalId } = options; - const { data } = - await this.workos.get( - `/authorization/organizations/${organizationId}/resources/${resourceTypeSlug}/${externalId}/organization_memberships`, - { - query: serializeListMembershipsForResourceOptions(options), - }, - ); - return { - object: 'list', - data: data.data.map(deserializeAuthorizationOrganizationMembership), - listMetadata: { - before: data.list_metadata.before, - after: data.list_metadata.after, - }, - }; + const endpoint = `/authorization/organizations/${organizationId}/resources/${resourceTypeSlug}/${externalId}/organization_memberships`; + const serializedOptions = + serializeListMembershipsForResourceOptions(options); + return new AutoPaginatable( + await fetchAndDeserialize< + AuthorizationOrganizationMembershipResponse, + AuthorizationOrganizationMembership + >( + this.workos, + endpoint, + deserializeAuthorizationOrganizationMembership, + serializedOptions, + ), + (params) => + fetchAndDeserialize< + AuthorizationOrganizationMembershipResponse, + AuthorizationOrganizationMembership + >( + this.workos, + endpoint, + deserializeAuthorizationOrganizationMembership, + params, + ), + serializedOptions, + ); + } + + async listEffectivePermissions( + options: ListEffectivePermissionsOptions, + ): Promise> { + const { organizationMembershipId, resourceId } = options; + const endpoint = `/authorization/resources/${resourceId}/organization_memberships/${organizationMembershipId}/permissions`; + const serializedOptions = serializeListEffectivePermissionsOptions(options); + return new AutoPaginatable( + await fetchAndDeserialize( + this.workos, + endpoint, + deserializePermission, + serializedOptions, + ), + (params) => + fetchAndDeserialize( + this.workos, + endpoint, + deserializePermission, + params, + ), + serializedOptions, + ); + } + + async listEffectivePermissionsByExternalId( + options: ListEffectivePermissionsByExternalIdOptions, + ): Promise> { + const { + organizationMembershipId, + organizationId, + resourceTypeSlug, + externalId, + } = options; + const endpoint = `/authorization/organizations/${organizationId}/resources/${resourceTypeSlug}/${externalId}/organization_memberships/${organizationMembershipId}/permissions`; + const serializedOptions = serializeListEffectivePermissionsOptions(options); + return new AutoPaginatable( + await fetchAndDeserialize( + this.workos, + endpoint, + deserializePermission, + serializedOptions, + ), + (params) => + fetchAndDeserialize( + this.workos, + endpoint, + deserializePermission, + params, + ), + serializedOptions, + ); } } diff --git a/src/authorization/fixtures/list-effective-permissions.json b/src/authorization/fixtures/list-effective-permissions.json new file mode 100644 index 000000000..f56dca244 --- /dev/null +++ b/src/authorization/fixtures/list-effective-permissions.json @@ -0,0 +1,31 @@ +{ + "object": "list", + "data": [ + { + "object": "permission", + "id": "perm_01HXYZ123ABC456DEF789GHI", + "slug": "documents:read", + "name": "Read Documents", + "description": "Allows reading documents", + "resource_type_slug": "document", + "system": false, + "created_at": "2024-01-15T08:00:00.000Z", + "updated_at": "2024-01-15T08:00:00.000Z" + }, + { + "object": "permission", + "id": "perm_01HXYZ123ABC456DEF789GHJ", + "slug": "documents:edit", + "name": "Edit Documents", + "description": "Allows editing documents", + "resource_type_slug": "document", + "system": false, + "created_at": "2024-01-15T09:00:00.000Z", + "updated_at": "2024-01-15T09:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": "perm_01HXYZ123ABC456DEF789GHJ" + } +} diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index 51cfc6d0f..9af08050a 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -67,21 +67,3 @@ export interface SerializedUpdateAuthorizationResourceOptions { name?: string; description?: string | null; } - -export interface AuthorizationResourceList { - object: 'list'; - data: AuthorizationResource[]; - listMetadata: { - before: string | null; - after: string | null; - }; -} - -export interface AuthorizationResourceListResponse { - object: 'list'; - data: AuthorizationResourceResponse[]; - list_metadata: { - before: string | null; - after: string | null; - }; -} diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index a42183cd9..2a89427a6 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -28,3 +28,5 @@ export * from './list-role-assignments-options.interface'; export * from './assign-role-options.interface'; export * from './remove-role-options.interface'; export * from './remove-role-assignment-options.interface'; +export * from './list-effective-permissions-options.interface'; +export * from './list-effective-permissions-by-external-id-options.interface'; diff --git a/src/authorization/interfaces/list-effective-permissions-by-external-id-options.interface.ts b/src/authorization/interfaces/list-effective-permissions-by-external-id-options.interface.ts new file mode 100644 index 000000000..7bff1d6b9 --- /dev/null +++ b/src/authorization/interfaces/list-effective-permissions-by-external-id-options.interface.ts @@ -0,0 +1,8 @@ +import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; + +export interface ListEffectivePermissionsByExternalIdOptions extends PaginationOptions { + organizationMembershipId: string; + organizationId: string; + resourceTypeSlug: string; + externalId: string; +} diff --git a/src/authorization/interfaces/list-effective-permissions-options.interface.ts b/src/authorization/interfaces/list-effective-permissions-options.interface.ts new file mode 100644 index 000000000..583aaf719 --- /dev/null +++ b/src/authorization/interfaces/list-effective-permissions-options.interface.ts @@ -0,0 +1,6 @@ +import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; + +export interface ListEffectivePermissionsOptions extends PaginationOptions { + organizationMembershipId: string; + resourceId: string; +} diff --git a/src/authorization/interfaces/permission.interface.ts b/src/authorization/interfaces/permission.interface.ts index 0af2b6da0..8c6774a78 100644 --- a/src/authorization/interfaces/permission.interface.ts +++ b/src/authorization/interfaces/permission.interface.ts @@ -21,21 +21,3 @@ export interface PermissionResponse { created_at: string; updated_at: string; } - -export interface PermissionList { - object: 'list'; - data: Permission[]; - listMetadata: { - before: string | null; - after: string | null; - }; -} - -export interface PermissionListResponse { - object: 'list'; - data: PermissionResponse[]; - list_metadata: { - before: string | null; - after: string | null; - }; -} diff --git a/src/authorization/interfaces/role-assignment.interface.ts b/src/authorization/interfaces/role-assignment.interface.ts index 83167ed9c..1589b6813 100644 --- a/src/authorization/interfaces/role-assignment.interface.ts +++ b/src/authorization/interfaces/role-assignment.interface.ts @@ -31,21 +31,3 @@ export interface RoleAssignmentResponse { created_at: string; updated_at: string; } - -export interface RoleAssignmentList { - object: 'list'; - data: RoleAssignment[]; - listMetadata: { - before: string | null; - after: string | null; - }; -} - -export interface RoleAssignmentListResponse { - object: 'list'; - data: RoleAssignmentResponse[]; - list_metadata: { - before: string | null; - after: string | null; - }; -} diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index 8efbfd5e6..ed09c6570 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -18,3 +18,4 @@ export * from './list-memberships-for-resource-options.serializer'; export * from './role-assignment.serializer'; export * from './assign-role-options.serializer'; export * from './remove-role-options.serializer'; +export * from './list-effective-permissions-options.serializer'; diff --git a/src/authorization/serializers/list-effective-permissions-options.serializer.ts b/src/authorization/serializers/list-effective-permissions-options.serializer.ts new file mode 100644 index 000000000..15c2ae2e0 --- /dev/null +++ b/src/authorization/serializers/list-effective-permissions-options.serializer.ts @@ -0,0 +1,13 @@ +import { serializePaginationOptions } from '../../common/serializers'; +import { ListEffectivePermissionsOptions } from '../interfaces/list-effective-permissions-options.interface'; +import { ListEffectivePermissionsByExternalIdOptions } from '../interfaces/list-effective-permissions-by-external-id-options.interface'; + +type ListEffectivePermissionsQueryOptions = + | ListEffectivePermissionsOptions + | ListEffectivePermissionsByExternalIdOptions; + +export const serializeListEffectivePermissionsOptions = ( + options: ListEffectivePermissionsQueryOptions, +): Record => ({ + ...serializePaginationOptions(options), +}); diff --git a/src/user-management/interfaces/organization-membership.interface.ts b/src/user-management/interfaces/organization-membership.interface.ts index 287b3d5ca..7e1ad39a5 100644 --- a/src/user-management/interfaces/organization-membership.interface.ts +++ b/src/user-management/interfaces/organization-membership.interface.ts @@ -42,21 +42,3 @@ export interface OrganizationMembershipResponse extends BaseOrganizationMembersh export type AuthorizationOrganizationMembershipResponse = BaseOrganizationMembershipResponse; - -export interface AuthorizationOrganizationMembershipList { - object: 'list'; - data: AuthorizationOrganizationMembership[]; - listMetadata: { - before: string | null; - after: string | null; - }; -} - -export interface AuthorizationOrganizationMembershipListResponse { - object: 'list'; - data: AuthorizationOrganizationMembershipResponse[]; - list_metadata: { - before: string | null; - after: string | null; - }; -}