@shipfox/api-server 7.1.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@shipfox/api-server",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "7.1.0",
5
+ "version": "8.0.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -25,37 +25,37 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@shipfox/annotations": "7.1.0",
29
- "@shipfox/api-agent": "7.1.0",
28
+ "@shipfox/annotations": "8.0.0",
30
29
  "@shipfox/annotations-dto": "6.0.0",
31
- "@shipfox/api-agent-dto": "6.0.0",
30
+ "@shipfox/api-agent": "8.0.0",
31
+ "@shipfox/api-agent-dto": "8.0.0",
32
32
  "@shipfox/api-auth": "7.1.0",
33
33
  "@shipfox/api-auth-dto": "7.1.0",
34
+ "@shipfox/api-definitions": "8.0.0",
34
35
  "@shipfox/api-definitions-dto": "6.0.0",
35
36
  "@shipfox/api-dispatcher": "7.1.0",
36
37
  "@shipfox/api-email-challenges": "0.3.0",
37
- "@shipfox/api-definitions": "7.1.0",
38
- "@shipfox/api-integration-core": "7.1.0",
39
- "@shipfox/api-projects": "7.1.0",
40
- "@shipfox/api-logs": "7.1.0",
41
- "@shipfox/api-projects-dto": "6.0.0",
42
- "@shipfox/api-runners": "7.1.0",
38
+ "@shipfox/api-integration-core": "8.0.0",
39
+ "@shipfox/api-integration-core-dto": "8.0.0",
40
+ "@shipfox/api-logs": "8.0.0",
41
+ "@shipfox/api-projects": "8.0.0",
42
+ "@shipfox/api-projects-dto": "8.0.0",
43
+ "@shipfox/api-runners": "8.0.0",
43
44
  "@shipfox/api-runners-dto": "7.0.1",
44
- "@shipfox/api-secrets": "7.1.0",
45
- "@shipfox/api-integration-core-dto": "6.0.0",
46
- "@shipfox/api-workflows": "7.1.0",
45
+ "@shipfox/api-secrets": "8.0.0",
47
46
  "@shipfox/api-secrets-dto": "6.0.0",
48
- "@shipfox/api-triggers": "7.1.0",
47
+ "@shipfox/api-triggers": "8.0.0",
48
+ "@shipfox/api-workflows": "8.0.0",
49
+ "@shipfox/api-workflows-dto": "8.0.0",
49
50
  "@shipfox/api-workspaces-dto": "6.0.0",
50
- "@shipfox/api-workflows-dto": "6.0.0",
51
51
  "@shipfox/api-workspaces": "7.1.0",
52
52
  "@shipfox/config": "1.2.2",
53
- "@shipfox/node-fastify": "0.3.0",
54
53
  "@shipfox/node-error-monitoring": "0.2.0",
54
+ "@shipfox/node-fastify": "0.3.0",
55
55
  "@shipfox/node-jwt": "0.3.0",
56
- "@shipfox/node-postgres": "0.4.2",
57
56
  "@shipfox/node-module": "0.5.0",
58
- "@shipfox/node-opentelemetry": "0.6.0"
57
+ "@shipfox/node-opentelemetry": "0.6.0",
58
+ "@shipfox/node-postgres": "0.4.2"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "shipfox-swc",
@@ -0,0 +1,52 @@
1
+ import {AUTH_LEASED_JOB, requireLeasedJobContext} from '@shipfox/api-auth-context';
2
+ import {closeApp, createApp, defineRoute} from '@shipfox/node-fastify';
3
+ import {afterEach, describe, expect, it} from '@shipfox/vitest/vi';
4
+
5
+ process.env.AUTH_ROOT_KEY ??= 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=';
6
+
7
+ const leaseRoute = defineRoute({
8
+ method: 'GET',
9
+ path: '/lease',
10
+ description: 'Auth composition test route.',
11
+ handler: (request) => {
12
+ const lease = requireLeasedJobContext(request);
13
+ return {job_id: lease.jobId, step_id: lease.currentStepId};
14
+ },
15
+ });
16
+
17
+ describe('Auth token interoperability', () => {
18
+ afterEach(async () => {
19
+ await closeApp();
20
+ });
21
+
22
+ it('accepts an Auth-issued step lease through the server route boundary', async () => {
23
+ const {createLeaseTokenAuthMethod, issueJobLeaseToken} = await import('@shipfox/api-auth');
24
+ const jobId = crypto.randomUUID();
25
+ const stepId = crypto.randomUUID();
26
+ const token = await issueJobLeaseToken({
27
+ jobId,
28
+ jobExecutionId: crypto.randomUUID(),
29
+ workflowRunId: crypto.randomUUID(),
30
+ workflowRunAttemptId: crypto.randomUUID(),
31
+ projectId: crypto.randomUUID(),
32
+ workspaceId: crypto.randomUUID(),
33
+ runnerSessionId: crypto.randomUUID(),
34
+ currentStepId: stepId,
35
+ currentStepAttempt: 1,
36
+ });
37
+ const app = await createApp({
38
+ auth: [createLeaseTokenAuthMethod()],
39
+ routes: [{prefix: '/runs/jobs/current', auth: AUTH_LEASED_JOB, routes: [leaseRoute]}],
40
+ swagger: false,
41
+ });
42
+
43
+ const res = await app.inject({
44
+ method: 'GET',
45
+ url: '/runs/jobs/current/lease',
46
+ headers: {authorization: `Bearer ${token}`},
47
+ });
48
+
49
+ expect(res.statusCode).toBe(200);
50
+ expect(res.json()).toEqual({job_id: jobId, step_id: stepId});
51
+ });
52
+ });
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export {
2
+ type DefaultModulesExtension,
2
3
  type DefaultModulesOptions,
3
4
  type DefaultRunnersModuleFactory,
4
5
  defaultModules,
@@ -2,15 +2,18 @@ import {annotationsInterModuleContract} from '@shipfox/annotations-dto/inter-mod
2
2
  import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
3
3
  import {authInterModuleContract} from '@shipfox/api-auth-dto/inter-module';
4
4
  import {definitionsInterModuleContract} from '@shipfox/api-definitions-dto/inter-module';
5
- import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
6
- import {projectsInterModuleContract} from '@shipfox/api-projects-dto';
5
+ import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto/inter-module';
6
+ import {projectsInterModuleContract} from '@shipfox/api-projects-dto/inter-module';
7
7
  import {runnersInterModuleContract} from '@shipfox/api-runners-dto/inter-module';
8
8
  import {
9
9
  type SecretsInterModuleClient,
10
10
  secretsInterModuleContract,
11
11
  } from '@shipfox/api-secrets-dto/inter-module';
12
12
  import {workflowsInterModuleContract} from '@shipfox/api-workflows-dto/inter-module';
13
- import {workspacesInterModuleContract} from '@shipfox/api-workspaces-dto/inter-module';
13
+ import {
14
+ type WorkspacesInterModuleClient,
15
+ workspacesInterModuleContract,
16
+ } from '@shipfox/api-workspaces-dto/inter-module';
14
17
  import {defineInterModulePresentation} from '@shipfox/inter-module';
15
18
  import {defaultModules} from './modules.js';
16
19
 
@@ -30,6 +33,7 @@ const mocks = vi.hoisted(() => ({
30
33
  deleteSecrets: vi.fn(),
31
34
  getIntegrationConnectionById: vi.fn(),
32
35
  getSecret: vi.fn(),
36
+ listMembershipsForTokenClaims: vi.fn(),
33
37
  setSecrets: vi.fn(),
34
38
  }));
35
39
 
@@ -96,7 +100,7 @@ vi.mock('@shipfox/api-workspaces', () => ({
96
100
  {
97
101
  contract: workspacesInterModuleContract,
98
102
  handlers: {
99
- listMembershipsForTokenClaims: vi.fn(),
103
+ listMembershipsForTokenClaims: mocks.listMembershipsForTokenClaims,
100
104
  preflightInvitationAcceptance: vi.fn(),
101
105
  acceptInvitation: vi.fn(),
102
106
  requireActiveMembership: vi.fn(),
@@ -123,6 +127,7 @@ describe('defaultModules', () => {
123
127
  mocks.deleteSecrets.mockReset();
124
128
  mocks.getIntegrationConnectionById.mockReset();
125
129
  mocks.getSecret.mockReset();
130
+ mocks.listMembershipsForTokenClaims.mockReset();
126
131
  mocks.setSecrets.mockReset();
127
132
 
128
133
  mocks.createIntegrationsContext.mockResolvedValue({
@@ -147,6 +152,7 @@ describe('defaultModules', () => {
147
152
  mocks.createWorkspaceConnectionSnapshotLoader.mockReturnValue(vi.fn());
148
153
  mocks.deleteSecrets.mockResolvedValue({deleted: 1});
149
154
  mocks.getSecret.mockResolvedValue({value: 'secret'});
155
+ mocks.listMembershipsForTokenClaims.mockResolvedValue({memberships: []});
150
156
  mocks.setSecrets.mockResolvedValue({});
151
157
  mocks.createProjectsModule.mockReturnValue({
152
158
  name: 'projects',
@@ -182,7 +188,11 @@ describe('defaultModules', () => {
182
188
  interModulePresentations: [
183
189
  {
184
190
  contract: agentInterModuleContract,
185
- handlers: {resolveAgentConfig: vi.fn(), resolveRuntimeCredentials: vi.fn()},
191
+ handlers: {
192
+ getValidationCatalog: vi.fn(),
193
+ resolveAgentConfig: vi.fn(),
194
+ resolveRuntimeCredentials: vi.fn(),
195
+ },
186
196
  },
187
197
  ],
188
198
  });
@@ -288,6 +298,27 @@ describe('defaultModules', () => {
288
298
  ]);
289
299
  });
290
300
 
301
+ it('extends the default module list with the composed Workspaces client', async () => {
302
+ let workspaces: WorkspacesInterModuleClient | undefined;
303
+ const extensionModule = {name: 'cloud'};
304
+ const extension = vi.fn((options: {workspaces: WorkspacesInterModuleClient}) => {
305
+ workspaces = options.workspaces;
306
+ return [extensionModule];
307
+ });
308
+
309
+ const modules = await defaultModules({extension});
310
+ const userId = crypto.randomUUID();
311
+ const memberships = await workspaces?.listMembershipsForTokenClaims({userId});
312
+
313
+ expect(extension).toHaveBeenCalledWith({workspaces: expect.any(Object)});
314
+ expect(memberships).toEqual({memberships: []});
315
+ expect(mocks.listMembershipsForTokenClaims).toHaveBeenCalledWith(
316
+ {userId},
317
+ expect.objectContaining({signal: expect.any(AbortSignal)}),
318
+ );
319
+ expect(modules.at(-1)).toBe(extensionModule);
320
+ });
321
+
291
322
  it('injects Workflows into integrations and logs and namespaces provider secrets', async () => {
292
323
  await defaultModules();
293
324
 
package/src/modules.ts CHANGED
@@ -13,10 +13,10 @@ import {definitionsInterModuleContract} from '@shipfox/api-definitions-dto/inter
13
13
  import {dispatcherModule} from '@shipfox/api-dispatcher';
14
14
  import {emailChallengesModule} from '@shipfox/api-email-challenges';
15
15
  import {createIntegrationsContext, type WebhookDeliverySource} from '@shipfox/api-integration-core';
16
- import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
16
+ import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto/inter-module';
17
17
  import {createLogsModule} from '@shipfox/api-logs';
18
18
  import {createProjectsModule} from '@shipfox/api-projects';
19
- import {projectsInterModuleContract} from '@shipfox/api-projects-dto';
19
+ import {projectsInterModuleContract} from '@shipfox/api-projects-dto/inter-module';
20
20
  import {createRunnersModule} from '@shipfox/api-runners';
21
21
  import {runnersInterModuleContract} from '@shipfox/api-runners-dto/inter-module';
22
22
  import {createSecretsModule} from '@shipfox/api-secrets';
@@ -25,7 +25,10 @@ import {createTriggersModule} from '@shipfox/api-triggers';
25
25
  import {createWorkflowsModule} from '@shipfox/api-workflows';
26
26
  import {workflowsInterModuleContract} from '@shipfox/api-workflows-dto/inter-module';
27
27
  import {workspacesModule} from '@shipfox/api-workspaces';
28
- import {workspacesInterModuleContract} from '@shipfox/api-workspaces-dto/inter-module';
28
+ import {
29
+ type WorkspacesInterModuleClient,
30
+ workspacesInterModuleContract,
31
+ } from '@shipfox/api-workspaces-dto/inter-module';
29
32
  import {reportError} from '@shipfox/node-error-monitoring';
30
33
  import {durationToSeconds} from '@shipfox/node-jwt';
31
34
  import type {ShipfoxModule} from '@shipfox/node-module';
@@ -38,9 +41,13 @@ import {logger} from '@shipfox/node-opentelemetry';
38
41
  export interface DefaultModulesOptions {
39
42
  webhookDeliverySource?: WebhookDeliverySource | undefined;
40
43
  runnersModule?: DefaultRunnersModuleFactory | undefined;
44
+ extension?: DefaultModulesExtension | undefined;
41
45
  }
42
46
 
43
47
  export type DefaultRunnersModuleFactory = (options: {auth: AuthInterModuleClient}) => ShipfoxModule;
48
+ export type DefaultModulesExtension = (options: {
49
+ workspaces: WorkspacesInterModuleClient;
50
+ }) => ShipfoxModule[];
44
51
 
45
52
  export async function defaultModules(
46
53
  options: DefaultModulesOptions = {},
@@ -151,8 +158,10 @@ export async function defaultModules(
151
158
  const projectsModule = createProjectsModule({integrations: integrationsClient});
152
159
  const definitionsModule = createDefinitionsModule({
153
160
  projects: projectsClient,
161
+ agent: agentClient,
154
162
  integrations: integrationsClient,
155
163
  });
164
+ const extensionModules = options.extension?.({workspaces: workspacesClient}) ?? [];
156
165
 
157
166
  const modules = [
158
167
  emailChallengesModule,
@@ -181,6 +190,7 @@ export async function defaultModules(
181
190
  }),
182
191
  createTriggersModule({workflows: workflowsClient}),
183
192
  dispatcherModule,
193
+ ...extensionModules,
184
194
  ];
185
195
  registerInterModulePresentations({transport: interModuleTransport, modules});
186
196
  interModuleTransport.seal();