diff --git a/packages/cli/e2e/specs/2-Application Discovery.e2e.test.ts b/packages/cli/e2e/specs/2-Application Discovery.e2e.test.ts index 729450f..2f797e9 100644 --- a/packages/cli/e2e/specs/2-Application Discovery.e2e.test.ts +++ b/packages/cli/e2e/specs/2-Application Discovery.e2e.test.ts @@ -55,7 +55,7 @@ describe('SWR Application Details', () => { // Assert test-app properties expect(testApp).toMatchObject({ application_id: 'test-app', - name: 'test-app', + name: 'Test Application', regulatory_classes: expect.arrayContaining([expect.any(String)]), description: expect.any(String), }); diff --git a/packages/cli/src/cli-functions.spec.ts b/packages/cli/src/cli-functions.spec.ts index 70110c6..2e3bd8f 100644 --- a/packages/cli/src/cli-functions.spec.ts +++ b/packages/cli/src/cli-functions.spec.ts @@ -302,6 +302,154 @@ describe('CLI Functions Unit Tests', () => { ); }); + it('should list application runs with custom metadata filter', async () => { + const runsResponse = [ + { + application_run_id: 'run-1', + application_version_id: 'v1.0.0', + organization_id: 'org-1', + status: 'COMPLETED', + created_at: '2023-01-01T00:00:00Z', + updated_at: '2023-01-01T01:00:00Z', + }, + ]; + platformSDKMock.listApplicationRuns.mockResolvedValue(runsResponse); + + await listApplicationRuns('production', mockAuthService, { + applicationId: 'app1', + customMetadata: '{"key": "value"}', + }); + + expect(platformSDKMock.listApplicationRuns).toHaveBeenCalledWith({ + applicationId: 'app1', + customMetadata: '{"key": "value"}', + }); + expect(consoleSpy.log).toHaveBeenCalledWith( + 'Application runs:', + JSON.stringify(runsResponse, null, 2) + ); + }); + + it('should list application runs with sort parameter', async () => { + const runsResponse = [ + { + application_run_id: 'run-1', + application_version_id: 'v1.0.0', + organization_id: 'org-1', + status: 'COMPLETED', + created_at: '2023-01-01T00:00:00Z', + updated_at: '2023-01-01T01:00:00Z', + }, + ]; + platformSDKMock.listApplicationRuns.mockResolvedValue(runsResponse); + + await listApplicationRuns('production', mockAuthService, { + sort: '["created_at", "-updated_at"]', + }); + + expect(platformSDKMock.listApplicationRuns).toHaveBeenCalledWith({ + sort: ['created_at', '-updated_at'], + }); + expect(consoleSpy.log).toHaveBeenCalledWith( + 'Application runs:', + JSON.stringify(runsResponse, null, 2) + ); + }); + + it('should list application runs with all filters and sort', async () => { + const runsResponse = [ + { + application_run_id: 'run-1', + application_version_id: 'v1.0.0', + organization_id: 'org-1', + status: 'COMPLETED', + created_at: '2023-01-01T00:00:00Z', + updated_at: '2023-01-01T01:00:00Z', + }, + ]; + platformSDKMock.listApplicationRuns.mockResolvedValue(runsResponse); + + await listApplicationRuns('production', mockAuthService, { + applicationId: 'app1', + applicationVersion: 'v1.0.0', + customMetadata: '{"environment": "test"}', + sort: '["-created_at"]', + }); + + expect(platformSDKMock.listApplicationRuns).toHaveBeenCalledWith({ + applicationId: 'app1', + applicationVersion: 'v1.0.0', + customMetadata: '{"environment": "test"}', + sort: ['-created_at'], + }); + expect(consoleSpy.log).toHaveBeenCalledWith( + 'Application runs:', + JSON.stringify(runsResponse, null, 2) + ); + }); + + it('should handle invalid sort JSON', async () => { + await listApplicationRuns('production', mockAuthService, { + sort: 'invalid-json', + }); + + expect(consoleSpy.error).toHaveBeenCalledWith('❌ Invalid sort array:', expect.any(Error)); + expect(mockExit).toHaveBeenCalledWith(1); + expect(platformSDKMock.listApplicationRuns).not.toHaveBeenCalled(); + }); + + it('should handle empty customMetadata', async () => { + const runsResponse = [ + { + application_run_id: 'run-1', + application_version_id: 'v1.0.0', + organization_id: 'org-1', + status: 'COMPLETED', + created_at: '2023-01-01T00:00:00Z', + updated_at: '2023-01-01T01:00:00Z', + }, + ]; + platformSDKMock.listApplicationRuns.mockResolvedValue(runsResponse); + + await listApplicationRuns('production', mockAuthService, { + customMetadata: '{}', + }); + + expect(platformSDKMock.listApplicationRuns).toHaveBeenCalledWith({ + customMetadata: '{}', + }); + expect(consoleSpy.log).toHaveBeenCalledWith( + 'Application runs:', + JSON.stringify(runsResponse, null, 2) + ); + }); + + it('should handle empty sort array', async () => { + const runsResponse = [ + { + application_run_id: 'run-1', + application_version_id: 'v1.0.0', + organization_id: 'org-1', + status: 'COMPLETED', + created_at: '2023-01-01T00:00:00Z', + updated_at: '2023-01-01T01:00:00Z', + }, + ]; + platformSDKMock.listApplicationRuns.mockResolvedValue(runsResponse); + + await listApplicationRuns('production', mockAuthService, { + sort: '[]', + }); + + expect(platformSDKMock.listApplicationRuns).toHaveBeenCalledWith({ + sort: [], + }); + expect(consoleSpy.log).toHaveBeenCalledWith( + 'Application runs:', + JSON.stringify(runsResponse, null, 2) + ); + }); + it('should handle API error', async () => { platformSDKMock.listApplicationRuns.mockRejectedValue(new Error('API error')); @@ -313,6 +461,22 @@ describe('CLI Functions Unit Tests', () => { ); expect(mockExit).toHaveBeenCalledWith(1); }); + + it('should handle API error with filters', async () => { + platformSDKMock.listApplicationRuns.mockRejectedValue(new Error('Network error')); + + await listApplicationRuns('production', mockAuthService, { + applicationId: 'app1', + customMetadata: '{"key": "value"}', + sort: '["-created_at"]', + }); + + expect(consoleSpy.error).toHaveBeenCalledWith( + '❌ Failed to list application runs:', + expect.any(Error) + ); + expect(mockExit).toHaveBeenCalledWith(1); + }); }); describe('getRun', () => { diff --git a/packages/cli/src/cli-functions.ts b/packages/cli/src/cli-functions.ts index 061d7f3..6914295 100644 --- a/packages/cli/src/cli-functions.ts +++ b/packages/cli/src/cli-functions.ts @@ -98,15 +98,33 @@ export async function listApplicationVersions( export async function listApplicationRuns( environment: EnvironmentKey, authService: AuthService, - options?: { applicationId?: string; applicationVersion?: string } + options?: { + applicationId?: string; + applicationVersion?: string; + customMetadata?: string; + sort?: string; + } ): Promise { + let sortBy: string[] | undefined; + + if (options?.sort) { + try { + const sortParsed = JSON.parse(options.sort) as string[]; + sortBy = sortParsed; + } catch (parseError) { + console.error('❌ Invalid sort array:', parseError); + process.exit(1); + return; // Ensure we don't continue execution in tests + } + } + const { endpoint } = environmentConfig[environment]; const sdk = new PlatformSDKHttp({ baseURL: endpoint, tokenProvider: () => authService.getValidAccessToken(environment), }); try { - const response = await sdk.listApplicationRuns(options); + const response = await sdk.listApplicationRuns({ ...options, sort: sortBy }); console.log('Application runs:', JSON.stringify(response, null, 2)); } catch (error) { console.error('❌ Failed to list application runs:', error); diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 9e08f20..076473f 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -100,11 +100,22 @@ export async function main() { .option('applicationVersion', { describe: 'Filter by application version', type: 'string', + }) + .option('customMetadata', { + describe: 'Filter by metadata key-value pairs (JSONPath string)', + type: 'string', + }) + .option('sort', { + describe: + 'Sort by field (e.g., "run_id", "-status", "submitted_at"). Fields: run_id, application_version_id, organization_id, status, submitted_at, submitted_by.', + type: 'string', }), argv => listApplicationRuns(argv.environment as EnvironmentKey, authService, { applicationId: argv.applicationId, applicationVersion: argv.applicationVersion, + customMetadata: argv.customMetadata, + sort: argv.sort, }) ) .command( diff --git a/packages/sdk/src/platform-sdk.ts b/packages/sdk/src/platform-sdk.ts index 5cf4238..eaf5bda 100644 --- a/packages/sdk/src/platform-sdk.ts +++ b/packages/sdk/src/platform-sdk.ts @@ -337,12 +337,16 @@ export class PlatformSDKHttp implements PlatformSDK { async listApplicationRuns(options?: { applicationId?: string; applicationVersion?: string; + customMetadata?: string; + sort?: string[]; }): Promise { const client = await this.#getClient(); try { const response = await client.listRunsV1RunsGet({ applicationId: options?.applicationId, applicationVersion: options?.applicationVersion, + customMetadata: options?.customMetadata, + sort: options?.sort, }); return response.data;