From 310eeb1f3aa67cdd531e33356ebf847d0508d3bb Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 09:08:22 -1000 Subject: [PATCH 01/29] Authorization Resources endpoints for Internal ID --- src/authorization/authorization.spec.ts | 196 ++++++++++++++++++ src/authorization/authorization.ts | 65 ++++++ .../fixtures/authorization-resource.json | 12 ++ .../authorization-resource.interface.ts | 56 +++++ ...uthorization-resource-options.interface.ts | 29 +++ src/authorization/interfaces/index.ts | 5 + ...uthorization-resource-options.interface.ts | 19 ++ .../authorization-resource.serializer.ts | 23 ++ ...thorization-resource-options.serializer.ts | 19 ++ src/authorization/serializers/index.ts | 5 + ...thorization-resource-options.serializer.ts | 24 +++ 11 files changed, 453 insertions(+) create mode 100644 src/authorization/fixtures/authorization-resource.json create mode 100644 src/authorization/interfaces/authorization-resource.interface.ts create mode 100644 src/authorization/interfaces/create-authorization-resource-options.interface.ts create mode 100644 src/authorization/interfaces/update-authorization-resource-options.interface.ts create mode 100644 src/authorization/serializers/authorization-resource.serializer.ts create mode 100644 src/authorization/serializers/create-authorization-resource-options.serializer.ts create mode 100644 src/authorization/serializers/update-authorization-resource-options.serializer.ts diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index dae0e3e30..8591a22db 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -12,6 +12,7 @@ import organizationRoleFixture from './fixtures/organization-role.json'; import listOrganizationRolesFixture from './fixtures/list-organization-roles.json'; import permissionFixture from './fixtures/permission.json'; import listPermissionsFixture from './fixtures/list-permissions.json'; +import authorizationResourceFixture from './fixtures/authorization-resource.json'; const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); const testOrgId = 'org_01HXYZ123ABC456DEF789ABC'; @@ -591,4 +592,199 @@ describe('Authorization', () => { expect(fetchURL()).toContain('/authorization/permissions/users:read'); }); }); + + // ============================================================ + // Authorization Resources (FGA) + // ============================================================ + + describe('getResource', () => { + it('gets a resource by id', async () => { + fetchOnce(authorizationResourceFixture); + + const resource = await workos.authorization.getResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: 'authz_resource_01HXYZ123ABC456DEF789GHI', + externalId: 'doc-1', + name: 'Document 1', + description: 'A test document', + resourceType: 'document', + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + parentResourceId: null, + }); + }); + + it('deserializes all fields correctly', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'authz_resource_parent_123', + }; + fetchOnce(fixtureWithParent); + + const resource = await workos.authorization.getResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(resource.parentResourceId).toBe('authz_resource_parent_123'); + expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); + expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); + }); + }); + + describe('createResource', () => { + it('creates a resource with all options', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'authz_resource_parent_123', + }; + fetchOnce(fixtureWithParent, { status: 201 }); + + const resource = await workos.authorization.createResource({ + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + resourceTypeSlug: 'document', + externalId: 'doc-1', + name: 'Document 1', + description: 'A test document', + parentResourceId: 'authz_resource_parent_123', + }); + + expect(fetchURL()).toContain('/authorization/resources'); + expect(fetchBody()).toEqual({ + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + resource_type_slug: 'document', + external_id: 'doc-1', + name: 'Document 1', + description: 'A test document', + parent_resource_id: 'authz_resource_parent_123', + }); + expect(resource.externalId).toBe('doc-1'); + expect(resource.parentResourceId).toBe('authz_resource_parent_123'); + }); + + it('creates a resource with only required fields', async () => { + const minimalFixture = { + ...authorizationResourceFixture, + description: null, + parent_resource_id: null, + }; + fetchOnce(minimalFixture, { status: 201 }); + + const resource = await workos.authorization.createResource({ + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + resourceTypeSlug: 'document', + externalId: 'doc-2', + name: 'Document 2', + }); + + expect(fetchBody()).toEqual({ + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + resource_type_slug: 'document', + external_id: 'doc-2', + name: 'Document 2', + }); + expect(resource.description).toBeNull(); + expect(resource.parentResourceId).toBeNull(); + }); + }); + + describe('updateResource', () => { + it('updates a resource name', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + name: 'Updated Document', + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + name: 'Updated Document', + }); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + expect(fetchBody()).toEqual({ + name: 'Updated Document', + }); + expect(resource.name).toBe('Updated Document'); + }); + + it('updates a resource description', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + description: 'Updated description', + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + description: 'Updated description', + }); + + expect(fetchBody()).toEqual({ + description: 'Updated description', + }); + expect(resource.description).toBe('Updated description'); + }); + + it('clears description when set to null', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + description: null, + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + description: null, + }); + + expect(fetchBody()).toEqual({ + description: null, + }); + expect(resource.description).toBeNull(); + }); + + it('updates both name and description', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + name: 'New Name', + description: 'New Description', + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + name: 'New Name', + description: 'New Description', + }); + + expect(fetchBody()).toEqual({ + name: 'New Name', + description: 'New Description', + }); + expect(resource.name).toBe('New Name'); + expect(resource.description).toBe('New Description'); + }); + }); + + describe('deleteResource', () => { + it('deletes a resource', async () => { + fetchOnce({}, { status: 204 }); + + await workos.authorization.deleteResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + }); + }); }); diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 2cd92fa31..53d51a9ff 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -27,6 +27,10 @@ import { CreatePermissionOptions, UpdatePermissionOptions, ListPermissionsOptions, + AuthorizationResource, + AuthorizationResourceResponse, + CreateAuthorizationResourceOptions, + UpdateAuthorizationResourceOptions, } from './interfaces'; import { deserializeEnvironmentRole, @@ -39,6 +43,9 @@ import { deserializePermission, serializeCreatePermissionOptions, serializeUpdatePermissionOptions, + deserializeAuthorizationResource, + serializeCreateAuthorizationResourceOptions, + serializeUpdateAuthorizationResourceOptions, } from './serializers'; export class Authorization { @@ -238,4 +245,62 @@ export class Authorization { async deletePermission(slug: string): Promise { await this.workos.delete(`/authorization/permissions/${slug}`); } + + // ============================================================ + // Authorization Resources (FGA) + // ============================================================ + + /** + * Gets an authorization resource by its internal ID. + * + * @param resourceId - The internal resource ID (e.g., 'authz_resource_01H...') + * @returns The authorization resource + */ + async getResource(resourceId: string): Promise { + const { data } = await this.workos.get( + `/authorization/resources/${resourceId}`, + ); + return deserializeAuthorizationResource(data); + } + + /** + * Creates a new authorization resource. + * + * @param options - The resource creation options + * @returns The created authorization resource + */ + async createResource( + options: CreateAuthorizationResourceOptions, + ): Promise { + const { data } = await this.workos.post( + '/authorization/resources', + serializeCreateAuthorizationResourceOptions(options), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Updates an existing authorization resource. + * + * @param options - The resource update options (includes resourceId) + * @returns The updated authorization resource + */ + async updateResource( + options: UpdateAuthorizationResourceOptions, + ): Promise { + const { data } = await this.workos.patch( + `/authorization/resources/${options.resourceId}`, + serializeUpdateAuthorizationResourceOptions(options), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Deletes an authorization resource and all its descendants. + * + * @param resourceId - The internal resource ID to delete + */ + async deleteResource(resourceId: string): Promise { + await this.workos.delete(`/authorization/resources/${resourceId}`); + } } diff --git a/src/authorization/fixtures/authorization-resource.json b/src/authorization/fixtures/authorization-resource.json new file mode 100644 index 000000000..7cd7f8381 --- /dev/null +++ b/src/authorization/fixtures/authorization-resource.json @@ -0,0 +1,12 @@ +{ + "object": "authorization_resource", + "id": "authz_resource_01HXYZ123ABC456DEF789GHI", + "external_id": "doc-1", + "name": "Document 1", + "description": "A test document", + "resource_type": "document", + "organization_id": "org_01HXYZ123ABC456DEF789ABC", + "parent_resource_id": null, + "created_at": "2024-01-15T09:30:00.000Z", + "updated_at": "2024-01-15T09:30:00.000Z" +} diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts new file mode 100644 index 000000000..9acdaccb2 --- /dev/null +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -0,0 +1,56 @@ +/** + * Represents an authorization resource in the WorkOS FGA system. + * Resources are the objects on which permissions are granted (e.g., documents, folders). + */ +export interface AuthorizationResource { + object: 'authorization_resource'; + id: string; + externalId: string; + name: string; + description: string | null; + resourceType: string; + organizationId: string; + parentResourceId: string | null; + createdAt: string; + updatedAt: string; +} + +/** + * API response format for authorization resource (snake_case). + */ +export interface AuthorizationResourceResponse { + object: 'authorization_resource'; + id: string; + external_id: string; + name: string; + description: string | null; + resource_type: string; + organization_id: string; + parent_resource_id: string | null; + created_at: string; + updated_at: string; +} + +/** + * Paginated list of authorization resources. + */ +export interface AuthorizationResourceList { + object: 'list'; + data: AuthorizationResource[]; + listMetadata: { + before: string | null; + after: string | null; + }; +} + +/** + * API response format for paginated authorization resource list (snake_case). + */ +export interface AuthorizationResourceListResponse { + object: 'list'; + data: AuthorizationResourceResponse[]; + list_metadata: { + before: string | null; + after: string | null; + }; +} diff --git a/src/authorization/interfaces/create-authorization-resource-options.interface.ts b/src/authorization/interfaces/create-authorization-resource-options.interface.ts new file mode 100644 index 000000000..7dc84320e --- /dev/null +++ b/src/authorization/interfaces/create-authorization-resource-options.interface.ts @@ -0,0 +1,29 @@ +/** + * Options for creating an authorization resource. + */ +export interface CreateAuthorizationResourceOptions { + /** The organization this resource belongs to. */ + organizationId: string; + /** The slug of the resource type (e.g., 'document', 'folder'). */ + resourceTypeSlug: string; + /** External identifier for this resource (max 64 chars, ASCII only). */ + externalId: string; + /** Display name for the resource (max 48 chars). */ + name: string; + /** Optional description (max 150 chars). */ + description?: string; + /** Optional parent resource ID for hierarchical resources. */ + parentResourceId?: string; +} + +/** + * Serialized format for API request (snake_case). + */ +export interface SerializedCreateAuthorizationResourceOptions { + organization_id: string; + resource_type_slug: string; + external_id: string; + name: string; + description?: string; + parent_resource_id?: string; +} diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index e605c9351..19ae6ef7c 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -13,3 +13,8 @@ export * from './permission.interface'; export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; + +// Authorization Resources (FGA) +export * from './authorization-resource.interface'; +export * from './create-authorization-resource-options.interface'; +export * from './update-authorization-resource-options.interface'; diff --git a/src/authorization/interfaces/update-authorization-resource-options.interface.ts b/src/authorization/interfaces/update-authorization-resource-options.interface.ts new file mode 100644 index 000000000..2e28637f7 --- /dev/null +++ b/src/authorization/interfaces/update-authorization-resource-options.interface.ts @@ -0,0 +1,19 @@ +/** + * Options for updating an authorization resource. + */ +export interface UpdateAuthorizationResourceOptions { + /** The resource ID to update. */ + resourceId: string; + /** Updated display name (max 48 chars). */ + name?: string; + /** Updated description (max 150 chars). Set to null to clear. */ + description?: string | null; +} + +/** + * Serialized format for API request (snake_case). + */ +export interface SerializedUpdateAuthorizationResourceOptions { + name?: string; + description?: string | null; +} diff --git a/src/authorization/serializers/authorization-resource.serializer.ts b/src/authorization/serializers/authorization-resource.serializer.ts new file mode 100644 index 000000000..88afd68c4 --- /dev/null +++ b/src/authorization/serializers/authorization-resource.serializer.ts @@ -0,0 +1,23 @@ +import { + AuthorizationResource, + AuthorizationResourceResponse, +} from '../interfaces/authorization-resource.interface'; + +/** + * Deserializes an authorization resource from API response format to SDK format. + * Converts snake_case fields to camelCase. + */ +export const deserializeAuthorizationResource = ( + resource: AuthorizationResourceResponse, +): AuthorizationResource => ({ + object: resource.object, + id: resource.id, + externalId: resource.external_id, + name: resource.name, + description: resource.description, + resourceType: resource.resource_type, + organizationId: resource.organization_id, + parentResourceId: resource.parent_resource_id, + createdAt: resource.created_at, + updatedAt: resource.updated_at, +}); diff --git a/src/authorization/serializers/create-authorization-resource-options.serializer.ts b/src/authorization/serializers/create-authorization-resource-options.serializer.ts new file mode 100644 index 000000000..7afb7d288 --- /dev/null +++ b/src/authorization/serializers/create-authorization-resource-options.serializer.ts @@ -0,0 +1,19 @@ +import { + CreateAuthorizationResourceOptions, + SerializedCreateAuthorizationResourceOptions, +} from '../interfaces/create-authorization-resource-options.interface'; + +/** + * Serializes create resource options from SDK format to API request format. + * Converts camelCase fields to snake_case. + */ +export const serializeCreateAuthorizationResourceOptions = ( + options: CreateAuthorizationResourceOptions, +): SerializedCreateAuthorizationResourceOptions => ({ + organization_id: options.organizationId, + resource_type_slug: options.resourceTypeSlug, + external_id: options.externalId, + name: options.name, + description: options.description, + parent_resource_id: options.parentResourceId, +}); diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index bbaf011f3..d7f7ab66e 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -7,3 +7,8 @@ export * from './update-organization-role-options.serializer'; export * from './permission.serializer'; export * from './create-permission-options.serializer'; export * from './update-permission-options.serializer'; + +// Authorization Resources (FGA) +export * from './authorization-resource.serializer'; +export * from './create-authorization-resource-options.serializer'; +export * from './update-authorization-resource-options.serializer'; diff --git a/src/authorization/serializers/update-authorization-resource-options.serializer.ts b/src/authorization/serializers/update-authorization-resource-options.serializer.ts new file mode 100644 index 000000000..b1ac02e45 --- /dev/null +++ b/src/authorization/serializers/update-authorization-resource-options.serializer.ts @@ -0,0 +1,24 @@ +import { + UpdateAuthorizationResourceOptions, + SerializedUpdateAuthorizationResourceOptions, +} from '../interfaces/update-authorization-resource-options.interface'; + +/** + * Serializes update resource options from SDK format to API request format. + * Only includes fields that are explicitly provided (including null for clearing). + */ +export const serializeUpdateAuthorizationResourceOptions = ( + options: UpdateAuthorizationResourceOptions, +): SerializedUpdateAuthorizationResourceOptions => { + const serialized: SerializedUpdateAuthorizationResourceOptions = {}; + + if (options.name !== undefined) { + serialized.name = options.name; + } + + if (options.description !== undefined) { + serialized.description = options.description; + } + + return serialized; +}; From 685643c29dda6f61cf4e4e4fe37357c8f4c4d087 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 12:43:30 -1000 Subject: [PATCH 02/29] lol --- src/authorization/authorization.spec.ts | 196 ---------------- src/authorization/authorization.ts | 65 ------ .../fixtures/authorization-resource.json | 12 - src/authorization/interfaces/index.ts | 5 - ...uthorization-resource-options.interface.ts | 19 -- .../authorization-resource.serializer.ts | 23 -- ...thorization-resource-options.serializer.ts | 19 -- src/authorization/serializers/index.ts | 5 - ...thorization-resource-options.serializer.ts | 24 -- src/common/interfaces/event.interface.ts | 78 +++++++ src/common/serializers/event.serializer.ts | 18 ++ src/fga/authorization.ts | 76 +++++++ src/fga/fga.spec.ts | 210 ++++++++++++++++++ src/fga/fga.ts | 47 +++- ...thorization-resource-options.interface.ts} | 22 +- .../authorization-resource.interface.ts | 0 src/fga/interfaces/index.ts | 4 + .../authorization-resource.serializer.ts | 64 ++++++ src/fga/serializers/index.ts | 3 + 19 files changed, 520 insertions(+), 370 deletions(-) delete mode 100644 src/authorization/fixtures/authorization-resource.json delete mode 100644 src/authorization/interfaces/update-authorization-resource-options.interface.ts delete mode 100644 src/authorization/serializers/authorization-resource.serializer.ts delete mode 100644 src/authorization/serializers/create-authorization-resource-options.serializer.ts delete mode 100644 src/authorization/serializers/update-authorization-resource-options.serializer.ts create mode 100644 src/fga/authorization.ts rename src/{authorization/interfaces/create-authorization-resource-options.interface.ts => fga/interfaces/authorization-resource-options.interface.ts} (59%) rename src/{authorization => fga}/interfaces/authorization-resource.interface.ts (100%) create mode 100644 src/fga/serializers/authorization-resource.serializer.ts diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 8591a22db..dae0e3e30 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -12,7 +12,6 @@ import organizationRoleFixture from './fixtures/organization-role.json'; import listOrganizationRolesFixture from './fixtures/list-organization-roles.json'; import permissionFixture from './fixtures/permission.json'; import listPermissionsFixture from './fixtures/list-permissions.json'; -import authorizationResourceFixture from './fixtures/authorization-resource.json'; const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); const testOrgId = 'org_01HXYZ123ABC456DEF789ABC'; @@ -592,199 +591,4 @@ describe('Authorization', () => { expect(fetchURL()).toContain('/authorization/permissions/users:read'); }); }); - - // ============================================================ - // Authorization Resources (FGA) - // ============================================================ - - describe('getResource', () => { - it('gets a resource by id', async () => { - fetchOnce(authorizationResourceFixture); - - const resource = await workos.authorization.getResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - expect(resource).toMatchObject({ - object: 'authorization_resource', - id: 'authz_resource_01HXYZ123ABC456DEF789GHI', - externalId: 'doc-1', - name: 'Document 1', - description: 'A test document', - resourceType: 'document', - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - parentResourceId: null, - }); - }); - - it('deserializes all fields correctly', async () => { - const fixtureWithParent = { - ...authorizationResourceFixture, - parent_resource_id: 'authz_resource_parent_123', - }; - fetchOnce(fixtureWithParent); - - const resource = await workos.authorization.getResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(resource.parentResourceId).toBe('authz_resource_parent_123'); - expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); - expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); - }); - }); - - describe('createResource', () => { - it('creates a resource with all options', async () => { - const fixtureWithParent = { - ...authorizationResourceFixture, - parent_resource_id: 'authz_resource_parent_123', - }; - fetchOnce(fixtureWithParent, { status: 201 }); - - const resource = await workos.authorization.createResource({ - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - resourceTypeSlug: 'document', - externalId: 'doc-1', - name: 'Document 1', - description: 'A test document', - parentResourceId: 'authz_resource_parent_123', - }); - - expect(fetchURL()).toContain('/authorization/resources'); - expect(fetchBody()).toEqual({ - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - resource_type_slug: 'document', - external_id: 'doc-1', - name: 'Document 1', - description: 'A test document', - parent_resource_id: 'authz_resource_parent_123', - }); - expect(resource.externalId).toBe('doc-1'); - expect(resource.parentResourceId).toBe('authz_resource_parent_123'); - }); - - it('creates a resource with only required fields', async () => { - const minimalFixture = { - ...authorizationResourceFixture, - description: null, - parent_resource_id: null, - }; - fetchOnce(minimalFixture, { status: 201 }); - - const resource = await workos.authorization.createResource({ - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - resourceTypeSlug: 'document', - externalId: 'doc-2', - name: 'Document 2', - }); - - expect(fetchBody()).toEqual({ - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - resource_type_slug: 'document', - external_id: 'doc-2', - name: 'Document 2', - }); - expect(resource.description).toBeNull(); - expect(resource.parentResourceId).toBeNull(); - }); - }); - - describe('updateResource', () => { - it('updates a resource name', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - name: 'Updated Document', - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - name: 'Updated Document', - }); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - expect(fetchBody()).toEqual({ - name: 'Updated Document', - }); - expect(resource.name).toBe('Updated Document'); - }); - - it('updates a resource description', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - description: 'Updated description', - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - description: 'Updated description', - }); - - expect(fetchBody()).toEqual({ - description: 'Updated description', - }); - expect(resource.description).toBe('Updated description'); - }); - - it('clears description when set to null', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - description: null, - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - description: null, - }); - - expect(fetchBody()).toEqual({ - description: null, - }); - expect(resource.description).toBeNull(); - }); - - it('updates both name and description', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - name: 'New Name', - description: 'New Description', - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - name: 'New Name', - description: 'New Description', - }); - - expect(fetchBody()).toEqual({ - name: 'New Name', - description: 'New Description', - }); - expect(resource.name).toBe('New Name'); - expect(resource.description).toBe('New Description'); - }); - }); - - describe('deleteResource', () => { - it('deletes a resource', async () => { - fetchOnce({}, { status: 204 }); - - await workos.authorization.deleteResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - }); - }); }); diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 53d51a9ff..2cd92fa31 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -27,10 +27,6 @@ import { CreatePermissionOptions, UpdatePermissionOptions, ListPermissionsOptions, - AuthorizationResource, - AuthorizationResourceResponse, - CreateAuthorizationResourceOptions, - UpdateAuthorizationResourceOptions, } from './interfaces'; import { deserializeEnvironmentRole, @@ -43,9 +39,6 @@ import { deserializePermission, serializeCreatePermissionOptions, serializeUpdatePermissionOptions, - deserializeAuthorizationResource, - serializeCreateAuthorizationResourceOptions, - serializeUpdateAuthorizationResourceOptions, } from './serializers'; export class Authorization { @@ -245,62 +238,4 @@ export class Authorization { async deletePermission(slug: string): Promise { await this.workos.delete(`/authorization/permissions/${slug}`); } - - // ============================================================ - // Authorization Resources (FGA) - // ============================================================ - - /** - * Gets an authorization resource by its internal ID. - * - * @param resourceId - The internal resource ID (e.g., 'authz_resource_01H...') - * @returns The authorization resource - */ - async getResource(resourceId: string): Promise { - const { data } = await this.workos.get( - `/authorization/resources/${resourceId}`, - ); - return deserializeAuthorizationResource(data); - } - - /** - * Creates a new authorization resource. - * - * @param options - The resource creation options - * @returns The created authorization resource - */ - async createResource( - options: CreateAuthorizationResourceOptions, - ): Promise { - const { data } = await this.workos.post( - '/authorization/resources', - serializeCreateAuthorizationResourceOptions(options), - ); - return deserializeAuthorizationResource(data); - } - - /** - * Updates an existing authorization resource. - * - * @param options - The resource update options (includes resourceId) - * @returns The updated authorization resource - */ - async updateResource( - options: UpdateAuthorizationResourceOptions, - ): Promise { - const { data } = await this.workos.patch( - `/authorization/resources/${options.resourceId}`, - serializeUpdateAuthorizationResourceOptions(options), - ); - return deserializeAuthorizationResource(data); - } - - /** - * Deletes an authorization resource and all its descendants. - * - * @param resourceId - The internal resource ID to delete - */ - async deleteResource(resourceId: string): Promise { - await this.workos.delete(`/authorization/resources/${resourceId}`); - } } diff --git a/src/authorization/fixtures/authorization-resource.json b/src/authorization/fixtures/authorization-resource.json deleted file mode 100644 index 7cd7f8381..000000000 --- a/src/authorization/fixtures/authorization-resource.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "object": "authorization_resource", - "id": "authz_resource_01HXYZ123ABC456DEF789GHI", - "external_id": "doc-1", - "name": "Document 1", - "description": "A test document", - "resource_type": "document", - "organization_id": "org_01HXYZ123ABC456DEF789ABC", - "parent_resource_id": null, - "created_at": "2024-01-15T09:30:00.000Z", - "updated_at": "2024-01-15T09:30:00.000Z" -} diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index 19ae6ef7c..e605c9351 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -13,8 +13,3 @@ export * from './permission.interface'; export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; - -// Authorization Resources (FGA) -export * from './authorization-resource.interface'; -export * from './create-authorization-resource-options.interface'; -export * from './update-authorization-resource-options.interface'; diff --git a/src/authorization/interfaces/update-authorization-resource-options.interface.ts b/src/authorization/interfaces/update-authorization-resource-options.interface.ts deleted file mode 100644 index 2e28637f7..000000000 --- a/src/authorization/interfaces/update-authorization-resource-options.interface.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Options for updating an authorization resource. - */ -export interface UpdateAuthorizationResourceOptions { - /** The resource ID to update. */ - resourceId: string; - /** Updated display name (max 48 chars). */ - name?: string; - /** Updated description (max 150 chars). Set to null to clear. */ - description?: string | null; -} - -/** - * Serialized format for API request (snake_case). - */ -export interface SerializedUpdateAuthorizationResourceOptions { - name?: string; - description?: string | null; -} diff --git a/src/authorization/serializers/authorization-resource.serializer.ts b/src/authorization/serializers/authorization-resource.serializer.ts deleted file mode 100644 index 88afd68c4..000000000 --- a/src/authorization/serializers/authorization-resource.serializer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - AuthorizationResource, - AuthorizationResourceResponse, -} from '../interfaces/authorization-resource.interface'; - -/** - * Deserializes an authorization resource from API response format to SDK format. - * Converts snake_case fields to camelCase. - */ -export const deserializeAuthorizationResource = ( - resource: AuthorizationResourceResponse, -): AuthorizationResource => ({ - object: resource.object, - id: resource.id, - externalId: resource.external_id, - name: resource.name, - description: resource.description, - resourceType: resource.resource_type, - organizationId: resource.organization_id, - parentResourceId: resource.parent_resource_id, - createdAt: resource.created_at, - updatedAt: resource.updated_at, -}); diff --git a/src/authorization/serializers/create-authorization-resource-options.serializer.ts b/src/authorization/serializers/create-authorization-resource-options.serializer.ts deleted file mode 100644 index 7afb7d288..000000000 --- a/src/authorization/serializers/create-authorization-resource-options.serializer.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - CreateAuthorizationResourceOptions, - SerializedCreateAuthorizationResourceOptions, -} from '../interfaces/create-authorization-resource-options.interface'; - -/** - * Serializes create resource options from SDK format to API request format. - * Converts camelCase fields to snake_case. - */ -export const serializeCreateAuthorizationResourceOptions = ( - options: CreateAuthorizationResourceOptions, -): SerializedCreateAuthorizationResourceOptions => ({ - organization_id: options.organizationId, - resource_type_slug: options.resourceTypeSlug, - external_id: options.externalId, - name: options.name, - description: options.description, - parent_resource_id: options.parentResourceId, -}); diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index d7f7ab66e..bbaf011f3 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -7,8 +7,3 @@ export * from './update-organization-role-options.serializer'; export * from './permission.serializer'; export * from './create-permission-options.serializer'; export * from './update-permission-options.serializer'; - -// Authorization Resources (FGA) -export * from './authorization-resource.serializer'; -export * from './create-authorization-resource-options.serializer'; -export * from './update-authorization-resource-options.serializer'; diff --git a/src/authorization/serializers/update-authorization-resource-options.serializer.ts b/src/authorization/serializers/update-authorization-resource-options.serializer.ts deleted file mode 100644 index b1ac02e45..000000000 --- a/src/authorization/serializers/update-authorization-resource-options.serializer.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - UpdateAuthorizationResourceOptions, - SerializedUpdateAuthorizationResourceOptions, -} from '../interfaces/update-authorization-resource-options.interface'; - -/** - * Serializes update resource options from SDK format to API request format. - * Only includes fields that are explicitly provided (including null for clearing). - */ -export const serializeUpdateAuthorizationResourceOptions = ( - options: UpdateAuthorizationResourceOptions, -): SerializedUpdateAuthorizationResourceOptions => { - const serialized: SerializedUpdateAuthorizationResourceOptions = {}; - - if (options.name !== undefined) { - serialized.name = options.name; - } - - if (options.description !== undefined) { - serialized.description = options.description; - } - - return serialized; -}; diff --git a/src/common/interfaces/event.interface.ts b/src/common/interfaces/event.interface.ts index b401949ce..e1d2f2377 100644 --- a/src/common/interfaces/event.interface.ts +++ b/src/common/interfaces/event.interface.ts @@ -34,7 +34,13 @@ import { import { RoleEvent, RoleEventResponse, + OrganizationRoleResponse, } from '../../roles/interfaces/role.interface'; +import { OrganizationRole } from '../../authorization/interfaces/organization-role.interface'; +import { + Permission, + PermissionResponse, +} from '../../authorization/interfaces/permission.interface'; import { OrganizationDomain, OrganizationDomainResponse, @@ -532,6 +538,66 @@ export interface RoleUpdatedEventResponse extends EventResponseBase { data: RoleEventResponse; } +export interface OrganizationRoleCreatedEvent extends EventBase { + event: 'organization_role.created'; + data: OrganizationRole; +} + +export interface OrganizationRoleCreatedEventResponse extends EventResponseBase { + event: 'organization_role.created'; + data: OrganizationRoleResponse; +} + +export interface OrganizationRoleUpdatedEvent extends EventBase { + event: 'organization_role.updated'; + data: OrganizationRole; +} + +export interface OrganizationRoleUpdatedEventResponse extends EventResponseBase { + event: 'organization_role.updated'; + data: OrganizationRoleResponse; +} + +export interface OrganizationRoleDeletedEvent extends EventBase { + event: 'organization_role.deleted'; + data: OrganizationRole; +} + +export interface OrganizationRoleDeletedEventResponse extends EventResponseBase { + event: 'organization_role.deleted'; + data: OrganizationRoleResponse; +} + +export interface PermissionCreatedEvent extends EventBase { + event: 'permission.created'; + data: Permission; +} + +export interface PermissionCreatedEventResponse extends EventResponseBase { + event: 'permission.created'; + data: PermissionResponse; +} + +export interface PermissionUpdatedEvent extends EventBase { + event: 'permission.updated'; + data: Permission; +} + +export interface PermissionUpdatedEventResponse extends EventResponseBase { + event: 'permission.updated'; + data: PermissionResponse; +} + +export interface PermissionDeletedEvent extends EventBase { + event: 'permission.deleted'; + data: Permission; +} + +export interface PermissionDeletedEventResponse extends EventResponseBase { + event: 'permission.deleted'; + data: PermissionResponse; +} + export interface SessionCreatedEvent extends EventBase { event: 'session.created'; data: Session; @@ -666,6 +732,12 @@ export type Event = | RoleCreatedEvent | RoleDeletedEvent | RoleUpdatedEvent + | OrganizationRoleCreatedEvent + | OrganizationRoleUpdatedEvent + | OrganizationRoleDeletedEvent + | PermissionCreatedEvent + | PermissionUpdatedEvent + | PermissionDeletedEvent | SessionCreatedEvent | SessionRevokedEvent | OrganizationCreatedEvent @@ -723,6 +795,12 @@ export type EventResponse = | RoleCreatedEventResponse | RoleDeletedEventResponse | RoleUpdatedEventResponse + | OrganizationRoleCreatedEventResponse + | OrganizationRoleUpdatedEventResponse + | OrganizationRoleDeletedEventResponse + | PermissionCreatedEventResponse + | PermissionUpdatedEventResponse + | PermissionDeletedEventResponse | SessionCreatedEventResponse | SessionRevokedEventResponse | OrganizationCreatedResponse diff --git a/src/common/serializers/event.serializer.ts b/src/common/serializers/event.serializer.ts index 26f778dce..36db71150 100644 --- a/src/common/serializers/event.serializer.ts +++ b/src/common/serializers/event.serializer.ts @@ -23,6 +23,8 @@ import { deserializeSession } from '../../user-management/serializers/session.se import { Event, EventBase, EventResponse } from '../interfaces'; import { deserializeAuthenticationRadarRiskDetectedEvent } from '../../user-management/serializers/authentication-radar-risk-event-serializer'; import { deserializeApiKey } from '../../api-keys/serializers/api-key.serializer'; +import { deserializeOrganizationRole } from '../../authorization/serializers/organization-role.serializer'; +import { deserializePermission } from '../../authorization/serializers/permission.serializer'; export const deserializeEvent = (event: EventResponse): Event => { const eventBase: EventBase = { @@ -164,6 +166,22 @@ export const deserializeEvent = (event: EventResponse): Event => { event: event.event, data: deserializeRoleEvent(event.data), }; + case 'organization_role.created': + case 'organization_role.updated': + case 'organization_role.deleted': + return { + ...eventBase, + event: event.event, + data: deserializeOrganizationRole(event.data), + }; + case 'permission.created': + case 'permission.updated': + case 'permission.deleted': + return { + ...eventBase, + event: event.event, + data: deserializePermission(event.data), + }; case 'session.created': case 'session.revoked': return { diff --git a/src/fga/authorization.ts b/src/fga/authorization.ts new file mode 100644 index 000000000..6d81c7d47 --- /dev/null +++ b/src/fga/authorization.ts @@ -0,0 +1,76 @@ +import { WorkOS } from '../workos'; +import { + AuthorizationResource, + AuthorizationResourceResponse, +} from './interfaces/authorization-resource.interface'; +import { + CreateAuthorizationResourceOptions, + UpdateAuthorizationResourceOptions, +} from './interfaces/authorization-resource-options.interface'; +import { + deserializeAuthorizationResource, + serializeCreateAuthorizationResourceOptions, + serializeUpdateAuthorizationResourceOptions, +} from './serializers/authorization-resource.serializer'; + +/** + * FGA Authorization module for managing authorization resources. + * Provides CRUD operations for resources in the Advanced RBAC system. + */ +export class FGAAuthorization { + constructor(private readonly workos: WorkOS) {} + + /** + * Gets an authorization resource by its internal ID. + * + * @param resourceId - The internal resource ID (e.g., 'authz_resource_01H...') + * @returns The authorization resource + */ + async getResource(resourceId: string): Promise { + const { data } = await this.workos.get( + `/authorization/resources/${resourceId}`, + ); + return deserializeAuthorizationResource(data); + } + + /** + * Creates a new authorization resource. + * + * @param options - The resource creation options + * @returns The created authorization resource + */ + async createResource( + options: CreateAuthorizationResourceOptions, + ): Promise { + const { data } = await this.workos.post( + '/authorization/resources', + serializeCreateAuthorizationResourceOptions(options), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Updates an existing authorization resource. + * + * @param options - The resource update options (includes resourceId) + * @returns The updated authorization resource + */ + async updateResource( + options: UpdateAuthorizationResourceOptions, + ): Promise { + const { data } = await this.workos.patch( + `/authorization/resources/${options.resourceId}`, + serializeUpdateAuthorizationResourceOptions(options), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Deletes an authorization resource and all its descendants. + * + * @param resourceId - The internal resource ID to delete + */ + async deleteResource(resourceId: string): Promise { + await this.workos.delete(`/authorization/resources/${resourceId}`); + } +} diff --git a/src/fga/fga.spec.ts b/src/fga/fga.spec.ts index 45f44fa40..67d0be94d 100644 --- a/src/fga/fga.spec.ts +++ b/src/fga/fga.spec.ts @@ -806,4 +806,214 @@ describe('FGA', () => { }); }); }); + + // ============================================================ + // FGA Authorization (Advanced RBAC) + // ============================================================ + + describe('authorization', () => { + const authorizationResourceFixture = { + object: 'authorization_resource', + id: 'authz_resource_01HXYZ123ABC456DEF789GHI', + external_id: 'doc-1', + name: 'Document 1', + description: 'A test document', + resource_type: 'document', + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + parent_resource_id: null, + created_at: '2024-01-15T09:30:00.000Z', + updated_at: '2024-01-15T09:30:00.000Z', + }; + + describe('getResource', () => { + it('gets a resource by id', async () => { + fetchOnce(authorizationResourceFixture); + + const resource = await workos.fga.authorization.getResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: 'authz_resource_01HXYZ123ABC456DEF789GHI', + externalId: 'doc-1', + name: 'Document 1', + description: 'A test document', + resourceType: 'document', + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + parentResourceId: null, + }); + }); + + it('deserializes all fields correctly', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'authz_resource_parent_123', + }; + fetchOnce(fixtureWithParent); + + const resource = await workos.fga.authorization.getResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(resource.parentResourceId).toBe('authz_resource_parent_123'); + expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); + expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); + }); + }); + + describe('createResource', () => { + it('creates a resource with all options', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'authz_resource_parent_123', + }; + fetchOnce(fixtureWithParent, { status: 201 }); + + const resource = await workos.fga.authorization.createResource({ + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + resourceTypeSlug: 'document', + externalId: 'doc-1', + name: 'Document 1', + description: 'A test document', + parentResourceId: 'authz_resource_parent_123', + }); + + expect(fetchURL()).toContain('/authorization/resources'); + expect(fetchBody()).toEqual({ + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + resource_type_slug: 'document', + external_id: 'doc-1', + name: 'Document 1', + description: 'A test document', + parent_resource_id: 'authz_resource_parent_123', + }); + expect(resource.externalId).toBe('doc-1'); + expect(resource.parentResourceId).toBe('authz_resource_parent_123'); + }); + + it('creates a resource with only required fields', async () => { + const minimalFixture = { + ...authorizationResourceFixture, + description: null, + parent_resource_id: null, + }; + fetchOnce(minimalFixture, { status: 201 }); + + const resource = await workos.fga.authorization.createResource({ + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + resourceTypeSlug: 'document', + externalId: 'doc-2', + name: 'Document 2', + }); + + expect(fetchBody()).toEqual({ + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + resource_type_slug: 'document', + external_id: 'doc-2', + name: 'Document 2', + }); + expect(resource.description).toBeNull(); + expect(resource.parentResourceId).toBeNull(); + }); + }); + + describe('updateResource', () => { + it('updates a resource name', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + name: 'Updated Document', + }; + fetchOnce(updatedFixture); + + const resource = await workos.fga.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + name: 'Updated Document', + }); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + expect(fetchBody()).toEqual({ + name: 'Updated Document', + }); + expect(resource.name).toBe('Updated Document'); + }); + + it('updates a resource description', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + description: 'Updated description', + }; + fetchOnce(updatedFixture); + + const resource = await workos.fga.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + description: 'Updated description', + }); + + expect(fetchBody()).toEqual({ + description: 'Updated description', + }); + expect(resource.description).toBe('Updated description'); + }); + + it('clears description when set to null', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + description: null, + }; + fetchOnce(updatedFixture); + + const resource = await workos.fga.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + description: null, + }); + + expect(fetchBody()).toEqual({ + description: null, + }); + expect(resource.description).toBeNull(); + }); + + it('updates both name and description', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + name: 'New Name', + description: 'New Description', + }; + fetchOnce(updatedFixture); + + const resource = await workos.fga.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + name: 'New Name', + description: 'New Description', + }); + + expect(fetchBody()).toEqual({ + name: 'New Name', + description: 'New Description', + }); + expect(resource.name).toBe('New Name'); + expect(resource.description).toBe('New Description'); + }); + }); + + describe('deleteResource', () => { + it('deletes a resource', async () => { + fetchOnce({}, { status: 204 }); + + await workos.fga.authorization.deleteResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + }); + }); + }); }); diff --git a/src/fga/fga.ts b/src/fga/fga.ts index bb2cfadd9..23ca9bedb 100644 --- a/src/fga/fga.ts +++ b/src/fga/fga.ts @@ -30,6 +30,7 @@ import { SerializedListResourcesOptions, SerializedQueryOptions, } from './interfaces'; +import { FGAAuthorization } from './authorization'; import { deserializeBatchWriteResourcesResponse, deserializeResource, @@ -52,8 +53,19 @@ import { FgaPaginatable } from './utils/fga-paginatable'; import { fetchAndDeserializeFGAList } from './utils/fetch-and-deserialize-list'; export class FGA { - constructor(private readonly workos: WorkOS) {} + /** + * Authorization sub-module for Advanced RBAC resource management. + * Access via `workos.fga.authorization.*` + */ + readonly authorization: FGAAuthorization; + constructor(private readonly workos: WorkOS) { + this.authorization = new FGAAuthorization(workos); + } + + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async check( checkOptions: CheckOptions, options: CheckRequestOptions = {}, @@ -66,6 +78,9 @@ export class FGA { return new CheckResult(data); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async checkBatch( checkOptions: CheckBatchOptions, options: CheckRequestOptions = {}, @@ -80,6 +95,9 @@ export class FGA { ); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async createResource(resource: CreateResourceOptions): Promise { const { data } = await this.workos.post( '/fga/v1/resources', @@ -89,6 +107,9 @@ export class FGA { return deserializeResource(data); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async getResource( resource: ResourceInterface | ResourceOptions, ): Promise { @@ -106,6 +127,9 @@ export class FGA { return deserializeResource(data); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async listResources( options?: ListResourcesOptions, ): Promise> { @@ -127,6 +151,9 @@ export class FGA { ); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async updateResource(options: UpdateResourceOptions): Promise { const resourceType = isResourceInterface(options.resource) ? options.resource.getResourceType() @@ -145,6 +172,9 @@ export class FGA { return deserializeResource(data); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async deleteResource(resource: DeleteResourceOptions): Promise { const resourceType = isResourceInterface(resource) ? resource.getResourceType() @@ -156,6 +186,9 @@ export class FGA { await this.workos.delete(`/fga/v1/resources/${resourceType}/${resourceId}`); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async batchWriteResources( options: BatchWriteResourcesOptions, ): Promise { @@ -166,6 +199,9 @@ export class FGA { return deserializeBatchWriteResourcesResponse(data); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async writeWarrant(options: WriteWarrantOptions): Promise { const { data } = await this.workos.post( '/fga/v1/warrants', @@ -175,6 +211,9 @@ export class FGA { return deserializeWarrantToken(data); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async batchWriteWarrants( options: WriteWarrantOptions[], ): Promise { @@ -186,6 +225,9 @@ export class FGA { return deserializeWarrantToken(warrantToken); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async listWarrants( options?: ListWarrantsOptions, requestOptions?: ListWarrantsRequestOptions, @@ -210,6 +252,9 @@ export class FGA { ); } + /** + * @deprecated Use `workos.fga.authorization` methods instead. + */ async query( options: QueryOptions, requestOptions: QueryRequestOptions = {}, diff --git a/src/authorization/interfaces/create-authorization-resource-options.interface.ts b/src/fga/interfaces/authorization-resource-options.interface.ts similarity index 59% rename from src/authorization/interfaces/create-authorization-resource-options.interface.ts rename to src/fga/interfaces/authorization-resource-options.interface.ts index 7dc84320e..fe6bbe8ea 100644 --- a/src/authorization/interfaces/create-authorization-resource-options.interface.ts +++ b/src/fga/interfaces/authorization-resource-options.interface.ts @@ -17,7 +17,7 @@ export interface CreateAuthorizationResourceOptions { } /** - * Serialized format for API request (snake_case). + * Serialized format for create request (snake_case). */ export interface SerializedCreateAuthorizationResourceOptions { organization_id: string; @@ -27,3 +27,23 @@ export interface SerializedCreateAuthorizationResourceOptions { description?: string; parent_resource_id?: string; } + +/** + * Options for updating an authorization resource. + */ +export interface UpdateAuthorizationResourceOptions { + /** The resource ID to update. */ + resourceId: string; + /** Updated display name (max 48 chars). */ + name?: string; + /** Updated description (max 150 chars). Set to null to clear. */ + description?: string | null; +} + +/** + * Serialized format for update request (snake_case). + */ +export interface SerializedUpdateAuthorizationResourceOptions { + name?: string; + description?: string | null; +} diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/fga/interfaces/authorization-resource.interface.ts similarity index 100% rename from src/authorization/interfaces/authorization-resource.interface.ts rename to src/fga/interfaces/authorization-resource.interface.ts diff --git a/src/fga/interfaces/index.ts b/src/fga/interfaces/index.ts index 0c94e50fe..52bf750ee 100644 --- a/src/fga/interfaces/index.ts +++ b/src/fga/interfaces/index.ts @@ -6,3 +6,7 @@ export * from './resource.interface'; export * from './warrant-op.enum'; export * from './warrant-token.interface'; export * from './warrant.interface'; + +// Authorization Resources (Advanced RBAC) +export * from './authorization-resource.interface'; +export * from './authorization-resource-options.interface'; diff --git a/src/fga/serializers/authorization-resource.serializer.ts b/src/fga/serializers/authorization-resource.serializer.ts new file mode 100644 index 000000000..10aa489f9 --- /dev/null +++ b/src/fga/serializers/authorization-resource.serializer.ts @@ -0,0 +1,64 @@ +import { + AuthorizationResource, + AuthorizationResourceResponse, +} from '../interfaces/authorization-resource.interface'; +import { + CreateAuthorizationResourceOptions, + SerializedCreateAuthorizationResourceOptions, + UpdateAuthorizationResourceOptions, + SerializedUpdateAuthorizationResourceOptions, +} from '../interfaces/authorization-resource-options.interface'; + +/** + * Deserializes an authorization resource from API response format to SDK format. + * Converts snake_case fields to camelCase. + */ +export const deserializeAuthorizationResource = ( + resource: AuthorizationResourceResponse, +): AuthorizationResource => ({ + object: resource.object, + id: resource.id, + externalId: resource.external_id, + name: resource.name, + description: resource.description, + resourceType: resource.resource_type, + organizationId: resource.organization_id, + parentResourceId: resource.parent_resource_id, + createdAt: resource.created_at, + updatedAt: resource.updated_at, +}); + +/** + * Serializes create resource options from SDK format to API request format. + * Converts camelCase fields to snake_case. + */ +export const serializeCreateAuthorizationResourceOptions = ( + options: CreateAuthorizationResourceOptions, +): SerializedCreateAuthorizationResourceOptions => ({ + organization_id: options.organizationId, + resource_type_slug: options.resourceTypeSlug, + external_id: options.externalId, + name: options.name, + description: options.description, + parent_resource_id: options.parentResourceId, +}); + +/** + * Serializes update resource options from SDK format to API request format. + * Only includes fields that are explicitly provided (including null for clearing). + */ +export const serializeUpdateAuthorizationResourceOptions = ( + options: UpdateAuthorizationResourceOptions, +): SerializedUpdateAuthorizationResourceOptions => { + const serialized: SerializedUpdateAuthorizationResourceOptions = {}; + + if (options.name !== undefined) { + serialized.name = options.name; + } + + if (options.description !== undefined) { + serialized.description = options.description; + } + + return serialized; +}; diff --git a/src/fga/serializers/index.ts b/src/fga/serializers/index.ts index 3bb36abd9..c4255c08a 100644 --- a/src/fga/serializers/index.ts +++ b/src/fga/serializers/index.ts @@ -11,3 +11,6 @@ export * from './warrant-token.serializer'; export * from './warrant.serializer'; export * from './write-warrant-options.serializer'; export * from './list.serializer'; + +// Authorization Resources (Advanced RBAC) +export * from './authorization-resource.serializer'; From ea2628ea0b16d391e968e578541e091db83f6ad1 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:07:22 -1000 Subject: [PATCH 03/29] lol --- src/authorization/authorization.spec.ts | 184 ++++++++++++++++++ src/authorization/authorization.ts | 57 ++++++ .../fixtures/authorization-resource.json | 12 ++ src/authorization/interfaces/index.ts | 7 + .../authorization-resource.serializer.ts | 23 +++ .../create-resource-options.serializer.ts | 19 ++ src/authorization/serializers/index.ts | 3 + .../update-resource-options.serializer.ts | 16 ++ ...uthorization-resource-options.interface.ts | 21 -- .../authorization-resource.interface.ts | 13 -- 10 files changed, 321 insertions(+), 34 deletions(-) create mode 100644 src/authorization/fixtures/authorization-resource.json create mode 100644 src/authorization/serializers/authorization-resource.serializer.ts create mode 100644 src/authorization/serializers/create-resource-options.serializer.ts create mode 100644 src/authorization/serializers/update-resource-options.serializer.ts diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index dae0e3e30..0ac1e7c53 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -12,9 +12,11 @@ import organizationRoleFixture from './fixtures/organization-role.json'; import listOrganizationRolesFixture from './fixtures/list-organization-roles.json'; import permissionFixture from './fixtures/permission.json'; import listPermissionsFixture from './fixtures/list-permissions.json'; +import authorizationResourceFixture from './fixtures/authorization-resource.json'; const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); const testOrgId = 'org_01HXYZ123ABC456DEF789ABC'; +const testResourceId = 'resource_01HXYZ123ABC456DEF789ABC'; describe('Authorization', () => { beforeEach(() => fetch.resetMocks()); @@ -591,4 +593,186 @@ describe('Authorization', () => { expect(fetchURL()).toContain('/authorization/permissions/users:read'); }); }); + + describe('getResource', () => { + it('gets an authorization resource by internal ID', async () => { + fetchOnce(authorizationResourceFixture); + + const resource = await workos.authorization.getResource(testResourceId); + + expect(fetchURL()).toContain( + `/authorization/resources/${testResourceId}`, + ); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + resourceType: 'document', + organizationId: testOrgId, + parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + }); + + it('handles resource without parent', async () => { + fetchOnce({ ...authorizationResourceFixture, parent_resource_id: null }); + + const resource = await workos.authorization.getResource(testResourceId); + + expect(resource.parentResourceId).toBeNull(); + }); + }); + + describe('createResource', () => { + it('creates an authorization resource with all fields', async () => { + fetchOnce(authorizationResourceFixture, { status: 201 }); + + const resource = await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + + expect(fetchURL()).toContain('/authorization/resources'); + expect(fetchBody()).toEqual({ + organization_id: testOrgId, + resource_type_slug: 'document', + external_id: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + parent_resource_id: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + resourceType: 'document', + }); + }); + + it('creates an authorization resource with required fields only', async () => { + fetchOnce( + { + ...authorizationResourceFixture, + description: null, + parent_resource_id: null, + }, + { status: 201 }, + ); + + const resource = await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + }); + + expect(fetchBody()).toEqual({ + organization_id: testOrgId, + resource_type_slug: 'document', + external_id: 'doc-456', + name: 'Q4 Budget Report', + }); + expect(resource.description).toBeNull(); + expect(resource.parentResourceId).toBeNull(); + }); + }); + + describe('updateResource', () => { + it('updates resource name', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + name: 'Updated Report Name', + }; + fetchOnce(updatedResourceFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: testResourceId, + name: 'Updated Report Name', + }); + + expect(fetchURL()).toContain( + `/authorization/resources/${testResourceId}`, + ); + expect(fetchBody()).toEqual({ + name: 'Updated Report Name', + }); + expect(resource.name).toBe('Updated Report Name'); + }); + + it('updates resource description', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + description: 'Updated description', + }; + fetchOnce(updatedResourceFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: testResourceId, + description: 'Updated description', + }); + + expect(fetchBody()).toEqual({ + description: 'Updated description', + }); + expect(resource.description).toBe('Updated description'); + }); + + it('clears description when set to null', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + description: null, + }; + fetchOnce(updatedResourceFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: testResourceId, + description: null, + }); + + expect(fetchBody()).toEqual({ + description: null, + }); + expect(resource.description).toBeNull(); + }); + + it('updates both name and description', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + name: 'New Name', + description: 'New Description', + }; + fetchOnce(updatedResourceFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: testResourceId, + name: 'New Name', + description: 'New Description', + }); + + expect(fetchBody()).toEqual({ + name: 'New Name', + description: 'New Description', + }); + expect(resource.name).toBe('New Name'); + expect(resource.description).toBe('New Description'); + }); + }); + + describe('deleteResource', () => { + it('deletes an authorization resource', async () => { + fetchOnce({}, { status: 204 }); + + await workos.authorization.deleteResource(testResourceId); + + expect(fetchURL()).toContain( + `/authorization/resources/${testResourceId}`, + ); + }); + }); }); diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 2cd92fa31..c3e4e8f52 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -27,6 +27,10 @@ import { CreatePermissionOptions, UpdatePermissionOptions, ListPermissionsOptions, + AuthorizationResource, + AuthorizationResourceResponse, + CreateAuthorizationResourceOptions, + UpdateAuthorizationResourceOptions, } from './interfaces'; import { deserializeEnvironmentRole, @@ -39,6 +43,9 @@ import { deserializePermission, serializeCreatePermissionOptions, serializeUpdatePermissionOptions, + deserializeAuthorizationResource, + serializeCreateResourceOptions, + serializeUpdateResourceOptions, } from './serializers'; export class Authorization { @@ -238,4 +245,54 @@ export class Authorization { async deletePermission(slug: string): Promise { await this.workos.delete(`/authorization/permissions/${slug}`); } + + /** + * Retrieves an authorization resource by its internal ID. + * @param resourceId - The internal resource ID + * @returns The authorization resource + */ + async getResource(resourceId: string): Promise { + const { data } = await this.workos.get( + `/authorization/resources/${resourceId}`, + ); + return deserializeAuthorizationResource(data); + } + + /** + * Creates a new authorization resource. + * @param options - The resource creation options + * @returns The created authorization resource + */ + async createResource( + options: CreateAuthorizationResourceOptions, + ): Promise { + const { data } = await this.workos.post( + '/authorization/resources', + serializeCreateResourceOptions(options), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Updates an authorization resource by its internal ID. + * @param options - The resource update options including resourceId + * @returns The updated authorization resource + */ + async updateResource( + options: UpdateAuthorizationResourceOptions, + ): Promise { + const { data } = await this.workos.patch( + `/authorization/resources/${options.resourceId}`, + serializeUpdateResourceOptions(options), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Deletes an authorization resource by its internal ID. + * @param resourceId - The internal resource ID + */ + async deleteResource(resourceId: string): Promise { + await this.workos.delete(`/authorization/resources/${resourceId}`); + } } diff --git a/src/authorization/fixtures/authorization-resource.json b/src/authorization/fixtures/authorization-resource.json new file mode 100644 index 000000000..6fcb9f680 --- /dev/null +++ b/src/authorization/fixtures/authorization-resource.json @@ -0,0 +1,12 @@ +{ + "object": "authorization_resource", + "id": "resource_01HXYZ123ABC456DEF789ABC", + "external_id": "doc-456", + "name": "Q4 Budget Report", + "description": "Financial report for Q4 2025", + "resource_type": "document", + "organization_id": "org_01HXYZ123ABC456DEF789ABC", + "parent_resource_id": "resource_01HXYZ123ABC456DEF789XYZ", + "created_at": "2024-01-15T09:30:00.000Z", + "updated_at": "2024-01-15T09:30:00.000Z" +} diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index e605c9351..f5d551293 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -13,3 +13,10 @@ export * from './permission.interface'; export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; +// Re-export from fga for authorization resource types (Advanced RBAC) +export { + AuthorizationResource, + AuthorizationResourceResponse, + CreateAuthorizationResourceOptions, + UpdateAuthorizationResourceOptions, +} from '../../fga/interfaces'; diff --git a/src/authorization/serializers/authorization-resource.serializer.ts b/src/authorization/serializers/authorization-resource.serializer.ts new file mode 100644 index 000000000..015128d5d --- /dev/null +++ b/src/authorization/serializers/authorization-resource.serializer.ts @@ -0,0 +1,23 @@ +import { + AuthorizationResource, + AuthorizationResourceResponse, +} from '../../fga/interfaces'; + +/** + * Deserializes an API response into an AuthorizationResource. + * Converts snake_case API fields to camelCase. + */ +export const deserializeAuthorizationResource = ( + resource: AuthorizationResourceResponse, +): AuthorizationResource => ({ + object: resource.object, + id: resource.id, + externalId: resource.external_id, + name: resource.name, + description: resource.description, + resourceType: resource.resource_type, + organizationId: resource.organization_id, + parentResourceId: resource.parent_resource_id, + createdAt: resource.created_at, + updatedAt: resource.updated_at, +}); diff --git a/src/authorization/serializers/create-resource-options.serializer.ts b/src/authorization/serializers/create-resource-options.serializer.ts new file mode 100644 index 000000000..02f08d8f1 --- /dev/null +++ b/src/authorization/serializers/create-resource-options.serializer.ts @@ -0,0 +1,19 @@ +import { + CreateAuthorizationResourceOptions, + SerializedCreateAuthorizationResourceOptions, +} from '../../fga/interfaces'; + +/** + * Serializes CreateAuthorizationResourceOptions for the API request. + * Converts camelCase to snake_case for API compatibility. + */ +export const serializeCreateResourceOptions = ( + options: CreateAuthorizationResourceOptions, +): SerializedCreateAuthorizationResourceOptions => ({ + organization_id: options.organizationId, + resource_type_slug: options.resourceTypeSlug, + external_id: options.externalId, + name: options.name, + description: options.description, + parent_resource_id: options.parentResourceId, +}); diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index bbaf011f3..edd572720 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -7,3 +7,6 @@ export * from './update-organization-role-options.serializer'; export * from './permission.serializer'; export * from './create-permission-options.serializer'; export * from './update-permission-options.serializer'; +export * from './authorization-resource.serializer'; +export * from './create-resource-options.serializer'; +export * from './update-resource-options.serializer'; diff --git a/src/authorization/serializers/update-resource-options.serializer.ts b/src/authorization/serializers/update-resource-options.serializer.ts new file mode 100644 index 000000000..8000f76be --- /dev/null +++ b/src/authorization/serializers/update-resource-options.serializer.ts @@ -0,0 +1,16 @@ +import { + UpdateAuthorizationResourceOptions, + SerializedUpdateAuthorizationResourceOptions, +} from '../../fga/interfaces'; + +/** + * Serializes UpdateAuthorizationResourceOptions for the API request. + * Only includes fields that are provided (name and/or description). + * resourceId is excluded as it's used in the URL path. + */ +export const serializeUpdateResourceOptions = ( + options: UpdateAuthorizationResourceOptions, +): SerializedUpdateAuthorizationResourceOptions => ({ + name: options.name, + description: options.description, +}); diff --git a/src/fga/interfaces/authorization-resource-options.interface.ts b/src/fga/interfaces/authorization-resource-options.interface.ts index fe6bbe8ea..297f593d8 100644 --- a/src/fga/interfaces/authorization-resource-options.interface.ts +++ b/src/fga/interfaces/authorization-resource-options.interface.ts @@ -1,24 +1,12 @@ -/** - * Options for creating an authorization resource. - */ export interface CreateAuthorizationResourceOptions { - /** The organization this resource belongs to. */ organizationId: string; - /** The slug of the resource type (e.g., 'document', 'folder'). */ resourceTypeSlug: string; - /** External identifier for this resource (max 64 chars, ASCII only). */ externalId: string; - /** Display name for the resource (max 48 chars). */ name: string; - /** Optional description (max 150 chars). */ description?: string; - /** Optional parent resource ID for hierarchical resources. */ parentResourceId?: string; } -/** - * Serialized format for create request (snake_case). - */ export interface SerializedCreateAuthorizationResourceOptions { organization_id: string; resource_type_slug: string; @@ -28,21 +16,12 @@ export interface SerializedCreateAuthorizationResourceOptions { parent_resource_id?: string; } -/** - * Options for updating an authorization resource. - */ export interface UpdateAuthorizationResourceOptions { - /** The resource ID to update. */ resourceId: string; - /** Updated display name (max 48 chars). */ name?: string; - /** Updated description (max 150 chars). Set to null to clear. */ description?: string | null; } -/** - * Serialized format for update request (snake_case). - */ export interface SerializedUpdateAuthorizationResourceOptions { name?: string; description?: string | null; diff --git a/src/fga/interfaces/authorization-resource.interface.ts b/src/fga/interfaces/authorization-resource.interface.ts index 9acdaccb2..74259df9a 100644 --- a/src/fga/interfaces/authorization-resource.interface.ts +++ b/src/fga/interfaces/authorization-resource.interface.ts @@ -1,7 +1,3 @@ -/** - * Represents an authorization resource in the WorkOS FGA system. - * Resources are the objects on which permissions are granted (e.g., documents, folders). - */ export interface AuthorizationResource { object: 'authorization_resource'; id: string; @@ -15,9 +11,6 @@ export interface AuthorizationResource { updatedAt: string; } -/** - * API response format for authorization resource (snake_case). - */ export interface AuthorizationResourceResponse { object: 'authorization_resource'; id: string; @@ -31,9 +24,6 @@ export interface AuthorizationResourceResponse { updated_at: string; } -/** - * Paginated list of authorization resources. - */ export interface AuthorizationResourceList { object: 'list'; data: AuthorizationResource[]; @@ -43,9 +33,6 @@ export interface AuthorizationResourceList { }; } -/** - * API response format for paginated authorization resource list (snake_case). - */ export interface AuthorizationResourceListResponse { object: 'list'; data: AuthorizationResourceResponse[]; From 543b121eddcdbb8b637ee33b4f205e7acb4c235b Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:09:35 -1000 Subject: [PATCH 04/29] lol --- src/fga/fga.ts | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/src/fga/fga.ts b/src/fga/fga.ts index 23ca9bedb..78a8b6f72 100644 --- a/src/fga/fga.ts +++ b/src/fga/fga.ts @@ -52,20 +52,16 @@ import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize'; import { FgaPaginatable } from './utils/fga-paginatable'; import { fetchAndDeserializeFGAList } from './utils/fetch-and-deserialize-list'; +/** + * @deprecated + */ export class FGA { - /** - * Authorization sub-module for Advanced RBAC resource management. - * Access via `workos.fga.authorization.*` - */ readonly authorization: FGAAuthorization; constructor(private readonly workos: WorkOS) { this.authorization = new FGAAuthorization(workos); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async check( checkOptions: CheckOptions, options: CheckRequestOptions = {}, @@ -78,9 +74,6 @@ export class FGA { return new CheckResult(data); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async checkBatch( checkOptions: CheckBatchOptions, options: CheckRequestOptions = {}, @@ -95,9 +88,6 @@ export class FGA { ); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async createResource(resource: CreateResourceOptions): Promise { const { data } = await this.workos.post( '/fga/v1/resources', @@ -107,9 +97,6 @@ export class FGA { return deserializeResource(data); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async getResource( resource: ResourceInterface | ResourceOptions, ): Promise { @@ -127,9 +114,6 @@ export class FGA { return deserializeResource(data); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async listResources( options?: ListResourcesOptions, ): Promise> { @@ -151,9 +135,6 @@ export class FGA { ); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async updateResource(options: UpdateResourceOptions): Promise { const resourceType = isResourceInterface(options.resource) ? options.resource.getResourceType() @@ -172,9 +153,6 @@ export class FGA { return deserializeResource(data); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async deleteResource(resource: DeleteResourceOptions): Promise { const resourceType = isResourceInterface(resource) ? resource.getResourceType() @@ -186,9 +164,6 @@ export class FGA { await this.workos.delete(`/fga/v1/resources/${resourceType}/${resourceId}`); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async batchWriteResources( options: BatchWriteResourcesOptions, ): Promise { @@ -199,9 +174,6 @@ export class FGA { return deserializeBatchWriteResourcesResponse(data); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async writeWarrant(options: WriteWarrantOptions): Promise { const { data } = await this.workos.post( '/fga/v1/warrants', @@ -211,9 +183,6 @@ export class FGA { return deserializeWarrantToken(data); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async batchWriteWarrants( options: WriteWarrantOptions[], ): Promise { @@ -225,9 +194,6 @@ export class FGA { return deserializeWarrantToken(warrantToken); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async listWarrants( options?: ListWarrantsOptions, requestOptions?: ListWarrantsRequestOptions, @@ -252,9 +218,6 @@ export class FGA { ); } - /** - * @deprecated Use `workos.fga.authorization` methods instead. - */ async query( options: QueryOptions, requestOptions: QueryRequestOptions = {}, From c2734e853c38c6390f747c12ac48f564c8aa24e1 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:10:36 -1000 Subject: [PATCH 05/29] moar --- src/fga/fga.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/fga/fga.ts b/src/fga/fga.ts index 78a8b6f72..3d6cac3c4 100644 --- a/src/fga/fga.ts +++ b/src/fga/fga.ts @@ -30,7 +30,6 @@ import { SerializedListResourcesOptions, SerializedQueryOptions, } from './interfaces'; -import { FGAAuthorization } from './authorization'; import { deserializeBatchWriteResourcesResponse, deserializeResource, @@ -56,11 +55,7 @@ import { fetchAndDeserializeFGAList } from './utils/fetch-and-deserialize-list'; * @deprecated */ export class FGA { - readonly authorization: FGAAuthorization; - - constructor(private readonly workos: WorkOS) { - this.authorization = new FGAAuthorization(workos); - } + constructor(private readonly workos: WorkOS) {} async check( checkOptions: CheckOptions, From 5037673a44e58508ac90caaecb5ac7a76cd18a19 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:13:56 -1000 Subject: [PATCH 06/29] lol --- src/authorization/authorization.spec.ts | 210 ++++++++++++++++++++++++ src/fga/fga.spec.ts | 210 ------------------------ 2 files changed, 210 insertions(+), 210 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 0ac1e7c53..1eb8435a6 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -775,4 +775,214 @@ describe('Authorization', () => { ); }); }); + + // ============================================================ + // FGA Authorization (Advanced RBAC) + // ============================================================ + + describe('authorization', () => { + const authorizationResourceFixture = { + object: 'authorization_resource', + id: 'authz_resource_01HXYZ123ABC456DEF789GHI', + external_id: 'doc-1', + name: 'Document 1', + description: 'A test document', + resource_type: 'document', + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + parent_resource_id: null, + created_at: '2024-01-15T09:30:00.000Z', + updated_at: '2024-01-15T09:30:00.000Z', + }; + + describe('getResource', () => { + it('gets a resource by id', async () => { + fetchOnce(authorizationResourceFixture); + + const resource = await workos.authorization.getResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: 'authz_resource_01HXYZ123ABC456DEF789GHI', + externalId: 'doc-1', + name: 'Document 1', + description: 'A test document', + resourceType: 'document', + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + parentResourceId: null, + }); + }); + + it('deserializes all fields correctly', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'authz_resource_parent_123', + }; + fetchOnce(fixtureWithParent); + + const resource = await workos.authorization.getResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(resource.parentResourceId).toBe('authz_resource_parent_123'); + expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); + expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); + }); + }); + + describe('createResource', () => { + it('creates a resource with all options', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'authz_resource_parent_123', + }; + fetchOnce(fixtureWithParent, { status: 201 }); + + const resource = await workos.authorization.createResource({ + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + resourceTypeSlug: 'document', + externalId: 'doc-1', + name: 'Document 1', + description: 'A test document', + parentResourceId: 'authz_resource_parent_123', + }); + + expect(fetchURL()).toContain('/authorization/resources'); + expect(fetchBody()).toEqual({ + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + resource_type_slug: 'document', + external_id: 'doc-1', + name: 'Document 1', + description: 'A test document', + parent_resource_id: 'authz_resource_parent_123', + }); + expect(resource.externalId).toBe('doc-1'); + expect(resource.parentResourceId).toBe('authz_resource_parent_123'); + }); + + it('creates a resource with only required fields', async () => { + const minimalFixture = { + ...authorizationResourceFixture, + description: null, + parent_resource_id: null, + }; + fetchOnce(minimalFixture, { status: 201 }); + + const resource = await workos.authorization.createResource({ + organizationId: 'org_01HXYZ123ABC456DEF789ABC', + resourceTypeSlug: 'document', + externalId: 'doc-2', + name: 'Document 2', + }); + + expect(fetchBody()).toEqual({ + organization_id: 'org_01HXYZ123ABC456DEF789ABC', + resource_type_slug: 'document', + external_id: 'doc-2', + name: 'Document 2', + }); + expect(resource.description).toBeNull(); + expect(resource.parentResourceId).toBeNull(); + }); + }); + + describe('updateResource', () => { + it('updates a resource name', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + name: 'Updated Document', + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + name: 'Updated Document', + }); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + expect(fetchBody()).toEqual({ + name: 'Updated Document', + }); + expect(resource.name).toBe('Updated Document'); + }); + + it('updates a resource description', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + description: 'Updated description', + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + description: 'Updated description', + }); + + expect(fetchBody()).toEqual({ + description: 'Updated description', + }); + expect(resource.description).toBe('Updated description'); + }); + + it('clears description when set to null', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + description: null, + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + description: null, + }); + + expect(fetchBody()).toEqual({ + description: null, + }); + expect(resource.description).toBeNull(); + }); + + it('updates both name and description', async () => { + const updatedFixture = { + ...authorizationResourceFixture, + name: 'New Name', + description: 'New Description', + }; + fetchOnce(updatedFixture); + + const resource = await workos.authorization.updateResource({ + resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', + name: 'New Name', + description: 'New Description', + }); + + expect(fetchBody()).toEqual({ + name: 'New Name', + description: 'New Description', + }); + expect(resource.name).toBe('New Name'); + expect(resource.description).toBe('New Description'); + }); + }); + + describe('deleteResource', () => { + it('deletes a resource', async () => { + fetchOnce({}, { status: 204 }); + + await workos.authorization.deleteResource( + 'authz_resource_01HXYZ123ABC456DEF789GHI', + ); + + expect(fetchURL()).toContain( + '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', + ); + }); + }); + }); }); diff --git a/src/fga/fga.spec.ts b/src/fga/fga.spec.ts index 67d0be94d..45f44fa40 100644 --- a/src/fga/fga.spec.ts +++ b/src/fga/fga.spec.ts @@ -806,214 +806,4 @@ describe('FGA', () => { }); }); }); - - // ============================================================ - // FGA Authorization (Advanced RBAC) - // ============================================================ - - describe('authorization', () => { - const authorizationResourceFixture = { - object: 'authorization_resource', - id: 'authz_resource_01HXYZ123ABC456DEF789GHI', - external_id: 'doc-1', - name: 'Document 1', - description: 'A test document', - resource_type: 'document', - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - parent_resource_id: null, - created_at: '2024-01-15T09:30:00.000Z', - updated_at: '2024-01-15T09:30:00.000Z', - }; - - describe('getResource', () => { - it('gets a resource by id', async () => { - fetchOnce(authorizationResourceFixture); - - const resource = await workos.fga.authorization.getResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - expect(resource).toMatchObject({ - object: 'authorization_resource', - id: 'authz_resource_01HXYZ123ABC456DEF789GHI', - externalId: 'doc-1', - name: 'Document 1', - description: 'A test document', - resourceType: 'document', - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - parentResourceId: null, - }); - }); - - it('deserializes all fields correctly', async () => { - const fixtureWithParent = { - ...authorizationResourceFixture, - parent_resource_id: 'authz_resource_parent_123', - }; - fetchOnce(fixtureWithParent); - - const resource = await workos.fga.authorization.getResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(resource.parentResourceId).toBe('authz_resource_parent_123'); - expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); - expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); - }); - }); - - describe('createResource', () => { - it('creates a resource with all options', async () => { - const fixtureWithParent = { - ...authorizationResourceFixture, - parent_resource_id: 'authz_resource_parent_123', - }; - fetchOnce(fixtureWithParent, { status: 201 }); - - const resource = await workos.fga.authorization.createResource({ - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - resourceTypeSlug: 'document', - externalId: 'doc-1', - name: 'Document 1', - description: 'A test document', - parentResourceId: 'authz_resource_parent_123', - }); - - expect(fetchURL()).toContain('/authorization/resources'); - expect(fetchBody()).toEqual({ - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - resource_type_slug: 'document', - external_id: 'doc-1', - name: 'Document 1', - description: 'A test document', - parent_resource_id: 'authz_resource_parent_123', - }); - expect(resource.externalId).toBe('doc-1'); - expect(resource.parentResourceId).toBe('authz_resource_parent_123'); - }); - - it('creates a resource with only required fields', async () => { - const minimalFixture = { - ...authorizationResourceFixture, - description: null, - parent_resource_id: null, - }; - fetchOnce(minimalFixture, { status: 201 }); - - const resource = await workos.fga.authorization.createResource({ - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - resourceTypeSlug: 'document', - externalId: 'doc-2', - name: 'Document 2', - }); - - expect(fetchBody()).toEqual({ - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - resource_type_slug: 'document', - external_id: 'doc-2', - name: 'Document 2', - }); - expect(resource.description).toBeNull(); - expect(resource.parentResourceId).toBeNull(); - }); - }); - - describe('updateResource', () => { - it('updates a resource name', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - name: 'Updated Document', - }; - fetchOnce(updatedFixture); - - const resource = await workos.fga.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - name: 'Updated Document', - }); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - expect(fetchBody()).toEqual({ - name: 'Updated Document', - }); - expect(resource.name).toBe('Updated Document'); - }); - - it('updates a resource description', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - description: 'Updated description', - }; - fetchOnce(updatedFixture); - - const resource = await workos.fga.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - description: 'Updated description', - }); - - expect(fetchBody()).toEqual({ - description: 'Updated description', - }); - expect(resource.description).toBe('Updated description'); - }); - - it('clears description when set to null', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - description: null, - }; - fetchOnce(updatedFixture); - - const resource = await workos.fga.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - description: null, - }); - - expect(fetchBody()).toEqual({ - description: null, - }); - expect(resource.description).toBeNull(); - }); - - it('updates both name and description', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - name: 'New Name', - description: 'New Description', - }; - fetchOnce(updatedFixture); - - const resource = await workos.fga.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - name: 'New Name', - description: 'New Description', - }); - - expect(fetchBody()).toEqual({ - name: 'New Name', - description: 'New Description', - }); - expect(resource.name).toBe('New Name'); - expect(resource.description).toBe('New Description'); - }); - }); - - describe('deleteResource', () => { - it('deletes a resource', async () => { - fetchOnce({}, { status: 204 }); - - await workos.fga.authorization.deleteResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - }); - }); - }); }); From ca6f2d13dc0ae7d7ab82a12f0f829c6f4ec82b67 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:14:52 -1000 Subject: [PATCH 07/29] lol --- src/fga/serializers/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/fga/serializers/index.ts b/src/fga/serializers/index.ts index c4255c08a..3bb36abd9 100644 --- a/src/fga/serializers/index.ts +++ b/src/fga/serializers/index.ts @@ -11,6 +11,3 @@ export * from './warrant-token.serializer'; export * from './warrant.serializer'; export * from './write-warrant-options.serializer'; export * from './list.serializer'; - -// Authorization Resources (Advanced RBAC) -export * from './authorization-resource.serializer'; From dfdd37d198a9932cc9830b635b226f0d254e2cb5 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:18:52 -1000 Subject: [PATCH 08/29] lol --- src/fga/authorization.ts | 4 +-- ...uthorization-resource-options.interface.ts | 28 ----------------- .../authorization-resource.interface.ts | 31 +++++++++++++++++++ src/fga/interfaces/index.ts | 1 - .../authorization-resource.serializer.ts | 4 +-- 5 files changed, 33 insertions(+), 35 deletions(-) delete mode 100644 src/fga/interfaces/authorization-resource-options.interface.ts diff --git a/src/fga/authorization.ts b/src/fga/authorization.ts index 6d81c7d47..cd3955ee2 100644 --- a/src/fga/authorization.ts +++ b/src/fga/authorization.ts @@ -2,11 +2,9 @@ import { WorkOS } from '../workos'; import { AuthorizationResource, AuthorizationResourceResponse, -} from './interfaces/authorization-resource.interface'; -import { CreateAuthorizationResourceOptions, UpdateAuthorizationResourceOptions, -} from './interfaces/authorization-resource-options.interface'; +} from './interfaces/authorization-resource.interface'; import { deserializeAuthorizationResource, serializeCreateAuthorizationResourceOptions, diff --git a/src/fga/interfaces/authorization-resource-options.interface.ts b/src/fga/interfaces/authorization-resource-options.interface.ts deleted file mode 100644 index 297f593d8..000000000 --- a/src/fga/interfaces/authorization-resource-options.interface.ts +++ /dev/null @@ -1,28 +0,0 @@ -export interface CreateAuthorizationResourceOptions { - organizationId: string; - resourceTypeSlug: string; - externalId: string; - name: string; - description?: string; - parentResourceId?: string; -} - -export interface SerializedCreateAuthorizationResourceOptions { - organization_id: string; - resource_type_slug: string; - external_id: string; - name: string; - description?: string; - parent_resource_id?: string; -} - -export interface UpdateAuthorizationResourceOptions { - resourceId: string; - name?: string; - description?: string | null; -} - -export interface SerializedUpdateAuthorizationResourceOptions { - name?: string; - description?: string | null; -} diff --git a/src/fga/interfaces/authorization-resource.interface.ts b/src/fga/interfaces/authorization-resource.interface.ts index 74259df9a..34bb3e0b0 100644 --- a/src/fga/interfaces/authorization-resource.interface.ts +++ b/src/fga/interfaces/authorization-resource.interface.ts @@ -41,3 +41,34 @@ export interface AuthorizationResourceListResponse { after: string | null; }; } + +// Create / Update options + +export interface CreateAuthorizationResourceOptions { + organizationId: string; + resourceTypeSlug: string; + externalId: string; + name: string; + description?: string; + parentResourceId?: string; +} + +export interface SerializedCreateAuthorizationResourceOptions { + organization_id: string; + resource_type_slug: string; + external_id: string; + name: string; + description?: string; + parent_resource_id?: string; +} + +export interface UpdateAuthorizationResourceOptions { + resourceId: string; + name?: string; + description?: string | null; +} + +export interface SerializedUpdateAuthorizationResourceOptions { + name?: string; + description?: string | null; +} diff --git a/src/fga/interfaces/index.ts b/src/fga/interfaces/index.ts index 52bf750ee..71965a33d 100644 --- a/src/fga/interfaces/index.ts +++ b/src/fga/interfaces/index.ts @@ -9,4 +9,3 @@ export * from './warrant.interface'; // Authorization Resources (Advanced RBAC) export * from './authorization-resource.interface'; -export * from './authorization-resource-options.interface'; diff --git a/src/fga/serializers/authorization-resource.serializer.ts b/src/fga/serializers/authorization-resource.serializer.ts index 10aa489f9..ad6169cc4 100644 --- a/src/fga/serializers/authorization-resource.serializer.ts +++ b/src/fga/serializers/authorization-resource.serializer.ts @@ -1,13 +1,11 @@ import { AuthorizationResource, AuthorizationResourceResponse, -} from '../interfaces/authorization-resource.interface'; -import { CreateAuthorizationResourceOptions, SerializedCreateAuthorizationResourceOptions, UpdateAuthorizationResourceOptions, SerializedUpdateAuthorizationResourceOptions, -} from '../interfaces/authorization-resource-options.interface'; +} from '../interfaces/authorization-resource.interface'; /** * Deserializes an authorization resource from API response format to SDK format. From 43a0765a5056c34c2d18b44f961d8c996801d966 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:27:18 -1000 Subject: [PATCH 09/29] lol --- src/authorization/index.ts | 2 +- .../interfaces/authorization-resource.interface.ts | 0 src/authorization/interfaces/index.ts | 8 +------- .../serializers/authorization-resource.serializer.ts | 2 +- .../serializers/create-resource-options.serializer.ts | 2 +- .../serializers/update-resource-options.serializer.ts | 2 +- src/fga/authorization.ts | 2 +- src/fga/interfaces/index.ts | 3 --- src/fga/serializers/authorization-resource.serializer.ts | 2 +- 9 files changed, 7 insertions(+), 16 deletions(-) rename src/{fga => authorization}/interfaces/authorization-resource.interface.ts (100%) diff --git a/src/authorization/index.ts b/src/authorization/index.ts index d7b90839c..06580c94d 100644 --- a/src/authorization/index.ts +++ b/src/authorization/index.ts @@ -1,2 +1,2 @@ export * from './authorization'; -export * from './interfaces'; +export * from './interfaces'; \ No newline at end of file diff --git a/src/fga/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts similarity index 100% rename from src/fga/interfaces/authorization-resource.interface.ts rename to src/authorization/interfaces/authorization-resource.interface.ts diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index f5d551293..5c45cd2d0 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -13,10 +13,4 @@ export * from './permission.interface'; export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; -// Re-export from fga for authorization resource types (Advanced RBAC) -export { - AuthorizationResource, - AuthorizationResourceResponse, - CreateAuthorizationResourceOptions, - UpdateAuthorizationResourceOptions, -} from '../../fga/interfaces'; +export * from './authorization-resource.interface'; \ No newline at end of file diff --git a/src/authorization/serializers/authorization-resource.serializer.ts b/src/authorization/serializers/authorization-resource.serializer.ts index 015128d5d..7077a42e4 100644 --- a/src/authorization/serializers/authorization-resource.serializer.ts +++ b/src/authorization/serializers/authorization-resource.serializer.ts @@ -1,7 +1,7 @@ import { AuthorizationResource, AuthorizationResourceResponse, -} from '../../fga/interfaces'; +} from '../interfaces/authorization-resource.interface'; /** * Deserializes an API response into an AuthorizationResource. diff --git a/src/authorization/serializers/create-resource-options.serializer.ts b/src/authorization/serializers/create-resource-options.serializer.ts index 02f08d8f1..5b77b4cc5 100644 --- a/src/authorization/serializers/create-resource-options.serializer.ts +++ b/src/authorization/serializers/create-resource-options.serializer.ts @@ -1,7 +1,7 @@ import { CreateAuthorizationResourceOptions, SerializedCreateAuthorizationResourceOptions, -} from '../../fga/interfaces'; +} from '../interfaces/authorization-resource.interface'; /** * Serializes CreateAuthorizationResourceOptions for the API request. diff --git a/src/authorization/serializers/update-resource-options.serializer.ts b/src/authorization/serializers/update-resource-options.serializer.ts index 8000f76be..d4d83dbd0 100644 --- a/src/authorization/serializers/update-resource-options.serializer.ts +++ b/src/authorization/serializers/update-resource-options.serializer.ts @@ -1,7 +1,7 @@ import { UpdateAuthorizationResourceOptions, SerializedUpdateAuthorizationResourceOptions, -} from '../../fga/interfaces'; +} from '../interfaces/authorization-resource.interface'; /** * Serializes UpdateAuthorizationResourceOptions for the API request. diff --git a/src/fga/authorization.ts b/src/fga/authorization.ts index cd3955ee2..21e1a8c0f 100644 --- a/src/fga/authorization.ts +++ b/src/fga/authorization.ts @@ -4,7 +4,7 @@ import { AuthorizationResourceResponse, CreateAuthorizationResourceOptions, UpdateAuthorizationResourceOptions, -} from './interfaces/authorization-resource.interface'; +} from '../authorization/interfaces/authorization-resource.interface'; import { deserializeAuthorizationResource, serializeCreateAuthorizationResourceOptions, diff --git a/src/fga/interfaces/index.ts b/src/fga/interfaces/index.ts index 71965a33d..0c94e50fe 100644 --- a/src/fga/interfaces/index.ts +++ b/src/fga/interfaces/index.ts @@ -6,6 +6,3 @@ export * from './resource.interface'; export * from './warrant-op.enum'; export * from './warrant-token.interface'; export * from './warrant.interface'; - -// Authorization Resources (Advanced RBAC) -export * from './authorization-resource.interface'; diff --git a/src/fga/serializers/authorization-resource.serializer.ts b/src/fga/serializers/authorization-resource.serializer.ts index ad6169cc4..57c414c19 100644 --- a/src/fga/serializers/authorization-resource.serializer.ts +++ b/src/fga/serializers/authorization-resource.serializer.ts @@ -5,7 +5,7 @@ import { SerializedCreateAuthorizationResourceOptions, UpdateAuthorizationResourceOptions, SerializedUpdateAuthorizationResourceOptions, -} from '../interfaces/authorization-resource.interface'; +} from '../../authorization/interfaces/authorization-resource.interface'; /** * Deserializes an authorization resource from API response format to SDK format. From b61be8afc81b1fd5aaabd38e4bfd3471d2ba48b5 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:34:41 -1000 Subject: [PATCH 10/29] pretty --- src/authorization/index.ts | 2 +- src/authorization/interfaces/index.ts | 2 +- src/fga/fga.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/authorization/index.ts b/src/authorization/index.ts index 06580c94d..d7b90839c 100644 --- a/src/authorization/index.ts +++ b/src/authorization/index.ts @@ -1,2 +1,2 @@ export * from './authorization'; -export * from './interfaces'; \ No newline at end of file +export * from './interfaces'; diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index 5c45cd2d0..d81de36c8 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -13,4 +13,4 @@ export * from './permission.interface'; export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; -export * from './authorization-resource.interface'; \ No newline at end of file +export * from './authorization-resource.interface'; diff --git a/src/fga/fga.ts b/src/fga/fga.ts index 3d6cac3c4..f3f221ad5 100644 --- a/src/fga/fga.ts +++ b/src/fga/fga.ts @@ -52,7 +52,7 @@ import { FgaPaginatable } from './utils/fga-paginatable'; import { fetchAndDeserializeFGAList } from './utils/fetch-and-deserialize-list'; /** - * @deprecated + * @deprecated */ export class FGA { constructor(private readonly workos: WorkOS) {} From 0b65b75dab6735c0953becfd4886d98159b7c204 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:39:01 -1000 Subject: [PATCH 11/29] moar --- src/authorization/authorization.ts | 19 ----- .../authorization-resource.serializer.ts | 4 - .../create-resource-options.serializer.ts | 4 - .../update-resource-options.serializer.ts | 5 -- src/fga/authorization.ts | 74 ------------------- .../authorization-resource.serializer.ts | 12 --- 6 files changed, 118 deletions(-) delete mode 100644 src/fga/authorization.ts diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index c3e4e8f52..ac1fd8610 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -246,11 +246,6 @@ export class Authorization { await this.workos.delete(`/authorization/permissions/${slug}`); } - /** - * Retrieves an authorization resource by its internal ID. - * @param resourceId - The internal resource ID - * @returns The authorization resource - */ async getResource(resourceId: string): Promise { const { data } = await this.workos.get( `/authorization/resources/${resourceId}`, @@ -258,11 +253,6 @@ export class Authorization { return deserializeAuthorizationResource(data); } - /** - * Creates a new authorization resource. - * @param options - The resource creation options - * @returns The created authorization resource - */ async createResource( options: CreateAuthorizationResourceOptions, ): Promise { @@ -273,11 +263,6 @@ export class Authorization { return deserializeAuthorizationResource(data); } - /** - * Updates an authorization resource by its internal ID. - * @param options - The resource update options including resourceId - * @returns The updated authorization resource - */ async updateResource( options: UpdateAuthorizationResourceOptions, ): Promise { @@ -288,10 +273,6 @@ export class Authorization { return deserializeAuthorizationResource(data); } - /** - * Deletes an authorization resource by its internal ID. - * @param resourceId - The internal resource ID - */ async deleteResource(resourceId: string): Promise { await this.workos.delete(`/authorization/resources/${resourceId}`); } diff --git a/src/authorization/serializers/authorization-resource.serializer.ts b/src/authorization/serializers/authorization-resource.serializer.ts index 7077a42e4..71e2d682f 100644 --- a/src/authorization/serializers/authorization-resource.serializer.ts +++ b/src/authorization/serializers/authorization-resource.serializer.ts @@ -3,10 +3,6 @@ import { AuthorizationResourceResponse, } from '../interfaces/authorization-resource.interface'; -/** - * Deserializes an API response into an AuthorizationResource. - * Converts snake_case API fields to camelCase. - */ export const deserializeAuthorizationResource = ( resource: AuthorizationResourceResponse, ): AuthorizationResource => ({ diff --git a/src/authorization/serializers/create-resource-options.serializer.ts b/src/authorization/serializers/create-resource-options.serializer.ts index 5b77b4cc5..c11ea971b 100644 --- a/src/authorization/serializers/create-resource-options.serializer.ts +++ b/src/authorization/serializers/create-resource-options.serializer.ts @@ -3,10 +3,6 @@ import { SerializedCreateAuthorizationResourceOptions, } from '../interfaces/authorization-resource.interface'; -/** - * Serializes CreateAuthorizationResourceOptions for the API request. - * Converts camelCase to snake_case for API compatibility. - */ export const serializeCreateResourceOptions = ( options: CreateAuthorizationResourceOptions, ): SerializedCreateAuthorizationResourceOptions => ({ diff --git a/src/authorization/serializers/update-resource-options.serializer.ts b/src/authorization/serializers/update-resource-options.serializer.ts index d4d83dbd0..edb758c9c 100644 --- a/src/authorization/serializers/update-resource-options.serializer.ts +++ b/src/authorization/serializers/update-resource-options.serializer.ts @@ -3,11 +3,6 @@ import { SerializedUpdateAuthorizationResourceOptions, } from '../interfaces/authorization-resource.interface'; -/** - * Serializes UpdateAuthorizationResourceOptions for the API request. - * Only includes fields that are provided (name and/or description). - * resourceId is excluded as it's used in the URL path. - */ export const serializeUpdateResourceOptions = ( options: UpdateAuthorizationResourceOptions, ): SerializedUpdateAuthorizationResourceOptions => ({ diff --git a/src/fga/authorization.ts b/src/fga/authorization.ts deleted file mode 100644 index 21e1a8c0f..000000000 --- a/src/fga/authorization.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { WorkOS } from '../workos'; -import { - AuthorizationResource, - AuthorizationResourceResponse, - CreateAuthorizationResourceOptions, - UpdateAuthorizationResourceOptions, -} from '../authorization/interfaces/authorization-resource.interface'; -import { - deserializeAuthorizationResource, - serializeCreateAuthorizationResourceOptions, - serializeUpdateAuthorizationResourceOptions, -} from './serializers/authorization-resource.serializer'; - -/** - * FGA Authorization module for managing authorization resources. - * Provides CRUD operations for resources in the Advanced RBAC system. - */ -export class FGAAuthorization { - constructor(private readonly workos: WorkOS) {} - - /** - * Gets an authorization resource by its internal ID. - * - * @param resourceId - The internal resource ID (e.g., 'authz_resource_01H...') - * @returns The authorization resource - */ - async getResource(resourceId: string): Promise { - const { data } = await this.workos.get( - `/authorization/resources/${resourceId}`, - ); - return deserializeAuthorizationResource(data); - } - - /** - * Creates a new authorization resource. - * - * @param options - The resource creation options - * @returns The created authorization resource - */ - async createResource( - options: CreateAuthorizationResourceOptions, - ): Promise { - const { data } = await this.workos.post( - '/authorization/resources', - serializeCreateAuthorizationResourceOptions(options), - ); - return deserializeAuthorizationResource(data); - } - - /** - * Updates an existing authorization resource. - * - * @param options - The resource update options (includes resourceId) - * @returns The updated authorization resource - */ - async updateResource( - options: UpdateAuthorizationResourceOptions, - ): Promise { - const { data } = await this.workos.patch( - `/authorization/resources/${options.resourceId}`, - serializeUpdateAuthorizationResourceOptions(options), - ); - return deserializeAuthorizationResource(data); - } - - /** - * Deletes an authorization resource and all its descendants. - * - * @param resourceId - The internal resource ID to delete - */ - async deleteResource(resourceId: string): Promise { - await this.workos.delete(`/authorization/resources/${resourceId}`); - } -} diff --git a/src/fga/serializers/authorization-resource.serializer.ts b/src/fga/serializers/authorization-resource.serializer.ts index 57c414c19..4d22daff0 100644 --- a/src/fga/serializers/authorization-resource.serializer.ts +++ b/src/fga/serializers/authorization-resource.serializer.ts @@ -7,10 +7,6 @@ import { SerializedUpdateAuthorizationResourceOptions, } from '../../authorization/interfaces/authorization-resource.interface'; -/** - * Deserializes an authorization resource from API response format to SDK format. - * Converts snake_case fields to camelCase. - */ export const deserializeAuthorizationResource = ( resource: AuthorizationResourceResponse, ): AuthorizationResource => ({ @@ -26,10 +22,6 @@ export const deserializeAuthorizationResource = ( updatedAt: resource.updated_at, }); -/** - * Serializes create resource options from SDK format to API request format. - * Converts camelCase fields to snake_case. - */ export const serializeCreateAuthorizationResourceOptions = ( options: CreateAuthorizationResourceOptions, ): SerializedCreateAuthorizationResourceOptions => ({ @@ -41,10 +33,6 @@ export const serializeCreateAuthorizationResourceOptions = ( parent_resource_id: options.parentResourceId, }); -/** - * Serializes update resource options from SDK format to API request format. - * Only includes fields that are explicitly provided (including null for clearing). - */ export const serializeUpdateAuthorizationResourceOptions = ( options: UpdateAuthorizationResourceOptions, ): SerializedUpdateAuthorizationResourceOptions => { From 9281d60aa106e4137c9c5241d579f841ba3edccb Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Mon, 9 Feb 2026 13:43:24 -1000 Subject: [PATCH 12/29] remove --- .../authorization-resource.serializer.ts | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/fga/serializers/authorization-resource.serializer.ts diff --git a/src/fga/serializers/authorization-resource.serializer.ts b/src/fga/serializers/authorization-resource.serializer.ts deleted file mode 100644 index 4d22daff0..000000000 --- a/src/fga/serializers/authorization-resource.serializer.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - AuthorizationResource, - AuthorizationResourceResponse, - CreateAuthorizationResourceOptions, - SerializedCreateAuthorizationResourceOptions, - UpdateAuthorizationResourceOptions, - SerializedUpdateAuthorizationResourceOptions, -} from '../../authorization/interfaces/authorization-resource.interface'; - -export const deserializeAuthorizationResource = ( - resource: AuthorizationResourceResponse, -): AuthorizationResource => ({ - object: resource.object, - id: resource.id, - externalId: resource.external_id, - name: resource.name, - description: resource.description, - resourceType: resource.resource_type, - organizationId: resource.organization_id, - parentResourceId: resource.parent_resource_id, - createdAt: resource.created_at, - updatedAt: resource.updated_at, -}); - -export const serializeCreateAuthorizationResourceOptions = ( - options: CreateAuthorizationResourceOptions, -): SerializedCreateAuthorizationResourceOptions => ({ - organization_id: options.organizationId, - resource_type_slug: options.resourceTypeSlug, - external_id: options.externalId, - name: options.name, - description: options.description, - parent_resource_id: options.parentResourceId, -}); - -export const serializeUpdateAuthorizationResourceOptions = ( - options: UpdateAuthorizationResourceOptions, -): SerializedUpdateAuthorizationResourceOptions => { - const serialized: SerializedUpdateAuthorizationResourceOptions = {}; - - if (options.name !== undefined) { - serialized.name = options.name; - } - - if (options.description !== undefined) { - serialized.description = options.description; - } - - return serialized; -}; From 24f675841ebe615042d8ab9711b5e780e27d72af Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 04:50:17 -1000 Subject: [PATCH 13/29] moar --- src/authorization/authorization.spec.ts | 229 ++---------------- .../fixtures/authorization-resource.json | 2 +- .../authorization-resource.interface.ts | 14 +- .../authorization-resource.serializer.ts | 2 +- 4 files changed, 26 insertions(+), 221 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 1eb8435a6..3d07fee50 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -609,7 +609,7 @@ describe('Authorization', () => { externalId: 'doc-456', name: 'Q4 Budget Report', description: 'Financial report for Q4 2025', - resourceType: 'document', + resourceTypeSlug: 'document', organizationId: testOrgId, parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', }); @@ -622,6 +622,22 @@ describe('Authorization', () => { expect(resource.parentResourceId).toBeNull(); }); + + it('deserializes all fields correctly including timestamps', async () => { + const fixtureWithParent = { + ...authorizationResourceFixture, + parent_resource_id: 'resource_01HXYZ123ABC456DEF789XYZ', + }; + fetchOnce(fixtureWithParent); + + const resource = await workos.authorization.getResource(testResourceId); + + expect(resource.parentResourceId).toBe( + 'resource_01HXYZ123ABC456DEF789XYZ', + ); + expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); + expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); + }); }); describe('createResource', () => { @@ -651,7 +667,7 @@ describe('Authorization', () => { id: testResourceId, externalId: 'doc-456', name: 'Q4 Budget Report', - resourceType: 'document', + resourceTypeSlug: 'document', }); }); @@ -776,213 +792,4 @@ describe('Authorization', () => { }); }); - // ============================================================ - // FGA Authorization (Advanced RBAC) - // ============================================================ - - describe('authorization', () => { - const authorizationResourceFixture = { - object: 'authorization_resource', - id: 'authz_resource_01HXYZ123ABC456DEF789GHI', - external_id: 'doc-1', - name: 'Document 1', - description: 'A test document', - resource_type: 'document', - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - parent_resource_id: null, - created_at: '2024-01-15T09:30:00.000Z', - updated_at: '2024-01-15T09:30:00.000Z', - }; - - describe('getResource', () => { - it('gets a resource by id', async () => { - fetchOnce(authorizationResourceFixture); - - const resource = await workos.authorization.getResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - expect(resource).toMatchObject({ - object: 'authorization_resource', - id: 'authz_resource_01HXYZ123ABC456DEF789GHI', - externalId: 'doc-1', - name: 'Document 1', - description: 'A test document', - resourceType: 'document', - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - parentResourceId: null, - }); - }); - - it('deserializes all fields correctly', async () => { - const fixtureWithParent = { - ...authorizationResourceFixture, - parent_resource_id: 'authz_resource_parent_123', - }; - fetchOnce(fixtureWithParent); - - const resource = await workos.authorization.getResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(resource.parentResourceId).toBe('authz_resource_parent_123'); - expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); - expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); - }); - }); - - describe('createResource', () => { - it('creates a resource with all options', async () => { - const fixtureWithParent = { - ...authorizationResourceFixture, - parent_resource_id: 'authz_resource_parent_123', - }; - fetchOnce(fixtureWithParent, { status: 201 }); - - const resource = await workos.authorization.createResource({ - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - resourceTypeSlug: 'document', - externalId: 'doc-1', - name: 'Document 1', - description: 'A test document', - parentResourceId: 'authz_resource_parent_123', - }); - - expect(fetchURL()).toContain('/authorization/resources'); - expect(fetchBody()).toEqual({ - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - resource_type_slug: 'document', - external_id: 'doc-1', - name: 'Document 1', - description: 'A test document', - parent_resource_id: 'authz_resource_parent_123', - }); - expect(resource.externalId).toBe('doc-1'); - expect(resource.parentResourceId).toBe('authz_resource_parent_123'); - }); - - it('creates a resource with only required fields', async () => { - const minimalFixture = { - ...authorizationResourceFixture, - description: null, - parent_resource_id: null, - }; - fetchOnce(minimalFixture, { status: 201 }); - - const resource = await workos.authorization.createResource({ - organizationId: 'org_01HXYZ123ABC456DEF789ABC', - resourceTypeSlug: 'document', - externalId: 'doc-2', - name: 'Document 2', - }); - - expect(fetchBody()).toEqual({ - organization_id: 'org_01HXYZ123ABC456DEF789ABC', - resource_type_slug: 'document', - external_id: 'doc-2', - name: 'Document 2', - }); - expect(resource.description).toBeNull(); - expect(resource.parentResourceId).toBeNull(); - }); - }); - - describe('updateResource', () => { - it('updates a resource name', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - name: 'Updated Document', - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - name: 'Updated Document', - }); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - expect(fetchBody()).toEqual({ - name: 'Updated Document', - }); - expect(resource.name).toBe('Updated Document'); - }); - - it('updates a resource description', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - description: 'Updated description', - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - description: 'Updated description', - }); - - expect(fetchBody()).toEqual({ - description: 'Updated description', - }); - expect(resource.description).toBe('Updated description'); - }); - - it('clears description when set to null', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - description: null, - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - description: null, - }); - - expect(fetchBody()).toEqual({ - description: null, - }); - expect(resource.description).toBeNull(); - }); - - it('updates both name and description', async () => { - const updatedFixture = { - ...authorizationResourceFixture, - name: 'New Name', - description: 'New Description', - }; - fetchOnce(updatedFixture); - - const resource = await workos.authorization.updateResource({ - resourceId: 'authz_resource_01HXYZ123ABC456DEF789GHI', - name: 'New Name', - description: 'New Description', - }); - - expect(fetchBody()).toEqual({ - name: 'New Name', - description: 'New Description', - }); - expect(resource.name).toBe('New Name'); - expect(resource.description).toBe('New Description'); - }); - }); - - describe('deleteResource', () => { - it('deletes a resource', async () => { - fetchOnce({}, { status: 204 }); - - await workos.authorization.deleteResource( - 'authz_resource_01HXYZ123ABC456DEF789GHI', - ); - - expect(fetchURL()).toContain( - '/authorization/resources/authz_resource_01HXYZ123ABC456DEF789GHI', - ); - }); - }); - }); }); diff --git a/src/authorization/fixtures/authorization-resource.json b/src/authorization/fixtures/authorization-resource.json index 6fcb9f680..ce9eb5362 100644 --- a/src/authorization/fixtures/authorization-resource.json +++ b/src/authorization/fixtures/authorization-resource.json @@ -4,7 +4,7 @@ "external_id": "doc-456", "name": "Q4 Budget Report", "description": "Financial report for Q4 2025", - "resource_type": "document", + "resource_type_slug": "document", "organization_id": "org_01HXYZ123ABC456DEF789ABC", "parent_resource_id": "resource_01HXYZ123ABC456DEF789XYZ", "created_at": "2024-01-15T09:30:00.000Z", diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index 34bb3e0b0..cde65d5fb 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -4,7 +4,7 @@ export interface AuthorizationResource { externalId: string; name: string; description: string | null; - resourceType: string; + resourceTypeSlug: string; organizationId: string; parentResourceId: string | null; createdAt: string; @@ -17,7 +17,7 @@ export interface AuthorizationResourceResponse { external_id: string; name: string; description: string | null; - resource_type: string; + resource_type_slug: string; organization_id: string; parent_resource_id: string | null; created_at: string; @@ -42,23 +42,21 @@ export interface AuthorizationResourceListResponse { }; } -// Create / Update options - export interface CreateAuthorizationResourceOptions { - organizationId: string; - resourceTypeSlug: string; externalId: string; name: string; description?: string; + resourceTypeSlug: string; + organizationId: string; parentResourceId?: string; } export interface SerializedCreateAuthorizationResourceOptions { - organization_id: string; - resource_type_slug: string; external_id: string; name: string; description?: string; + resource_type_slug: string; + organization_id: string; parent_resource_id?: string; } diff --git a/src/authorization/serializers/authorization-resource.serializer.ts b/src/authorization/serializers/authorization-resource.serializer.ts index 71e2d682f..68b10a508 100644 --- a/src/authorization/serializers/authorization-resource.serializer.ts +++ b/src/authorization/serializers/authorization-resource.serializer.ts @@ -11,7 +11,7 @@ export const deserializeAuthorizationResource = ( externalId: resource.external_id, name: resource.name, description: resource.description, - resourceType: resource.resource_type, + resourceTypeSlug: resource.resource_type_slug, organizationId: resource.organization_id, parentResourceId: resource.parent_resource_id, createdAt: resource.created_at, From ea411928cd536336afb265433fbb7ac37cc1bacf Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 05:28:44 -1000 Subject: [PATCH 14/29] moar --- src/authorization/authorization.spec.ts | 111 ++++++++++++++++-- .../authorization-resource.interface.ts | 18 --- ...horization-resource-options.serializer.ts} | 0 src/authorization/serializers/index.ts | 4 +- ...horization-resource-options.serializer.ts} | 0 src/fga/fga.ts | 3 - 6 files changed, 100 insertions(+), 36 deletions(-) rename src/authorization/serializers/{create-resource-options.serializer.ts => create-authorization-resource-options.serializer.ts} (100%) rename src/authorization/serializers/{update-resource-options.serializer.ts => update-authorization-resource-options.serializer.ts} (100%) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 3d07fee50..44ccb0bad 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -612,6 +612,8 @@ describe('Authorization', () => { resourceTypeSlug: 'document', organizationId: testOrgId, parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', }); }); @@ -623,20 +625,17 @@ describe('Authorization', () => { expect(resource.parentResourceId).toBeNull(); }); - it('deserializes all fields correctly including timestamps', async () => { - const fixtureWithParent = { + it('handles resource without parent and without description', async () => { + fetchOnce({ ...authorizationResourceFixture, - parent_resource_id: 'resource_01HXYZ123ABC456DEF789XYZ', - }; - fetchOnce(fixtureWithParent); + parent_resource_id: null, + description: null, + }); const resource = await workos.authorization.getResource(testResourceId); - expect(resource.parentResourceId).toBe( - 'resource_01HXYZ123ABC456DEF789XYZ', - ); - expect(resource.createdAt).toBe('2024-01-15T09:30:00.000Z'); - expect(resource.updatedAt).toBe('2024-01-15T09:30:00.000Z'); + expect(resource.parentResourceId).toBeNull(); + expect(resource.description).toBeNull(); }); }); @@ -667,7 +666,11 @@ describe('Authorization', () => { id: testResourceId, externalId: 'doc-456', name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', resourceTypeSlug: 'document', + parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', }); }); @@ -694,8 +697,91 @@ describe('Authorization', () => { external_id: 'doc-456', name: 'Q4 Budget Report', }); - expect(resource.description).toBeNull(); - expect(resource.parentResourceId).toBeNull(); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: null, + resourceTypeSlug: 'document', + parentResourceId: null, + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', + }); + }); + + it('creates an authorization resource with description but no parent resource', async () => { + fetchOnce( + { + ...authorizationResourceFixture, + parent_resource_id: null, + }, + { status: 201 }, + ); + + const resource = await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + }); + + expect(fetchBody()).toEqual({ + organization_id: testOrgId, + resource_type_slug: 'document', + external_id: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + }); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + resourceTypeSlug: 'document', + parentResourceId: null, + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', + }); + }); + + it('creates an authorization resource with parent resource but no description', async () => { + fetchOnce( + { + ...authorizationResourceFixture, + description: null, + }, + { status: 201 }, + ); + + const resource = await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + + expect(fetchBody()).toEqual({ + organization_id: testOrgId, + resource_type_slug: 'document', + external_id: 'doc-456', + name: 'Q4 Budget Report', + parent_resource_id: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: null, + resourceTypeSlug: 'document', + parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', + }); }); }); @@ -791,5 +877,4 @@ describe('Authorization', () => { ); }); }); - }); diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index cde65d5fb..9d721a27f 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -24,24 +24,6 @@ export interface AuthorizationResourceResponse { updated_at: string; } -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; - }; -} - export interface CreateAuthorizationResourceOptions { externalId: string; name: string; diff --git a/src/authorization/serializers/create-resource-options.serializer.ts b/src/authorization/serializers/create-authorization-resource-options.serializer.ts similarity index 100% rename from src/authorization/serializers/create-resource-options.serializer.ts rename to src/authorization/serializers/create-authorization-resource-options.serializer.ts diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index edd572720..5b8a3c029 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -8,5 +8,5 @@ export * from './permission.serializer'; export * from './create-permission-options.serializer'; export * from './update-permission-options.serializer'; export * from './authorization-resource.serializer'; -export * from './create-resource-options.serializer'; -export * from './update-resource-options.serializer'; +export * from './create-authorization-resource-options.serializer'; +export * from './update-authorization-resource-options.serializer'; diff --git a/src/authorization/serializers/update-resource-options.serializer.ts b/src/authorization/serializers/update-authorization-resource-options.serializer.ts similarity index 100% rename from src/authorization/serializers/update-resource-options.serializer.ts rename to src/authorization/serializers/update-authorization-resource-options.serializer.ts diff --git a/src/fga/fga.ts b/src/fga/fga.ts index f3f221ad5..bb2cfadd9 100644 --- a/src/fga/fga.ts +++ b/src/fga/fga.ts @@ -51,9 +51,6 @@ import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize'; import { FgaPaginatable } from './utils/fga-paginatable'; import { fetchAndDeserializeFGAList } from './utils/fetch-and-deserialize-list'; -/** - * @deprecated - */ export class FGA { constructor(private readonly workos: WorkOS) {} From 6fb444cf85870f6dad378f58405a915b2d963399 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 05:52:46 -1000 Subject: [PATCH 15/29] moar --- src/authorization/authorization.spec.ts | 102 ++++++++++++++++-- .../authorization-resource.interface.ts | 8 +- ...thorization-resource-options.serializer.ts | 8 +- ...thorization-resource-options.serializer.ts | 6 +- 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 44ccb0bad..7582928ac 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -783,10 +783,76 @@ describe('Authorization', () => { updatedAt: '2024-01-15T09:30:00.000Z', }); }); + + it('excludes description and parentResourceId when omitted (undefined)', async () => { + fetchOnce( + { + ...authorizationResourceFixture, + description: null, + parent_resource_id: null, + }, + { status: 201 }, + ); + + await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + }); + + const body = fetchBody(); + expect(body).not.toHaveProperty('description'); + expect(body).not.toHaveProperty('parent_resource_id'); + }); + + it('sends null when description is explicitly set to null', async () => { + fetchOnce( + { + ...authorizationResourceFixture, + description: null, + }, + { status: 201 }, + ); + + await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: null, + }); + + const body = fetchBody(); + expect(body).toHaveProperty('description', null); + expect(body).not.toHaveProperty('parent_resource_id'); + }); + + it('sends null when parentResourceId is explicitly set to null', async () => { + fetchOnce( + { + ...authorizationResourceFixture, + parent_resource_id: null, + }, + { status: 201 }, + ); + + await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + parentResourceId: null, + }); + + const body = fetchBody(); + expect(body).toHaveProperty('parent_resource_id', null); + expect(body).not.toHaveProperty('description'); + }); }); describe('updateResource', () => { - it('updates resource name', async () => { + it('updates name when description is omitted', async () => { const updatedResourceFixture = { ...authorizationResourceFixture, name: 'Updated Report Name', @@ -801,13 +867,14 @@ describe('Authorization', () => { expect(fetchURL()).toContain( `/authorization/resources/${testResourceId}`, ); - expect(fetchBody()).toEqual({ - name: 'Updated Report Name', - }); + const body = fetchBody(); + expect(body).toEqual({ name: 'Updated Report Name' }); + expect(body).not.toHaveProperty('description'); expect(resource.name).toBe('Updated Report Name'); + expect(resource.description).toBe('Financial report for Q4 2025'); }); - it('updates resource description', async () => { + it('updates description when name is omitted', async () => { const updatedResourceFixture = { ...authorizationResourceFixture, description: 'Updated description', @@ -819,10 +886,11 @@ describe('Authorization', () => { description: 'Updated description', }); - expect(fetchBody()).toEqual({ - description: 'Updated description', - }); + const body = fetchBody(); + expect(body).toEqual({ description: 'Updated description' }); + expect(body).not.toHaveProperty('name'); expect(resource.description).toBe('Updated description'); + expect(resource.name).toBe('Q4 Budget Report'); }); it('clears description when set to null', async () => { @@ -837,12 +905,24 @@ describe('Authorization', () => { description: null, }); - expect(fetchBody()).toEqual({ - description: null, - }); + const body = fetchBody(); + expect(body).toEqual({ description: null }); + expect(body).not.toHaveProperty('name'); expect(resource.description).toBeNull(); }); + it('excludes description from request body when undefined', async () => { + fetchOnce(authorizationResourceFixture); + + await workos.authorization.updateResource({ + resourceId: testResourceId, + name: 'Some Name', + }); + + const body = fetchBody(); + expect(body).not.toHaveProperty('description'); + }); + it('updates both name and description', async () => { const updatedResourceFixture = { ...authorizationResourceFixture, diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index 9d721a27f..2f01bedad 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -27,19 +27,19 @@ export interface AuthorizationResourceResponse { export interface CreateAuthorizationResourceOptions { externalId: string; name: string; - description?: string; + description?: string | null; resourceTypeSlug: string; organizationId: string; - parentResourceId?: string; + parentResourceId?: string | null; } export interface SerializedCreateAuthorizationResourceOptions { external_id: string; name: string; - description?: string; + description?: string | null; resource_type_slug: string; organization_id: string; - parent_resource_id?: string; + parent_resource_id?: string | null; } export interface UpdateAuthorizationResourceOptions { diff --git a/src/authorization/serializers/create-authorization-resource-options.serializer.ts b/src/authorization/serializers/create-authorization-resource-options.serializer.ts index c11ea971b..f63f843a7 100644 --- a/src/authorization/serializers/create-authorization-resource-options.serializer.ts +++ b/src/authorization/serializers/create-authorization-resource-options.serializer.ts @@ -10,6 +10,10 @@ export const serializeCreateResourceOptions = ( resource_type_slug: options.resourceTypeSlug, external_id: options.externalId, name: options.name, - description: options.description, - parent_resource_id: options.parentResourceId, + ...(options.description !== undefined && { + description: options.description, + }), + ...(options.parentResourceId !== undefined && { + parent_resource_id: options.parentResourceId, + }), }); diff --git a/src/authorization/serializers/update-authorization-resource-options.serializer.ts b/src/authorization/serializers/update-authorization-resource-options.serializer.ts index edb758c9c..cbd3e95ea 100644 --- a/src/authorization/serializers/update-authorization-resource-options.serializer.ts +++ b/src/authorization/serializers/update-authorization-resource-options.serializer.ts @@ -6,6 +6,8 @@ import { export const serializeUpdateResourceOptions = ( options: UpdateAuthorizationResourceOptions, ): SerializedUpdateAuthorizationResourceOptions => ({ - name: options.name, - description: options.description, + ...(options.name !== undefined && { name: options.name }), + ...(options.description !== undefined && { + description: options.description, + }), }); From 5689beea1cde20c957884c646eb06e00ab8ad8c8 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 05:57:04 -1000 Subject: [PATCH 16/29] moar --- src/authorization/authorization.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 7582928ac..435d8fcf3 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -784,7 +784,7 @@ describe('Authorization', () => { }); }); - it('excludes description and parentResourceId when omitted (undefined)', async () => { + it('excludes description and parentResourceId when omitted', async () => { fetchOnce( { ...authorizationResourceFixture, From f77627fe9ed32a0bacfe76f291ff0fc11bb690c7 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 06:48:23 -1000 Subject: [PATCH 17/29] lol --- src/authorization/authorization.spec.ts | 222 ++++++++++++++++++ src/authorization/authorization.ts | 91 +++++++ .../fixtures/list-resources.json | 33 +++ .../authorization-resource.interface.ts | 24 ++ ...source-by-external-id-options.interface.ts | 14 ++ ...source-by-external-id-options.interface.ts | 14 ++ src/authorization/interfaces/index.ts | 4 + .../list-resources-options.interface.ts | 45 ++++ ...source-by-external-id-options.interface.ts | 18 ++ src/authorization/serializers/index.ts | 1 + .../list-resources-options.serializer.ts | 35 +++ 11 files changed, 501 insertions(+) create mode 100644 src/authorization/fixtures/list-resources.json create mode 100644 src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts create mode 100644 src/authorization/interfaces/get-resource-by-external-id-options.interface.ts create mode 100644 src/authorization/interfaces/list-resources-options.interface.ts create mode 100644 src/authorization/interfaces/update-resource-by-external-id-options.interface.ts create mode 100644 src/authorization/serializers/list-resources-options.serializer.ts diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 435d8fcf3..f33e82ae2 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -13,6 +13,7 @@ import listOrganizationRolesFixture from './fixtures/list-organization-roles.jso import permissionFixture from './fixtures/permission.json'; import listPermissionsFixture from './fixtures/list-permissions.json'; import authorizationResourceFixture from './fixtures/authorization-resource.json'; +import listResourcesFixture from './fixtures/list-resources.json'; const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); const testOrgId = 'org_01HXYZ123ABC456DEF789ABC'; @@ -957,4 +958,225 @@ describe('Authorization', () => { ); }); }); + + describe('listResources', () => { + it('returns a paginated list of resources', async () => { + fetchOnce(listResourcesFixture); + + const { data, object, listMetadata } = + await workos.authorization.listResources(); + + expect(fetchURL()).toContain('/authorization/resources'); + expect(object).toEqual('list'); + expect(data).toHaveLength(2); + expect(data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + object: 'authorization_resource', + id: 'resource_01HXYZ123ABC456DEF789ABC', + externalId: 'doc-456', + name: 'Q4 Budget Report', + resourceTypeSlug: 'document', + }), + expect.objectContaining({ + object: 'authorization_resource', + id: 'resource_01HXYZ123ABC456DEF789DEF', + externalId: 'folder-123', + name: 'Finance Folder', + resourceTypeSlug: 'folder', + }), + ]), + ); + expect(listMetadata).toEqual({ + before: null, + after: 'resource_01HXYZ123ABC456DEF789DEF', + }); + }); + + it('passes filter parameters as comma-separated strings', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ + organizationIds: ['org_01', 'org_02'], + resourceTypeSlugs: ['document', 'folder'], + }); + + expect(fetchSearchParams()).toEqual({ + organization_ids: 'org_01,org_02', + resource_type_slugs: 'document,folder', + }); + }); + + it('passes pagination parameters', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ + limit: 10, + after: 'resource_01HXYZ123ABC456DEF789ABC', + order: 'desc', + }); + + expect(fetchSearchParams()).toEqual({ + limit: '10', + after: 'resource_01HXYZ123ABC456DEF789ABC', + order: 'desc', + }); + }); + + it('passes parent resource filters', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ + parentResourceId: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + + expect(fetchSearchParams()).toEqual({ + parent_resource_id: 'resource_01HXYZ123ABC456DEF789XYZ', + }); + }); + + it('passes parent external ID filters', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ + parentResourceTypeSlug: 'folder', + parentExternalId: 'folder-123', + }); + + expect(fetchSearchParams()).toEqual({ + parent_resource_type_slug: 'folder', + parent_external_id: 'folder-123', + }); + }); + + it('passes search filter', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ + search: 'Budget', + }); + + expect(fetchSearchParams()).toEqual({ + search: 'Budget', + }); + }); + }); + + describe('getResourceByExternalId', () => { + it('gets a resource by organization, type, and external ID', async () => { + fetchOnce(authorizationResourceFixture); + + const resource = await workos.authorization.getResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(fetchURL()).toContain( + `/authorization/organizations/${testOrgId}/resource_types/document/resources/doc-456`, + ); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + resourceTypeSlug: 'document', + organizationId: testOrgId, + }); + }); + + it('handles resource without parent', async () => { + fetchOnce({ ...authorizationResourceFixture, parent_resource_id: null }); + + const resource = await workos.authorization.getResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(resource.parentResourceId).toBeNull(); + }); + }); + + describe('updateResourceByExternalId', () => { + it('updates a resource by external ID', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + name: 'Updated Report Name', + description: 'Updated description', + }; + fetchOnce(updatedResourceFixture); + + const resource = await workos.authorization.updateResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Updated Report Name', + description: 'Updated description', + }); + + expect(fetchURL()).toContain( + `/authorization/organizations/${testOrgId}/resource_types/document/resources/doc-456`, + ); + expect(fetchBody()).toEqual({ + name: 'Updated Report Name', + description: 'Updated description', + }); + expect(resource.name).toBe('Updated Report Name'); + expect(resource.description).toBe('Updated description'); + }); + + it('updates only name when description is omitted', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + name: 'New Name', + }; + fetchOnce(updatedResourceFixture); + + await workos.authorization.updateResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'New Name', + }); + + const body = fetchBody(); + expect(body).toEqual({ name: 'New Name' }); + expect(body).not.toHaveProperty('description'); + }); + + it('clears description when set to null', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + description: null, + }; + fetchOnce(updatedResourceFixture); + + const resource = await workos.authorization.updateResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + description: null, + }); + + expect(fetchBody()).toEqual({ description: null }); + expect(resource.description).toBeNull(); + }); + }); + + describe('deleteResourceByExternalId', () => { + it('deletes a resource by external ID', async () => { + fetchOnce({}, { status: 204 }); + + await workos.authorization.deleteResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(fetchURL()).toContain( + `/authorization/organizations/${testOrgId}/resource_types/document/resources/doc-456`, + ); + }); + }); }); diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index ac1fd8610..25abf1da9 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -29,8 +29,14 @@ import { ListPermissionsOptions, AuthorizationResource, AuthorizationResourceResponse, + AuthorizationResourceList, + AuthorizationResourceListResponse, CreateAuthorizationResourceOptions, UpdateAuthorizationResourceOptions, + ListAuthorizationResourcesOptions, + GetResourceByExternalIdOptions, + UpdateResourceByExternalIdOptions, + DeleteResourceByExternalIdOptions, } from './interfaces'; import { deserializeEnvironmentRole, @@ -46,6 +52,7 @@ import { deserializeAuthorizationResource, serializeCreateResourceOptions, serializeUpdateResourceOptions, + serializeListAuthorizationResourcesOptions, } from './serializers'; export class Authorization { @@ -276,4 +283,88 @@ export class Authorization { async deleteResource(resourceId: string): Promise { await this.workos.delete(`/authorization/resources/${resourceId}`); } + + /** + * List authorization resources with optional filtering and pagination. + * + * @param options - Filter and pagination options + * @returns Paginated list of authorization resources + */ + async listResources( + options?: ListAuthorizationResourcesOptions, + ): Promise { + const { data } = await this.workos.get( + '/authorization/resources', + { + query: options + ? serializeListAuthorizationResourcesOptions(options) + : undefined, + }, + ); + return { + object: 'list', + data: data.data.map(deserializeAuthorizationResource), + listMetadata: { + before: data.list_metadata.before, + after: data.list_metadata.after, + }, + }; + } + + /** + * Get an authorization resource by its external ID. + * + * Uses the organization + resource type + external ID path format + * to uniquely identify the resource. + * + * @param options - The organization ID, resource type slug, and external ID + * @returns The authorization resource + */ + async getResourceByExternalId( + options: GetResourceByExternalIdOptions, + ): Promise { + const { organizationId, resourceTypeSlug, externalId } = options; + const { data } = await this.workos.get( + `/authorization/organizations/${organizationId}/resource_types/${resourceTypeSlug}/resources/${externalId}`, + ); + return deserializeAuthorizationResource(data); + } + + /** + * Update an authorization resource by its external ID. + * + * Uses the organization + resource type + external ID path format. + * At least one of name or description must be provided. + * + * @param options - The organization ID, resource type slug, external ID, and fields to update + * @returns The updated authorization resource + */ + async updateResourceByExternalId( + options: UpdateResourceByExternalIdOptions, + ): Promise { + const { organizationId, resourceTypeSlug, externalId, ...updateFields } = + options; + const { data } = await this.workos.patch( + `/authorization/organizations/${organizationId}/resource_types/${resourceTypeSlug}/resources/${externalId}`, + serializeUpdateResourceOptions({ resourceId: '', ...updateFields }), + ); + return deserializeAuthorizationResource(data); + } + + /** + * Delete an authorization resource by its external ID. + * + * Uses the organization + resource type + external ID path format + * to uniquely identify the resource to delete. + * + * @param options - The organization ID, resource type slug, and external ID + */ + async deleteResourceByExternalId( + options: DeleteResourceByExternalIdOptions, + ): Promise { + const { organizationId, resourceTypeSlug, externalId } = options; + await this.workos.delete( + `/authorization/organizations/${organizationId}/resource_types/${resourceTypeSlug}/resources/${externalId}`, + ); + } } diff --git a/src/authorization/fixtures/list-resources.json b/src/authorization/fixtures/list-resources.json new file mode 100644 index 000000000..0cc17df84 --- /dev/null +++ b/src/authorization/fixtures/list-resources.json @@ -0,0 +1,33 @@ +{ + "object": "list", + "data": [ + { + "object": "authorization_resource", + "id": "resource_01HXYZ123ABC456DEF789ABC", + "external_id": "doc-456", + "name": "Q4 Budget Report", + "description": "Financial report for Q4 2025", + "resource_type_slug": "document", + "organization_id": "org_01HXYZ123ABC456DEF789ABC", + "parent_resource_id": "resource_01HXYZ123ABC456DEF789XYZ", + "created_at": "2024-01-15T09:30:00.000Z", + "updated_at": "2024-01-15T09:30:00.000Z" + }, + { + "object": "authorization_resource", + "id": "resource_01HXYZ123ABC456DEF789DEF", + "external_id": "folder-123", + "name": "Finance Folder", + "description": null, + "resource_type_slug": "folder", + "organization_id": "org_01HXYZ123ABC456DEF789ABC", + "parent_resource_id": null, + "created_at": "2024-01-14T08:00:00.000Z", + "updated_at": "2024-01-14T08:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": "resource_01HXYZ123ABC456DEF789DEF" + } +} diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index 2f01bedad..f359bccfc 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -52,3 +52,27 @@ export interface SerializedUpdateAuthorizationResourceOptions { name?: string; description?: string | null; } + +/** + * SDK representation of a paginated list of authorization resources. + */ +export interface AuthorizationResourceList { + object: 'list'; + data: AuthorizationResource[]; + listMetadata: { + before: string | null; + after: string | null; + }; +} + +/** + * API response format for a paginated list of authorization resources. + */ +export interface AuthorizationResourceListResponse { + object: 'list'; + data: AuthorizationResourceResponse[]; + list_metadata: { + before: string | null; + after: string | null; + }; +} diff --git a/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts new file mode 100644 index 000000000..7e978f17e --- /dev/null +++ b/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts @@ -0,0 +1,14 @@ +/** + * Options for deleting a resource by its external ID. + * + * Uses the organization + resource type + external ID path format + * to uniquely identify the resource to delete. + */ +export interface DeleteResourceByExternalIdOptions { + /** The organization ID that owns the resource */ + organizationId: string; + /** The resource type slug (e.g., 'document', 'folder') */ + resourceTypeSlug: string; + /** The external ID of the resource */ + externalId: string; +} diff --git a/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts new file mode 100644 index 000000000..0f66e0a41 --- /dev/null +++ b/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts @@ -0,0 +1,14 @@ +/** + * Options for getting a resource by its external ID. + * + * Uses the organization + resource type + external ID path format + * to uniquely identify a resource. + */ +export interface GetResourceByExternalIdOptions { + /** The organization ID that owns the resource */ + organizationId: string; + /** The resource type slug (e.g., 'document', 'folder') */ + resourceTypeSlug: string; + /** The external ID of the resource */ + externalId: string; +} diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index d81de36c8..e6165a38e 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -14,3 +14,7 @@ export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; export * from './authorization-resource.interface'; +export * from './list-resources-options.interface'; +export * from './get-resource-by-external-id-options.interface'; +export * from './update-resource-by-external-id-options.interface'; +export * from './delete-resource-by-external-id-options.interface'; diff --git a/src/authorization/interfaces/list-resources-options.interface.ts b/src/authorization/interfaces/list-resources-options.interface.ts new file mode 100644 index 000000000..4c8a8d35f --- /dev/null +++ b/src/authorization/interfaces/list-resources-options.interface.ts @@ -0,0 +1,45 @@ +/** + * Options for listing authorization resources. + * + * Supports filtering by organization, resource type, parent resource, + * and search. Arrays are converted to comma-separated strings for the API. + */ +export interface ListAuthorizationResourcesOptions { + /** Filter by organization IDs (will be comma-separated in API request) */ + organizationIds?: string[]; + /** Filter by resource type slugs (will be comma-separated in API request) */ + resourceTypeSlugs?: string[]; + /** Filter by parent resource ID */ + parentResourceId?: string; + /** Filter by parent resource type slug (used with parentExternalId) */ + parentResourceTypeSlug?: string; + /** Filter by parent external ID (used with parentResourceTypeSlug) */ + parentExternalId?: string; + /** Search filter for resource names */ + search?: string; + /** Maximum number of results per page */ + limit?: number; + /** Cursor for forward pagination */ + after?: string; + /** Cursor for backward pagination */ + before?: string; + /** Sort order for results */ + order?: 'asc' | 'desc'; +} + +/** + * Serialized format for API request query parameters. + * Array fields are converted to comma-separated strings. + */ +export interface SerializedListAuthorizationResourcesOptions { + organization_ids?: string; + resource_type_slugs?: string; + parent_resource_id?: string; + parent_resource_type_slug?: string; + parent_external_id?: string; + search?: string; + limit?: number; + after?: string; + before?: string; + order?: 'asc' | 'desc'; +} diff --git a/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts new file mode 100644 index 000000000..307c02361 --- /dev/null +++ b/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts @@ -0,0 +1,18 @@ +/** + * Options for updating a resource by its external ID. + * + * Uses the organization + resource type + external ID path format. + * At least one of name or description must be provided. + */ +export interface UpdateResourceByExternalIdOptions { + /** The organization ID that owns the resource */ + organizationId: string; + /** The resource type slug (e.g., 'document', 'folder') */ + resourceTypeSlug: string; + /** The external ID of the resource */ + externalId: string; + /** New name for the resource (optional) */ + name?: string; + /** New description for the resource (optional, null to clear) */ + description?: string | null; +} diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index 5b8a3c029..eb42714a4 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -10,3 +10,4 @@ export * from './update-permission-options.serializer'; export * from './authorization-resource.serializer'; export * from './create-authorization-resource-options.serializer'; export * from './update-authorization-resource-options.serializer'; +export * from './list-resources-options.serializer'; diff --git a/src/authorization/serializers/list-resources-options.serializer.ts b/src/authorization/serializers/list-resources-options.serializer.ts new file mode 100644 index 000000000..9c094804e --- /dev/null +++ b/src/authorization/serializers/list-resources-options.serializer.ts @@ -0,0 +1,35 @@ +import { + ListAuthorizationResourcesOptions, + SerializedListAuthorizationResourcesOptions, +} from '../interfaces/list-resources-options.interface'; + +/** + * Serialize list resources options from SDK format to API query parameters. + * + * Converts array fields (organizationIds, resourceTypeSlugs) to comma-separated + * strings as expected by the API. + */ +export const serializeListAuthorizationResourcesOptions = ( + options: ListAuthorizationResourcesOptions, +): SerializedListAuthorizationResourcesOptions => ({ + ...(options.organizationIds && { + organization_ids: options.organizationIds.join(','), + }), + ...(options.resourceTypeSlugs && { + resource_type_slugs: options.resourceTypeSlugs.join(','), + }), + ...(options.parentResourceId && { + parent_resource_id: options.parentResourceId, + }), + ...(options.parentResourceTypeSlug && { + parent_resource_type_slug: options.parentResourceTypeSlug, + }), + ...(options.parentExternalId && { + parent_external_id: options.parentExternalId, + }), + ...(options.search && { search: options.search }), + ...(options.limit && { limit: options.limit }), + ...(options.after && { after: options.after }), + ...(options.before && { before: options.before }), + ...(options.order && { order: options.order }), +}); From 1ddca3961bd09e94d065ffb35ff4d5189078eb4c Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 07:30:15 -1000 Subject: [PATCH 18/29] comments --- src/authorization/authorization.ts | 32 ------------------- .../authorization-resource.interface.ts | 6 ---- ...source-by-external-id-options.interface.ts | 9 ------ ...source-by-external-id-options.interface.ts | 9 ------ .../list-resources-options.interface.ts | 20 ------------ ...source-by-external-id-options.interface.ts | 11 ------- .../list-resources-options.serializer.ts | 6 ---- 7 files changed, 93 deletions(-) diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 25abf1da9..b145d1b01 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -284,12 +284,6 @@ export class Authorization { await this.workos.delete(`/authorization/resources/${resourceId}`); } - /** - * List authorization resources with optional filtering and pagination. - * - * @param options - Filter and pagination options - * @returns Paginated list of authorization resources - */ async listResources( options?: ListAuthorizationResourcesOptions, ): Promise { @@ -311,15 +305,6 @@ export class Authorization { }; } - /** - * Get an authorization resource by its external ID. - * - * Uses the organization + resource type + external ID path format - * to uniquely identify the resource. - * - * @param options - The organization ID, resource type slug, and external ID - * @returns The authorization resource - */ async getResourceByExternalId( options: GetResourceByExternalIdOptions, ): Promise { @@ -330,15 +315,6 @@ export class Authorization { return deserializeAuthorizationResource(data); } - /** - * Update an authorization resource by its external ID. - * - * Uses the organization + resource type + external ID path format. - * At least one of name or description must be provided. - * - * @param options - The organization ID, resource type slug, external ID, and fields to update - * @returns The updated authorization resource - */ async updateResourceByExternalId( options: UpdateResourceByExternalIdOptions, ): Promise { @@ -351,14 +327,6 @@ export class Authorization { return deserializeAuthorizationResource(data); } - /** - * Delete an authorization resource by its external ID. - * - * Uses the organization + resource type + external ID path format - * to uniquely identify the resource to delete. - * - * @param options - The organization ID, resource type slug, and external ID - */ async deleteResourceByExternalId( options: DeleteResourceByExternalIdOptions, ): Promise { diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index f359bccfc..94cead6ab 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -53,9 +53,6 @@ export interface SerializedUpdateAuthorizationResourceOptions { description?: string | null; } -/** - * SDK representation of a paginated list of authorization resources. - */ export interface AuthorizationResourceList { object: 'list'; data: AuthorizationResource[]; @@ -65,9 +62,6 @@ export interface AuthorizationResourceList { }; } -/** - * API response format for a paginated list of authorization resources. - */ export interface AuthorizationResourceListResponse { object: 'list'; data: AuthorizationResourceResponse[]; diff --git a/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts index 7e978f17e..e80b164cb 100644 --- a/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts +++ b/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts @@ -1,14 +1,5 @@ -/** - * Options for deleting a resource by its external ID. - * - * Uses the organization + resource type + external ID path format - * to uniquely identify the resource to delete. - */ export interface DeleteResourceByExternalIdOptions { - /** The organization ID that owns the resource */ organizationId: string; - /** The resource type slug (e.g., 'document', 'folder') */ resourceTypeSlug: string; - /** The external ID of the resource */ externalId: string; } diff --git a/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts index 0f66e0a41..cefc9e7f5 100644 --- a/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts +++ b/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts @@ -1,14 +1,5 @@ -/** - * Options for getting a resource by its external ID. - * - * Uses the organization + resource type + external ID path format - * to uniquely identify a resource. - */ export interface GetResourceByExternalIdOptions { - /** The organization ID that owns the resource */ organizationId: string; - /** The resource type slug (e.g., 'document', 'folder') */ resourceTypeSlug: string; - /** The external ID of the resource */ externalId: string; } diff --git a/src/authorization/interfaces/list-resources-options.interface.ts b/src/authorization/interfaces/list-resources-options.interface.ts index 4c8a8d35f..f3f2a9072 100644 --- a/src/authorization/interfaces/list-resources-options.interface.ts +++ b/src/authorization/interfaces/list-resources-options.interface.ts @@ -1,36 +1,16 @@ -/** - * Options for listing authorization resources. - * - * Supports filtering by organization, resource type, parent resource, - * and search. Arrays are converted to comma-separated strings for the API. - */ export interface ListAuthorizationResourcesOptions { - /** Filter by organization IDs (will be comma-separated in API request) */ organizationIds?: string[]; - /** Filter by resource type slugs (will be comma-separated in API request) */ resourceTypeSlugs?: string[]; - /** Filter by parent resource ID */ parentResourceId?: string; - /** Filter by parent resource type slug (used with parentExternalId) */ parentResourceTypeSlug?: string; - /** Filter by parent external ID (used with parentResourceTypeSlug) */ parentExternalId?: string; - /** Search filter for resource names */ search?: string; - /** Maximum number of results per page */ limit?: number; - /** Cursor for forward pagination */ after?: string; - /** Cursor for backward pagination */ before?: string; - /** Sort order for results */ order?: 'asc' | 'desc'; } -/** - * Serialized format for API request query parameters. - * Array fields are converted to comma-separated strings. - */ export interface SerializedListAuthorizationResourcesOptions { organization_ids?: string; resource_type_slugs?: string; diff --git a/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts index 307c02361..4d6118126 100644 --- a/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts +++ b/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts @@ -1,18 +1,7 @@ -/** - * Options for updating a resource by its external ID. - * - * Uses the organization + resource type + external ID path format. - * At least one of name or description must be provided. - */ export interface UpdateResourceByExternalIdOptions { - /** The organization ID that owns the resource */ organizationId: string; - /** The resource type slug (e.g., 'document', 'folder') */ resourceTypeSlug: string; - /** The external ID of the resource */ externalId: string; - /** New name for the resource (optional) */ name?: string; - /** New description for the resource (optional, null to clear) */ description?: string | null; } diff --git a/src/authorization/serializers/list-resources-options.serializer.ts b/src/authorization/serializers/list-resources-options.serializer.ts index 9c094804e..444691170 100644 --- a/src/authorization/serializers/list-resources-options.serializer.ts +++ b/src/authorization/serializers/list-resources-options.serializer.ts @@ -3,12 +3,6 @@ import { SerializedListAuthorizationResourcesOptions, } from '../interfaces/list-resources-options.interface'; -/** - * Serialize list resources options from SDK format to API query parameters. - * - * Converts array fields (organizationIds, resourceTypeSlugs) to comma-separated - * strings as expected by the API. - */ export const serializeListAuthorizationResourcesOptions = ( options: ListAuthorizationResourcesOptions, ): SerializedListAuthorizationResourcesOptions => ({ From 3e8744684ee60b36714f1582cb4470758d31ad79 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 11:39:38 -1000 Subject: [PATCH 19/29] moar --- src/authorization/authorization.spec.ts | 4 ++-- src/authorization/authorization.ts | 1 + src/authorization/fixtures/list-resources.json | 6 +++--- .../interfaces/list-resources-options.interface.ts | 8 +++----- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index f33e82ae2..b933936d5 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -974,8 +974,8 @@ describe('Authorization', () => { expect.objectContaining({ object: 'authorization_resource', id: 'resource_01HXYZ123ABC456DEF789ABC', - externalId: 'doc-456', - name: 'Q4 Budget Report', + externalId: 'doc-12345678', + name: 'Q5 Budget Report', resourceTypeSlug: 'document', }), expect.objectContaining({ diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index b145d1b01..5fe060f6b 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -284,6 +284,7 @@ export class Authorization { await this.workos.delete(`/authorization/resources/${resourceId}`); } + // part 2 async listResources( options?: ListAuthorizationResourcesOptions, ): Promise { diff --git a/src/authorization/fixtures/list-resources.json b/src/authorization/fixtures/list-resources.json index 0cc17df84..0f64cafdc 100644 --- a/src/authorization/fixtures/list-resources.json +++ b/src/authorization/fixtures/list-resources.json @@ -4,9 +4,9 @@ { "object": "authorization_resource", "id": "resource_01HXYZ123ABC456DEF789ABC", - "external_id": "doc-456", - "name": "Q4 Budget Report", - "description": "Financial report for Q4 2025", + "external_id": "doc-12345678", + "name": "Q5 Budget Report", + "description": "Financial report for Q5 2025", "resource_type_slug": "document", "organization_id": "org_01HXYZ123ABC456DEF789ABC", "parent_resource_id": "resource_01HXYZ123ABC456DEF789XYZ", diff --git a/src/authorization/interfaces/list-resources-options.interface.ts b/src/authorization/interfaces/list-resources-options.interface.ts index f3f2a9072..713a8dd21 100644 --- a/src/authorization/interfaces/list-resources-options.interface.ts +++ b/src/authorization/interfaces/list-resources-options.interface.ts @@ -1,14 +1,12 @@ -export interface ListAuthorizationResourcesOptions { +import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; + +export interface ListAuthorizationResourcesOptions extends PaginationOptions { organizationIds?: string[]; resourceTypeSlugs?: string[]; parentResourceId?: string; parentResourceTypeSlug?: string; parentExternalId?: string; search?: string; - limit?: number; - after?: string; - before?: string; - order?: 'asc' | 'desc'; } export interface SerializedListAuthorizationResourcesOptions { From 7364e4607173731b3618b22fc51e1b8e6660e905 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 11:47:26 -1000 Subject: [PATCH 20/29] moar --- src/authorization/authorization.ts | 12 ++++++------ ...ion-resource-by-external-id-options.interface.ts} | 2 +- ...ion-resource-by-external-id-options.interface.ts} | 2 +- src/authorization/interfaces/index.ts | 8 ++++---- ...ist-authorization-resources-options.interface.ts} | 0 ...ion-resource-by-external-id-options.interface.ts} | 2 +- src/authorization/serializers/index.ts | 2 +- ...st-authorization-resources-options.serializer.ts} | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) rename src/authorization/interfaces/{get-resource-by-external-id-options.interface.ts => delete-authorization-resource-by-external-id-options.interface.ts} (54%) rename src/authorization/interfaces/{delete-resource-by-external-id-options.interface.ts => get-authorization-resource-by-external-id-options.interface.ts} (55%) rename src/authorization/interfaces/{list-resources-options.interface.ts => list-authorization-resources-options.interface.ts} (100%) rename src/authorization/interfaces/{update-resource-by-external-id-options.interface.ts => update-authorization-resource-by-external-id-options.interface.ts} (65%) rename src/authorization/serializers/{list-resources-options.serializer.ts => list-authorization-resources-options.serializer.ts} (93%) diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 5fe060f6b..14dbd66c6 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -34,9 +34,9 @@ import { CreateAuthorizationResourceOptions, UpdateAuthorizationResourceOptions, ListAuthorizationResourcesOptions, - GetResourceByExternalIdOptions, - UpdateResourceByExternalIdOptions, - DeleteResourceByExternalIdOptions, + GetAuthorizationResourceByExternalIdOptions, + UpdateAuthorizationResourceByExternalIdOptions, + DeleteAuthorizationResourceByExternalIdOptions, } from './interfaces'; import { deserializeEnvironmentRole, @@ -307,7 +307,7 @@ export class Authorization { } async getResourceByExternalId( - options: GetResourceByExternalIdOptions, + options: GetAuthorizationResourceByExternalIdOptions, ): Promise { const { organizationId, resourceTypeSlug, externalId } = options; const { data } = await this.workos.get( @@ -317,7 +317,7 @@ export class Authorization { } async updateResourceByExternalId( - options: UpdateResourceByExternalIdOptions, + options: UpdateAuthorizationResourceByExternalIdOptions, ): Promise { const { organizationId, resourceTypeSlug, externalId, ...updateFields } = options; @@ -329,7 +329,7 @@ export class Authorization { } async deleteResourceByExternalId( - options: DeleteResourceByExternalIdOptions, + options: DeleteAuthorizationResourceByExternalIdOptions, ): Promise { const { organizationId, resourceTypeSlug, externalId } = options; await this.workos.delete( diff --git a/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/delete-authorization-resource-by-external-id-options.interface.ts similarity index 54% rename from src/authorization/interfaces/get-resource-by-external-id-options.interface.ts rename to src/authorization/interfaces/delete-authorization-resource-by-external-id-options.interface.ts index cefc9e7f5..d0e2d75c7 100644 --- a/src/authorization/interfaces/get-resource-by-external-id-options.interface.ts +++ b/src/authorization/interfaces/delete-authorization-resource-by-external-id-options.interface.ts @@ -1,4 +1,4 @@ -export interface GetResourceByExternalIdOptions { +export interface DeleteAuthorizationResourceByExternalIdOptions { organizationId: string; resourceTypeSlug: string; externalId: string; diff --git a/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/get-authorization-resource-by-external-id-options.interface.ts similarity index 55% rename from src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts rename to src/authorization/interfaces/get-authorization-resource-by-external-id-options.interface.ts index e80b164cb..7663c0395 100644 --- a/src/authorization/interfaces/delete-resource-by-external-id-options.interface.ts +++ b/src/authorization/interfaces/get-authorization-resource-by-external-id-options.interface.ts @@ -1,4 +1,4 @@ -export interface DeleteResourceByExternalIdOptions { +export interface GetAuthorizationResourceByExternalIdOptions { organizationId: string; resourceTypeSlug: string; externalId: string; diff --git a/src/authorization/interfaces/index.ts b/src/authorization/interfaces/index.ts index e6165a38e..96c127fc2 100644 --- a/src/authorization/interfaces/index.ts +++ b/src/authorization/interfaces/index.ts @@ -14,7 +14,7 @@ export * from './create-permission-options.interface'; export * from './update-permission-options.interface'; export * from './list-permissions-options.interface'; export * from './authorization-resource.interface'; -export * from './list-resources-options.interface'; -export * from './get-resource-by-external-id-options.interface'; -export * from './update-resource-by-external-id-options.interface'; -export * from './delete-resource-by-external-id-options.interface'; +export * from './list-authorization-resources-options.interface'; +export * from './get-authorization-resource-by-external-id-options.interface'; +export * from './update-authorization-resource-by-external-id-options.interface'; +export * from './delete-authorization-resource-by-external-id-options.interface'; diff --git a/src/authorization/interfaces/list-resources-options.interface.ts b/src/authorization/interfaces/list-authorization-resources-options.interface.ts similarity index 100% rename from src/authorization/interfaces/list-resources-options.interface.ts rename to src/authorization/interfaces/list-authorization-resources-options.interface.ts diff --git a/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts b/src/authorization/interfaces/update-authorization-resource-by-external-id-options.interface.ts similarity index 65% rename from src/authorization/interfaces/update-resource-by-external-id-options.interface.ts rename to src/authorization/interfaces/update-authorization-resource-by-external-id-options.interface.ts index 4d6118126..b3320475a 100644 --- a/src/authorization/interfaces/update-resource-by-external-id-options.interface.ts +++ b/src/authorization/interfaces/update-authorization-resource-by-external-id-options.interface.ts @@ -1,4 +1,4 @@ -export interface UpdateResourceByExternalIdOptions { +export interface UpdateAuthorizationResourceByExternalIdOptions { organizationId: string; resourceTypeSlug: string; externalId: string; diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index eb42714a4..b8f3e3893 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -10,4 +10,4 @@ export * from './update-permission-options.serializer'; export * from './authorization-resource.serializer'; export * from './create-authorization-resource-options.serializer'; export * from './update-authorization-resource-options.serializer'; -export * from './list-resources-options.serializer'; +export * from './list-authorization-resources-options.serializer'; diff --git a/src/authorization/serializers/list-resources-options.serializer.ts b/src/authorization/serializers/list-authorization-resources-options.serializer.ts similarity index 93% rename from src/authorization/serializers/list-resources-options.serializer.ts rename to src/authorization/serializers/list-authorization-resources-options.serializer.ts index 444691170..7762b94b0 100644 --- a/src/authorization/serializers/list-resources-options.serializer.ts +++ b/src/authorization/serializers/list-authorization-resources-options.serializer.ts @@ -1,7 +1,7 @@ import { ListAuthorizationResourcesOptions, SerializedListAuthorizationResourcesOptions, -} from '../interfaces/list-resources-options.interface'; +} from '../interfaces/list-authorization-resources-options.interface'; export const serializeListAuthorizationResourcesOptions = ( options: ListAuthorizationResourcesOptions, From 0dc5d3075c2d4fd645bb997b8629a6ecdec30694 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 12:09:34 -1000 Subject: [PATCH 21/29] moar --- src/authorization/authorization.ts | 12 ++++++------ src/authorization/serializers/index.ts | 1 + ...ion-resource-by-external-id-options.serializer.ts | 11 +++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 src/authorization/serializers/update-authorization-resource-by-external-id-options.serializer.ts diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 14dbd66c6..3a5719bbb 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -52,6 +52,7 @@ import { deserializeAuthorizationResource, serializeCreateResourceOptions, serializeUpdateResourceOptions, + serializeUpdateResourceByExternalIdOptions, serializeListAuthorizationResourcesOptions, } from './serializers'; @@ -311,7 +312,7 @@ export class Authorization { ): Promise { const { organizationId, resourceTypeSlug, externalId } = options; const { data } = await this.workos.get( - `/authorization/organizations/${organizationId}/resource_types/${resourceTypeSlug}/resources/${externalId}`, + `/authorization/organizations/${organizationId}/resources/${resourceTypeSlug}/${externalId}`, ); return deserializeAuthorizationResource(data); } @@ -319,11 +320,10 @@ export class Authorization { async updateResourceByExternalId( options: UpdateAuthorizationResourceByExternalIdOptions, ): Promise { - const { organizationId, resourceTypeSlug, externalId, ...updateFields } = - options; + const { organizationId, resourceTypeSlug, externalId } = options; const { data } = await this.workos.patch( - `/authorization/organizations/${organizationId}/resource_types/${resourceTypeSlug}/resources/${externalId}`, - serializeUpdateResourceOptions({ resourceId: '', ...updateFields }), + `/authorization/organizations/${organizationId}/resources/${resourceTypeSlug}/${externalId}`, + serializeUpdateResourceByExternalIdOptions(options), ); return deserializeAuthorizationResource(data); } @@ -333,7 +333,7 @@ export class Authorization { ): Promise { const { organizationId, resourceTypeSlug, externalId } = options; await this.workos.delete( - `/authorization/organizations/${organizationId}/resource_types/${resourceTypeSlug}/resources/${externalId}`, + `/authorization/organizations/${organizationId}/resources/${resourceTypeSlug}/${externalId}`, ); } } diff --git a/src/authorization/serializers/index.ts b/src/authorization/serializers/index.ts index b8f3e3893..9fc045233 100644 --- a/src/authorization/serializers/index.ts +++ b/src/authorization/serializers/index.ts @@ -10,4 +10,5 @@ export * from './update-permission-options.serializer'; export * from './authorization-resource.serializer'; export * from './create-authorization-resource-options.serializer'; export * from './update-authorization-resource-options.serializer'; +export * from './update-authorization-resource-by-external-id-options.serializer'; export * from './list-authorization-resources-options.serializer'; diff --git a/src/authorization/serializers/update-authorization-resource-by-external-id-options.serializer.ts b/src/authorization/serializers/update-authorization-resource-by-external-id-options.serializer.ts new file mode 100644 index 000000000..906d79057 --- /dev/null +++ b/src/authorization/serializers/update-authorization-resource-by-external-id-options.serializer.ts @@ -0,0 +1,11 @@ +import { UpdateAuthorizationResourceByExternalIdOptions } from '../interfaces/update-authorization-resource-by-external-id-options.interface'; +import { SerializedUpdateAuthorizationResourceOptions } from '../interfaces/authorization-resource.interface'; + +export const serializeUpdateResourceByExternalIdOptions = ( + options: UpdateAuthorizationResourceByExternalIdOptions, +): SerializedUpdateAuthorizationResourceOptions => ({ + ...(options.name !== undefined && { name: options.name }), + ...(options.description !== undefined && { + description: options.description, + }), +}); From be4b7d394760e99725c06e661821b6e9e73a8db0 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 12:11:54 -1000 Subject: [PATCH 22/29] lol --- src/authorization/authorization.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index b933936d5..729c43d37 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -1073,7 +1073,7 @@ describe('Authorization', () => { }); expect(fetchURL()).toContain( - `/authorization/organizations/${testOrgId}/resource_types/document/resources/doc-456`, + `/authorization/organizations/${testOrgId}/resources/document/doc-456`, ); expect(resource).toMatchObject({ object: 'authorization_resource', @@ -1116,7 +1116,7 @@ describe('Authorization', () => { }); expect(fetchURL()).toContain( - `/authorization/organizations/${testOrgId}/resource_types/document/resources/doc-456`, + `/authorization/organizations/${testOrgId}/resources/document/doc-456`, ); expect(fetchBody()).toEqual({ name: 'Updated Report Name', @@ -1175,7 +1175,7 @@ describe('Authorization', () => { }); expect(fetchURL()).toContain( - `/authorization/organizations/${testOrgId}/resource_types/document/resources/doc-456`, + `/authorization/organizations/${testOrgId}/resources/document/doc-456`, ); }); }); From b38b24d34fc2cf5744cc1cbf61c45125e18afd0b Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 12:28:46 -1000 Subject: [PATCH 23/29] lol --- src/authorization/authorization.spec.ts | 63 +++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 729c43d37..63590c1c8 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -1082,6 +1082,8 @@ describe('Authorization', () => { name: 'Q4 Budget Report', resourceTypeSlug: 'document', organizationId: testOrgId, + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', }); }); @@ -1122,8 +1124,15 @@ describe('Authorization', () => { name: 'Updated Report Name', description: 'Updated description', }); - expect(resource.name).toBe('Updated Report Name'); - expect(resource.description).toBe('Updated description'); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Updated Report Name', + description: 'Updated description', + resourceTypeSlug: 'document', + organizationId: testOrgId + }); }); it('updates only name when description is omitted', async () => { @@ -1145,6 +1154,48 @@ describe('Authorization', () => { expect(body).not.toHaveProperty('description'); }); + it('updates only description when name is omitted', async () => { + const updatedResourceFixture = { + ...authorizationResourceFixture, + description: 'new description', + }; + fetchOnce(updatedResourceFixture); + + await workos.authorization.updateResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + description: 'new description', + }); + + const body = fetchBody(); + expect(body).toEqual({ description: 'new description' }); + expect(body).not.toHaveProperty('name'); + }); + + it('returns unchanged resource when body is empty', async () => { + fetchOnce(authorizationResourceFixture); + + const resource = await workos.authorization.updateResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(fetchBody()).toEqual({}); + expect(resource).toMatchObject({ + object: 'authorization_resource', + id: testResourceId, + externalId: 'doc-456', + name: 'Q4 Budget Report', + description: 'Financial report for Q4 2025', + resourceTypeSlug: 'document', + organizationId: testOrgId, + createdAt: '2024-01-15T09:30:00.000Z', + updatedAt: '2024-01-15T09:30:00.000Z', + }); + }); + it('clears description when set to null', async () => { const updatedResourceFixture = { ...authorizationResourceFixture, @@ -1167,15 +1218,17 @@ describe('Authorization', () => { describe('deleteResourceByExternalId', () => { it('deletes a resource by external ID', async () => { fetchOnce({}, { status: 204 }); + const resourceTypeSlug = 'document'; + const externalId = 'externalId'; await workos.authorization.deleteResourceByExternalId({ organizationId: testOrgId, - resourceTypeSlug: 'document', - externalId: 'doc-456', + resourceTypeSlug: resourceTypeSlug, + externalId: externalId, }); expect(fetchURL()).toContain( - `/authorization/organizations/${testOrgId}/resources/document/doc-456`, + `/authorization/organizations/${testOrgId}/resources/${resourceTypeSlug}/${externalId}`, ); }); }); From fc934f07c9033f2a1d12cc199c07e03f33a66eb1 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 12:32:56 -1000 Subject: [PATCH 24/29] moar Co-authored-by: Cursor --- src/authorization/authorization.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 63590c1c8..57554a4d6 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -1098,6 +1098,18 @@ describe('Authorization', () => { expect(resource.parentResourceId).toBeNull(); }); + + it('handles resource without description', async () => { + fetchOnce({ ...authorizationResourceFixture, description: null }); + + const resource = await workos.authorization.getResourceByExternalId({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + }); + + expect(resource.description).toBeNull(); + }); }); describe('updateResourceByExternalId', () => { From de58c5666d6989c09693cc5b04d072bc379ddc22 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 12:47:21 -1000 Subject: [PATCH 25/29] Add missing `before` pagination parameter test for listResources The interface and serializer both supported `before`, but it was never exercised. Added a test with `before` + `order: 'asc'` complementing the existing `after` + `order: 'desc'` test. Co-authored-by: Cursor --- src/authorization/authorization.spec.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 57554a4d6..2200e28c9 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -1007,7 +1007,7 @@ describe('Authorization', () => { }); }); - it('passes pagination parameters', async () => { + it('passes pagination parameters with after cursor', async () => { fetchOnce(listResourcesFixture); await workos.authorization.listResources({ @@ -1023,6 +1023,22 @@ describe('Authorization', () => { }); }); + it('passes pagination parameters with before cursor', async () => { + fetchOnce(listResourcesFixture); + + await workos.authorization.listResources({ + limit: 10, + before: 'resource_01HXYZ123ABC456DEF789DEF', + order: 'asc', + }); + + expect(fetchSearchParams()).toEqual({ + limit: '10', + before: 'resource_01HXYZ123ABC456DEF789DEF', + order: 'asc', + }); + }); + it('passes parent resource filters', async () => { fetchOnce(listResourcesFixture); From 1d6124d4f80cf42a12e7679c580079c04eac9eae Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 12:51:04 -1000 Subject: [PATCH 26/29] lol --- src/authorization/authorization.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index 2200e28c9..9ac3e1520 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -1159,7 +1159,7 @@ describe('Authorization', () => { name: 'Updated Report Name', description: 'Updated description', resourceTypeSlug: 'document', - organizationId: testOrgId + organizationId: testOrgId, }); }); From 64383ed5f532d75329e2399a12c05d27c7cd5c35 Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 13:12:13 -1000 Subject: [PATCH 27/29] lol --- .../list-authorization-resources-options.serializer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/authorization/serializers/list-authorization-resources-options.serializer.ts b/src/authorization/serializers/list-authorization-resources-options.serializer.ts index 7762b94b0..bb3cc6e2b 100644 --- a/src/authorization/serializers/list-authorization-resources-options.serializer.ts +++ b/src/authorization/serializers/list-authorization-resources-options.serializer.ts @@ -22,8 +22,8 @@ export const serializeListAuthorizationResourcesOptions = ( parent_external_id: options.parentExternalId, }), ...(options.search && { search: options.search }), - ...(options.limit && { limit: options.limit }), - ...(options.after && { after: options.after }), + ...(options.limit !== undefined && { limit: options.limit }), + ...(options.after && { after: options.after }), ...(options.before && { before: options.before }), ...(options.order && { order: options.order }), }); From bb2fb27f41266ad56c7cdd2b9a769bb5863e317e Mon Sep 17 00:00:00 2001 From: swaroopakkineni Date: Tue, 10 Feb 2026 13:18:44 -1000 Subject: [PATCH 28/29] lol --- .../list-authorization-resources-options.serializer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/authorization/serializers/list-authorization-resources-options.serializer.ts b/src/authorization/serializers/list-authorization-resources-options.serializer.ts index bb3cc6e2b..b8313348e 100644 --- a/src/authorization/serializers/list-authorization-resources-options.serializer.ts +++ b/src/authorization/serializers/list-authorization-resources-options.serializer.ts @@ -23,7 +23,7 @@ export const serializeListAuthorizationResourcesOptions = ( }), ...(options.search && { search: options.search }), ...(options.limit !== undefined && { limit: options.limit }), - ...(options.after && { after: options.after }), + ...(options.after && { after: options.after }), ...(options.before && { before: options.before }), ...(options.order && { order: options.order }), }); From 53d03ba882838fab9289d9095441cc53873a4699 Mon Sep 17 00:00:00 2001 From: swaroopThereItIs Date: Wed, 11 Feb 2026 11:31:29 -1000 Subject: [PATCH 29/29] union of parentResourceExternalId & parentResourceTypeSlug (#1480) --- src/authorization/authorization.spec.ts | 97 +++++++------------ .../authorization-resource.interface.ts | 18 +++- ...thorization-resource-options.serializer.ts | 6 +- 3 files changed, 52 insertions(+), 69 deletions(-) diff --git a/src/authorization/authorization.spec.ts b/src/authorization/authorization.spec.ts index dff2ba6c7..83a079478 100644 --- a/src/authorization/authorization.spec.ts +++ b/src/authorization/authorization.spec.ts @@ -675,21 +675,15 @@ describe('Authorization', () => { }); }); - it('creates an authorization resource with required fields only', async () => { - fetchOnce( - { - ...authorizationResourceFixture, - description: null, - parent_resource_id: null, - }, - { status: 201 }, - ); + it('creates an authorization resource with parentResourceId', async () => { + fetchOnce(authorizationResourceFixture, { status: 201 }); const resource = await workos.authorization.createResource({ organizationId: testOrgId, resourceTypeSlug: 'document', externalId: 'doc-456', name: 'Q4 Budget Report', + parentResourceId: 'resource_01HXYZ', }); expect(fetchBody()).toEqual({ @@ -697,28 +691,21 @@ describe('Authorization', () => { resource_type_slug: 'document', external_id: 'doc-456', name: 'Q4 Budget Report', + parent_resource_id: 'resource_01HXYZ', }); expect(resource).toMatchObject({ object: 'authorization_resource', id: testResourceId, externalId: 'doc-456', name: 'Q4 Budget Report', - description: null, resourceTypeSlug: 'document', - parentResourceId: null, createdAt: '2024-01-15T09:30:00.000Z', updatedAt: '2024-01-15T09:30:00.000Z', }); }); - it('creates an authorization resource with description but no parent resource', async () => { - fetchOnce( - { - ...authorizationResourceFixture, - parent_resource_id: null, - }, - { status: 201 }, - ); + it('creates an authorization resource with description and parentResourceId', async () => { + fetchOnce(authorizationResourceFixture, { status: 201 }); const resource = await workos.authorization.createResource({ organizationId: testOrgId, @@ -726,6 +713,7 @@ describe('Authorization', () => { externalId: 'doc-456', name: 'Q4 Budget Report', description: 'Financial report for Q4 2025', + parentResourceId: 'resource_01HXYZ', }); expect(fetchBody()).toEqual({ @@ -734,6 +722,7 @@ describe('Authorization', () => { external_id: 'doc-456', name: 'Q4 Budget Report', description: 'Financial report for Q4 2025', + parent_resource_id: 'resource_01HXYZ', }); expect(resource).toMatchObject({ object: 'authorization_resource', @@ -742,7 +731,6 @@ describe('Authorization', () => { name: 'Q4 Budget Report', description: 'Financial report for Q4 2025', resourceTypeSlug: 'document', - parentResourceId: null, createdAt: '2024-01-15T09:30:00.000Z', updatedAt: '2024-01-15T09:30:00.000Z', }); @@ -785,26 +773,20 @@ describe('Authorization', () => { }); }); - it('excludes description and parentResourceId when omitted', async () => { - fetchOnce( - { - ...authorizationResourceFixture, - description: null, - parent_resource_id: null, - }, - { status: 201 }, - ); + it('excludes description when omitted', async () => { + fetchOnce(authorizationResourceFixture, { status: 201 }); await workos.authorization.createResource({ organizationId: testOrgId, resourceTypeSlug: 'document', externalId: 'doc-456', name: 'Q4 Budget Report', + parentResourceId: 'resource_01HXYZ', }); const body = fetchBody(); expect(body).not.toHaveProperty('description'); - expect(body).not.toHaveProperty('parent_resource_id'); + expect(body).toHaveProperty('parent_resource_id', 'resource_01HXYZ'); }); it('sends null when description is explicitly set to null', async () => { @@ -822,33 +804,12 @@ describe('Authorization', () => { externalId: 'doc-456', name: 'Q4 Budget Report', description: null, + parentResourceId: 'resource_01HXYZ', }); const body = fetchBody(); expect(body).toHaveProperty('description', null); - expect(body).not.toHaveProperty('parent_resource_id'); - }); - - it('sends null when parentResourceId is explicitly set to null', async () => { - fetchOnce( - { - ...authorizationResourceFixture, - parent_resource_id: null, - }, - { status: 201 }, - ); - - await workos.authorization.createResource({ - organizationId: testOrgId, - resourceTypeSlug: 'document', - externalId: 'doc-456', - name: 'Q4 Budget Report', - parentResourceId: null, - }); - - const body = fetchBody(); - expect(body).toHaveProperty('parent_resource_id', null); - expect(body).not.toHaveProperty('description'); + expect(body).toHaveProperty('parent_resource_id', 'resource_01HXYZ'); }); it('creates a resource with parentResourceExternalId and parentResourceTypeSlug', async () => { @@ -880,26 +841,40 @@ describe('Authorization', () => { }); }); - it('excludes parentResourceExternalId and parentResourceTypeSlug when omitted', async () => { - fetchOnce( - { - ...authorizationResourceFixture, - parent_resource_id: null, - }, - { status: 201 }, - ); + it('excludes parentResourceExternalId and parentResourceTypeSlug when parentResourceId is used', async () => { + fetchOnce(authorizationResourceFixture, { status: 201 }); await workos.authorization.createResource({ organizationId: testOrgId, resourceTypeSlug: 'document', externalId: 'doc-456', name: 'Q4 Budget Report', + parentResourceId: 'resource_01HXYZ', }); const body = fetchBody(); + expect(body).toHaveProperty('parent_resource_id', 'resource_01HXYZ'); expect(body).not.toHaveProperty('parent_resource_external_id'); expect(body).not.toHaveProperty('parent_resource_type_slug'); }); + + it('excludes parentResourceId when parentResourceExternalId is used', async () => { + fetchOnce(authorizationResourceFixture, { status: 201 }); + + await workos.authorization.createResource({ + organizationId: testOrgId, + resourceTypeSlug: 'document', + externalId: 'doc-456', + name: 'Q4 Budget Report', + parentResourceExternalId: 'folder-123', + parentResourceTypeSlug: 'folder', + }); + + const body = fetchBody(); + expect(body).toHaveProperty('parent_resource_external_id', 'folder-123'); + expect(body).toHaveProperty('parent_resource_type_slug', 'folder'); + expect(body).not.toHaveProperty('parent_resource_id'); + }); }); describe('updateResource', () => { diff --git a/src/authorization/interfaces/authorization-resource.interface.ts b/src/authorization/interfaces/authorization-resource.interface.ts index 57ab6d717..cc75a5c80 100644 --- a/src/authorization/interfaces/authorization-resource.interface.ts +++ b/src/authorization/interfaces/authorization-resource.interface.ts @@ -24,17 +24,27 @@ export interface AuthorizationResourceResponse { updated_at: string; } -export interface CreateAuthorizationResourceOptions { +interface BaseCreateAuthorizationResourceOptions { externalId: string; name: string; description?: string | null; resourceTypeSlug: string; organizationId: string; - parentResourceId?: string | null; - parentResourceExternalId?: string | null; - parentResourceTypeSlug?: string | null; } +export interface CreateOptionsWithParentResourceId extends BaseCreateAuthorizationResourceOptions { + parentResourceId: string; +} + +export interface CreateOptionsWithParentExternalId extends BaseCreateAuthorizationResourceOptions { + parentResourceExternalId: string; + parentResourceTypeSlug: string; +} + +export type CreateAuthorizationResourceOptions = + | CreateOptionsWithParentResourceId + | CreateOptionsWithParentExternalId; + export interface SerializedCreateAuthorizationResourceOptions { external_id: string; name: string; diff --git a/src/authorization/serializers/create-authorization-resource-options.serializer.ts b/src/authorization/serializers/create-authorization-resource-options.serializer.ts index e517fe690..c667f2c4f 100644 --- a/src/authorization/serializers/create-authorization-resource-options.serializer.ts +++ b/src/authorization/serializers/create-authorization-resource-options.serializer.ts @@ -13,13 +13,11 @@ export const serializeCreateResourceOptions = ( ...(options.description !== undefined && { description: options.description, }), - ...(options.parentResourceId !== undefined && { + ...('parentResourceId' in options && { parent_resource_id: options.parentResourceId, }), - ...(options.parentResourceExternalId !== undefined && { + ...('parentResourceExternalId' in options && { parent_resource_external_id: options.parentResourceExternalId, - }), - ...(options.parentResourceTypeSlug !== undefined && { parent_resource_type_slug: options.parentResourceTypeSlug, }), });