@shipfox/api-server 9.1.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.1.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.0.3",
26
25
  "@shipfox/annotations-dto": "9.0.2",
27
- "@shipfox/api-agent": "9.0.3",
26
+ "@shipfox/annotations": "9.3.0",
28
27
  "@shipfox/api-agent-dto": "9.0.2",
29
- "@shipfox/api-auth": "9.1.0",
30
- "@shipfox/api-auth-dto": "9.0.2",
31
- "@shipfox/api-definitions": "9.1.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.3",
35
- "@shipfox/api-integration-core": "9.1.0",
34
+ "@shipfox/api-integration-core": "9.3.0",
36
35
  "@shipfox/api-integration-core-dto": "9.0.2",
37
- "@shipfox/api-logs": "9.0.3",
38
- "@shipfox/api-projects": "9.0.3",
39
- "@shipfox/api-projects-dto": "9.0.2",
40
- "@shipfox/api-runners": "9.0.3",
36
+ "@shipfox/api-email-challenges": "1.1.5",
37
+ "@shipfox/api-projects": "9.3.0",
38
+ "@shipfox/api-runners": "9.3.0",
39
+ "@shipfox/api-projects-dto": "9.2.0",
41
40
  "@shipfox/api-runners-dto": "9.0.2",
42
- "@shipfox/api-secrets": "9.0.3",
41
+ "@shipfox/api-secrets": "9.3.0",
43
42
  "@shipfox/api-secrets-dto": "9.0.2",
44
- "@shipfox/api-triggers": "9.0.3",
45
- "@shipfox/api-workflows": "9.0.3",
46
- "@shipfox/api-workflows-dto": "9.0.2",
47
- "@shipfox/api-workspaces-dto": "9.0.2",
48
- "@shipfox/api-workspaces": "9.1.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,18 +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
- },
62
- },
63
- ],
64
- }),
55
+ createAuthModule: mocks.createAuthModule,
65
56
  }));
66
57
  vi.mock('@shipfox/api-agent', () => ({createAgentModule: mocks.createAgentModule}));
67
58
  vi.mock('@shipfox/api-auth/config', () => ({
@@ -103,6 +94,7 @@ vi.mock('@shipfox/api-workspaces', () => ({
103
94
  handlers: {
104
95
  listMembershipsForTokenClaims: mocks.listMembershipsForTokenClaims,
105
96
  getWorkspaceCreator: mocks.getWorkspaceCreator,
97
+ getWorkspaceOperatingState: mocks.getWorkspaceOperatingState,
106
98
  preflightInvitationAcceptance: vi.fn(),
107
99
  acceptInvitation: vi.fn(),
108
100
  requireActiveMembership: vi.fn(),
@@ -117,6 +109,7 @@ describe('defaultModules', () => {
117
109
  mocks.buildAgentToolCatalogs.mockReset();
118
110
  mocks.buildAgentToolSelectionCatalogs.mockReset();
119
111
  mocks.createAgentModule.mockReset();
112
+ mocks.createAuthModule.mockReset();
120
113
  mocks.createDefinitionsModule.mockReset();
121
114
  mocks.createIntegrationsContext.mockReset();
122
115
  mocks.createLogsModule.mockReset();
@@ -153,6 +146,20 @@ describe('defaultModules', () => {
153
146
  mocks.buildAgentToolCatalogs.mockResolvedValue(new Map());
154
147
  mocks.buildAgentToolSelectionCatalogs.mockResolvedValue(new Map());
155
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
+ });
156
163
  mocks.deleteSecrets.mockResolvedValue({deleted: 1});
157
164
  mocks.getSecret.mockResolvedValue({value: 'secret'});
158
165
  mocks.listMembershipsForTokenClaims.mockResolvedValue({memberships: []});
@@ -289,6 +296,43 @@ describe('defaultModules', () => {
289
296
  expect(mocks.createRunnersModule).toHaveBeenCalledWith({auth: expect.any(Object)});
290
297
  });
291
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
+
292
336
  it('lets a host replace only the Runners module with the composed Auth client', async () => {
293
337
  const policy = {filterEligibleWorkspaceIds: vi.fn().mockResolvedValue(new Set<string>())};
294
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,