@shipfox/api-server 9.2.0 → 9.3.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": "9.2.0",
5
+ "version": "9.3.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -22,36 +22,36 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@shipfox/annotations": "9.2.0",
26
25
  "@shipfox/annotations-dto": "9.0.2",
27
- "@shipfox/api-agent": "9.2.0",
26
+ "@shipfox/annotations": "9.3.0",
28
27
  "@shipfox/api-agent-dto": "9.0.2",
29
- "@shipfox/api-auth": "9.2.0",
30
- "@shipfox/api-auth-dto": "9.2.0",
31
- "@shipfox/api-definitions": "9.2.0",
28
+ "@shipfox/api-agent": "9.3.0",
29
+ "@shipfox/api-auth": "9.3.0",
30
+ "@shipfox/api-auth-dto": "9.3.0",
31
+ "@shipfox/api-definitions": "9.3.0",
32
+ "@shipfox/api-dispatcher": "9.3.0",
32
33
  "@shipfox/api-definitions-dto": "9.0.2",
33
- "@shipfox/api-dispatcher": "9.0.3",
34
- "@shipfox/api-email-challenges": "1.1.4",
35
- "@shipfox/api-integration-core": "9.2.0",
34
+ "@shipfox/api-integration-core": "9.3.0",
36
35
  "@shipfox/api-integration-core-dto": "9.0.2",
37
- "@shipfox/api-logs": "9.2.0",
38
- "@shipfox/api-projects": "9.2.0",
36
+ "@shipfox/api-email-challenges": "1.1.5",
37
+ "@shipfox/api-projects": "9.3.0",
38
+ "@shipfox/api-runners": "9.3.0",
39
39
  "@shipfox/api-projects-dto": "9.2.0",
40
- "@shipfox/api-runners": "9.2.0",
41
40
  "@shipfox/api-runners-dto": "9.0.2",
42
- "@shipfox/api-secrets": "9.2.0",
41
+ "@shipfox/api-secrets": "9.3.0",
43
42
  "@shipfox/api-secrets-dto": "9.0.2",
44
- "@shipfox/api-triggers": "9.2.0",
45
- "@shipfox/api-workflows": "9.2.0",
46
- "@shipfox/api-workflows-dto": "9.0.2",
47
- "@shipfox/api-workspaces-dto": "9.2.0",
48
- "@shipfox/api-workspaces": "9.2.0",
49
- "@shipfox/config": "1.2.4",
43
+ "@shipfox/api-logs": "9.3.0",
44
+ "@shipfox/api-triggers": "9.3.0",
45
+ "@shipfox/api-workflows": "9.3.0",
46
+ "@shipfox/api-workflows-dto": "9.3.0",
47
+ "@shipfox/api-workspaces": "9.3.0",
48
+ "@shipfox/api-workspaces-dto": "9.3.0",
50
49
  "@shipfox/node-error-monitoring": "0.3.0",
51
- "@shipfox/node-fastify": "0.3.3",
50
+ "@shipfox/config": "1.2.4",
51
+ "@shipfox/node-fastify": "0.3.4",
52
52
  "@shipfox/node-jwt": "0.3.2",
53
- "@shipfox/node-module": "1.0.2",
54
- "@shipfox/node-opentelemetry": "0.6.2",
53
+ "@shipfox/node-module": "1.0.3",
54
+ "@shipfox/node-opentelemetry": "0.6.3",
55
55
  "@shipfox/node-postgres": "0.4.4"
56
56
  },
57
57
  "imports": {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export {
2
+ type DefaultAuthModuleFactory,
2
3
  type DefaultModulesExtension,
3
4
  type DefaultModulesOptions,
4
5
  type DefaultRunnersModuleFactory,
@@ -8,5 +8,5 @@ const {version} = createRequire(import.meta.url)('../package.json') as {version:
8
8
  await startInstanceInstrumentation({
9
9
  serviceName: 'api',
10
10
  serviceVersion: version,
11
- instrumentations: {fastify: true, http: true, pg: true, awsSdk: true},
11
+ instrumentations: {fastify: true, http: true, pg: true, awsSdk: true, pino: true},
12
12
  });
@@ -21,6 +21,7 @@ const mocks = vi.hoisted(() => ({
21
21
  buildAgentToolCatalogs: vi.fn(),
22
22
  buildAgentToolSelectionCatalogs: vi.fn(),
23
23
  createAgentModule: vi.fn(),
24
+ createAuthModule: vi.fn(),
24
25
  createDefinitionsModule: vi.fn(),
25
26
  createIntegrationsContext: vi.fn(),
26
27
  createLogsModule: vi.fn(),
@@ -34,6 +35,7 @@ const mocks = vi.hoisted(() => ({
34
35
  getIntegrationConnectionById: vi.fn(),
35
36
  getSecret: vi.fn(),
36
37
  getWorkspaceCreator: vi.fn(),
38
+ getWorkspaceOperatingState: vi.fn(),
37
39
  listMembershipsForTokenClaims: vi.fn(),
38
40
  setSecrets: vi.fn(),
39
41
  }));
@@ -50,20 +52,7 @@ vi.mock('@shipfox/annotations', () => ({
50
52
  },
51
53
  }));
52
54
  vi.mock('@shipfox/api-auth', () => ({
53
- createAuthModule: () => ({
54
- name: 'auth',
55
- interModulePresentations: [
56
- {
57
- contract: authInterModuleContract,
58
- handlers: {
59
- mintJobLeaseToken: vi.fn(),
60
- mintRunnerSessionToken: vi.fn(),
61
- getCurrentAdminRole: vi.fn(),
62
- requireAdminRole: vi.fn(),
63
- },
64
- },
65
- ],
66
- }),
55
+ createAuthModule: mocks.createAuthModule,
67
56
  }));
68
57
  vi.mock('@shipfox/api-agent', () => ({createAgentModule: mocks.createAgentModule}));
69
58
  vi.mock('@shipfox/api-auth/config', () => ({
@@ -105,6 +94,7 @@ vi.mock('@shipfox/api-workspaces', () => ({
105
94
  handlers: {
106
95
  listMembershipsForTokenClaims: mocks.listMembershipsForTokenClaims,
107
96
  getWorkspaceCreator: mocks.getWorkspaceCreator,
97
+ getWorkspaceOperatingState: mocks.getWorkspaceOperatingState,
108
98
  preflightInvitationAcceptance: vi.fn(),
109
99
  acceptInvitation: vi.fn(),
110
100
  requireActiveMembership: vi.fn(),
@@ -119,6 +109,7 @@ describe('defaultModules', () => {
119
109
  mocks.buildAgentToolCatalogs.mockReset();
120
110
  mocks.buildAgentToolSelectionCatalogs.mockReset();
121
111
  mocks.createAgentModule.mockReset();
112
+ mocks.createAuthModule.mockReset();
122
113
  mocks.createDefinitionsModule.mockReset();
123
114
  mocks.createIntegrationsContext.mockReset();
124
115
  mocks.createLogsModule.mockReset();
@@ -155,6 +146,20 @@ describe('defaultModules', () => {
155
146
  mocks.buildAgentToolCatalogs.mockResolvedValue(new Map());
156
147
  mocks.buildAgentToolSelectionCatalogs.mockResolvedValue(new Map());
157
148
  mocks.createWorkspaceConnectionSnapshotLoader.mockReturnValue(vi.fn());
149
+ mocks.createAuthModule.mockReturnValue({
150
+ name: 'auth',
151
+ interModulePresentations: [
152
+ {
153
+ contract: authInterModuleContract,
154
+ handlers: {
155
+ mintJobLeaseToken: vi.fn(),
156
+ mintRunnerSessionToken: vi.fn(),
157
+ getCurrentAdminRole: vi.fn(),
158
+ requireAdminRole: vi.fn(),
159
+ },
160
+ },
161
+ ],
162
+ });
158
163
  mocks.deleteSecrets.mockResolvedValue({deleted: 1});
159
164
  mocks.getSecret.mockResolvedValue({value: 'secret'});
160
165
  mocks.listMembershipsForTokenClaims.mockResolvedValue({memberships: []});
@@ -291,6 +296,43 @@ describe('defaultModules', () => {
291
296
  expect(mocks.createRunnersModule).toHaveBeenCalledWith({auth: expect.any(Object)});
292
297
  });
293
298
 
299
+ it('uses the default Auth module factory when none is supplied', async () => {
300
+ await defaultModules();
301
+
302
+ expect(mocks.createAuthModule).toHaveBeenCalledWith({workspaces: expect.any(Object)});
303
+ });
304
+
305
+ it('composes Auth with the shared Workspaces client and registers the supplied module', async () => {
306
+ const signupPolicy = {isSignupAllowed: vi.fn().mockResolvedValue({allowed: true})};
307
+ const customAuthModule = mocks.createAuthModule();
308
+ mocks.createAuthModule.mockClear();
309
+ let extensionWorkspaces: WorkspacesInterModuleClient | undefined;
310
+ const extension = vi.fn(({workspaces}: {workspaces: WorkspacesInterModuleClient}) => {
311
+ extensionWorkspaces = workspaces;
312
+ return [];
313
+ });
314
+ const authModule = vi.fn(({workspaces}: {workspaces: WorkspacesInterModuleClient}) =>
315
+ mocks.createAuthModule({workspaces, signupPolicy}),
316
+ );
317
+
318
+ const modules = await defaultModules({authModule, extension});
319
+ const authWorkspaces = authModule.mock.calls[0]?.[0].workspaces;
320
+
321
+ expect(authModule).toHaveBeenCalledWith({workspaces: expect.any(Object)});
322
+ expect(mocks.createAuthModule).toHaveBeenCalledWith({
323
+ workspaces: authWorkspaces,
324
+ signupPolicy,
325
+ });
326
+ expect(extension).toHaveBeenCalledWith({workspaces: authWorkspaces});
327
+ expect(extensionWorkspaces).toBe(authWorkspaces);
328
+ expect(modules).toContain(customAuthModule);
329
+ expect(
330
+ modules.flatMap((module) =>
331
+ (module.interModulePresentations ?? []).map(({contract}) => contract.module),
332
+ ),
333
+ ).toContain(authInterModuleContract.module);
334
+ });
335
+
294
336
  it('lets a host replace only the Runners module with the composed Auth client', async () => {
295
337
  const policy = {filterEligibleWorkspaceIds: vi.fn().mockResolvedValue(new Set<string>())};
296
338
  const runnersModule = mocks.createRunnersModule({});
package/src/modules.ts CHANGED
@@ -40,10 +40,14 @@ import {logger} from '@shipfox/node-opentelemetry';
40
40
 
41
41
  export interface DefaultModulesOptions {
42
42
  webhookDeliverySource?: WebhookDeliverySource | undefined;
43
+ authModule?: DefaultAuthModuleFactory | undefined;
43
44
  runnersModule?: DefaultRunnersModuleFactory | undefined;
44
45
  extension?: DefaultModulesExtension | undefined;
45
46
  }
46
47
 
48
+ export type DefaultAuthModuleFactory = (options: {
49
+ workspaces: WorkspacesInterModuleClient;
50
+ }) => ShipfoxModule;
47
51
  export type DefaultRunnersModuleFactory = (options: {auth: AuthInterModuleClient}) => ShipfoxModule;
48
52
  export type DefaultModulesExtension = (options: {
49
53
  workspaces: WorkspacesInterModuleClient;
@@ -165,7 +169,7 @@ export async function defaultModules(
165
169
 
166
170
  const modules = [
167
171
  emailChallengesModule,
168
- createAuthModule({workspaces: workspacesClient}),
172
+ (options.authModule ?? createAuthModule)({workspaces: workspacesClient}),
169
173
  workspacesModule,
170
174
  createSecretsModule(projectsClient),
171
175
  createAgentModule({secrets: secretsClient}),
@@ -180,6 +184,7 @@ export async function defaultModules(
180
184
  projects: projectsClient,
181
185
  runners: runnersClient,
182
186
  secrets: secretsClient,
187
+ workspaces: workspacesClient,
183
188
  integrations: integrationsClient,
184
189
  }),
185
190
  annotationsModule,