Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

135 changes: 135 additions & 0 deletions src/audit-logs/audit-logs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fetch from 'jest-fetch-mock';
import { UnauthorizedException } from '../common/exceptions';
import { BadRequestException } from '../common/exceptions/bad-request.exception';
import { ListResponse } from '../common/interfaces';
import { mockWorkOsResponse } from '../common/utils/workos-mock-response';
import { WorkOS } from '../workos';
import {
Expand All @@ -11,6 +12,7 @@ import {
CreateAuditLogEventOptions,
CreateAuditLogSchemaOptions,
CreateAuditLogSchemaResponse,
ListAuditLogSchemaItemResponse,
} from './interfaces';
import {
serializeAuditLogExportOptions,
Expand Down Expand Up @@ -844,4 +846,137 @@ describe('AuditLogs', () => {
});
});
});

describe('listSchemas', () => {
describe('when the api responds with a 200', () => {
it('returns a paginated list of schemas', async () => {
const workosSpy = jest.spyOn(WorkOS.prototype, 'get');

const time = new Date().toISOString();

const schemaResponse: ListAuditLogSchemaItemResponse = {
object: 'audit_log_schema',
version: 1,
targets: [
{
type: 'user',
metadata: {
type: 'object',
properties: {
user_id: { type: 'string' },
},
},
Comment thread
swaroopAkkineniWorkos marked this conversation as resolved.
},
],
actor: {
metadata: {
type: 'object',
properties: {
actor_id: { type: 'string' },
},
},
},
metadata: {
type: 'object',
properties: {
foo: { type: 'number' },
},
},
created_at: time,
};

const listResponse: ListResponse<ListAuditLogSchemaItemResponse> = {
object: 'list',
data: [schemaResponse],
list_metadata: {
before: undefined,
after: undefined,
},
};

workosSpy.mockResolvedValueOnce(mockWorkOsResponse(200, listResponse));

const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');

const result = await workos.auditLogs.listSchemas({
action: 'user.logged_in',
});

expect(result.data).toHaveLength(1);
expect(result.data[0]).toEqual({
object: 'audit_log_schema',
version: 1,
targets: [
{
type: 'user',
metadata: {
user_id: 'string',
},
},
],
actor: {
metadata: {
actor_id: 'string',
},
},
metadata: {
foo: 'number',
},
createdAt: time,
});

expect(workosSpy).toHaveBeenCalledWith(
'/audit_logs/actions/user.logged_in/schemas',
{ query: { order: 'desc' } },
);
});
});

describe('with pagination options', () => {
it('passes pagination parameters to the API', async () => {
const workosSpy = jest.spyOn(WorkOS.prototype, 'get');

const listResponse: ListResponse<ListAuditLogSchemaItemResponse> = {
object: 'list',
data: [],
list_metadata: {
before: undefined,
after: undefined,
},
};

workosSpy.mockResolvedValueOnce(mockWorkOsResponse(200, listResponse));

const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');

await workos.auditLogs.listSchemas({
action: 'user.logged_in',
limit: 10,
after: 'cursor_123',
order: 'asc',
});

expect(workosSpy).toHaveBeenCalledWith(
'/audit_logs/actions/user.logged_in/schemas',
{ query: { limit: 10, after: 'cursor_123', order: 'asc' } },
);
});
});

describe('when the api responds with a 401', () => {
it('throws an UnauthorizedException', async () => {
const workosSpy = jest.spyOn(WorkOS.prototype, 'get');

workosSpy.mockImplementationOnce(() => {
throw new UnauthorizedException('a-request-id');
});

const workos = new WorkOS('invalid apikey');

await expect(
workos.auditLogs.listSchemas({ action: 'user.logged_in' }),
).rejects.toThrow(UnauthorizedException);
});
});
});
});
42 changes: 41 additions & 1 deletion src/audit-logs/audit-logs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { PaginationOptions } from '../common/interfaces';
import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize';
import { AutoPaginatable } from '../common/utils/pagination';
import { WorkOS } from '../workos';
import {
CreateAuditLogEventOptions,
CreateAuditLogEventRequestOptions,
ListSchemasOptions,
} from './interfaces';
import { AuditLogExportOptions } from './interfaces/audit-log-export-options.interface';
import {
Expand All @@ -14,12 +18,17 @@ import {
CreateAuditLogSchemaRequestOptions,
CreateAuditLogSchemaResponse,
} from './interfaces/create-audit-log-schema-options.interface';
import {
ListAuditLogSchemaItemResponse,
ListedAuditLogSchema,
} from './interfaces/list-audit-log-schemas.interface';
import {
deserializeAuditLogExport,
deserializeAuditLogSchema,
deserializeListedAuditLogSchema,
serializeAuditLogExportOptions,
serializeCreateAuditLogEventOptions,
serializeCreateAuditLogSchemaOptions,
deserializeAuditLogSchema,
} from './serializers';

export class AuditLogs {
Expand Down Expand Up @@ -77,4 +86,35 @@ export class AuditLogs {

return deserializeAuditLogSchema(data);
}

/**
* Lists all schemas for a specific audit log action.
*
* @param options - Options including the action identifier and pagination parameters
* @returns A paginated list of audit log schemas
*/
async listSchemas(
options: ListSchemasOptions,
): Promise<AutoPaginatable<ListedAuditLogSchema, ListSchemasOptions>> {
const { action, ...paginationOptions } = options;
const endpoint = `/audit_logs/actions/${action}/schemas`;
Comment on lines +87 to +91
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] listSchemas should accept optional options like other list methods

listSchemas(options: ListSchemasOptions) forces callers to pass an object, even ListSchemasOptions is mostly pagination + required action. Most other list-style SDK methods take options?: ... and allow a simpler call-site / future optional expansion. Consider listSchemas(action: string, options?: PaginationOptions) or listSchemas(options: ListSchemasOptions) but make options optional with a runtime check.

(If this is intentional API design, please ignore—just flagging because it differs from the rest of the SDK pattern.)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/audit-logs/audit-logs.ts
Line: 88:92

Comment:
[P2] `listSchemas` should accept optional options like other list methods

`listSchemas(options: ListSchemasOptions)` forces callers to pass an object, even `ListSchemasOptions` is mostly pagination + required `action`. Most other list-style SDK methods take `options?: ...` and allow a simpler call-site / future optional expansion. Consider `listSchemas(action: string, options?: PaginationOptions)` or `listSchemas(options: ListSchemasOptions)` but make `options` optional with a runtime check.

(If this is intentional API design, please ignore—just flagging because it differs from the rest of the SDK pattern.)

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@swaroopAkkineniWorkos This isn't a bad suggestion, since it might be a bit easier for devs to use listSchemas(action) if they're using the auto-pagination.


return new AutoPaginatable(
await fetchAndDeserialize<
ListAuditLogSchemaItemResponse,
ListedAuditLogSchema
>(
this.workos,
endpoint,
deserializeListedAuditLogSchema,
paginationOptions,
),
(params: PaginationOptions) =>
fetchAndDeserialize<
ListAuditLogSchemaItemResponse,
ListedAuditLogSchema
>(this.workos, endpoint, deserializeListedAuditLogSchema, params),
options,
);
}
}
2 changes: 2 additions & 0 deletions src/audit-logs/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from './audit-log-export-options.interface';
export * from './audit-log-export.interface';
export * from './create-audit-log-event-options.interface';
export * from './create-audit-log-schema-options.interface';
export * from './list-audit-log-schemas.interface';
export * from './list-schemas-options.interface';
55 changes: 55 additions & 0 deletions src/audit-logs/interfaces/list-audit-log-schemas.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { AuditLogSchemaMetadata } from './create-audit-log-schema-options.interface';

/**
* Target schema in the raw API response format.
*/
interface ListAuditLogSchemaTargetResponse {
type: string;
metadata?: {
type: 'object';
properties: AuditLogSchemaMetadata;
};
}

/**
* Actor schema in the raw API response format.
*/
interface ListAuditLogSchemaActorResponse {
metadata: {
type: 'object';
properties: AuditLogSchemaMetadata;
};
}

/**
* API response for a single schema item in the list schemas endpoint.
*/
export interface ListAuditLogSchemaItemResponse {
object: 'audit_log_schema';
version: number;
targets: ListAuditLogSchemaTargetResponse[];
actor?: ListAuditLogSchemaActorResponse;
metadata?: {
type: 'object';
properties: AuditLogSchemaMetadata;
};
created_at: string;
}

/**
* Deserialized audit log schema from the list schemas endpoint.
* Uses the same deserialized format as AuditLogSchema for consistency.
*/
export interface ListedAuditLogSchema {
object: 'audit_log_schema';
version: number;
targets: Array<{
type: string;
metadata?: Record<string, string | boolean | number>;
}>;
actor?: {
metadata: Record<string, string | boolean | number>;
};
metadata?: Record<string, string | boolean | number>;
createdAt: string;
}
9 changes: 9 additions & 0 deletions src/audit-logs/interfaces/list-schemas-options.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { PaginationOptions } from '../../common/interfaces';

/**
* Options for listing audit log schemas for a specific action.
*/
export interface ListSchemasOptions extends PaginationOptions {
/** The action identifier (e.g., 'user.logged_in') */
action: string;
}
1 change: 1 addition & 0 deletions src/audit-logs/serializers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './audit-log-export-options.serializer';
export * from './create-audit-log-event-options.serializer';
export * from './create-audit-log-schema-options.serializer';
export * from './create-audit-log-schema.serializer';
export * from './list-audit-log-schema.serializer';
Loading