@shipfox/api-definitions 5.0.0 → 7.1.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/.turbo/turbo-build.log +11 -13
- package/CHANGELOG.md +49 -0
- package/dist/core/entities/workflow-definition.d.ts +2 -5
- package/dist/core/entities/workflow-definition.d.ts.map +1 -1
- package/dist/core/entities/workflow-definition.js.map +1 -1
- package/dist/core/entities/workflow-model.d.ts +1 -137
- package/dist/core/entities/workflow-model.d.ts.map +1 -1
- package/dist/core/entities/workflow-model.js.map +1 -1
- package/dist/core/integrations.d.ts +25 -0
- package/dist/core/integrations.d.ts.map +1 -0
- package/dist/core/integrations.js +30 -0
- package/dist/core/integrations.js.map +1 -0
- package/dist/core/sync-definitions.d.ts +2 -2
- package/dist/core/sync-definitions.d.ts.map +1 -1
- package/dist/core/sync-definitions.js +23 -14
- package/dist/core/sync-definitions.js.map +1 -1
- package/dist/core/workflow-model/constants.d.ts +1 -1
- package/dist/core/workflow-model/constants.d.ts.map +1 -1
- package/dist/core/workflow-model/constants.js +1 -1
- package/dist/core/workflow-model/constants.js.map +1 -1
- package/dist/core/workflow-model/normalize-job-checkout.d.ts +2 -2
- package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-job-checkout.js +2 -6
- package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -1
- package/dist/core/workflow-model/normalize-job-success.d.ts +2 -1
- package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-job-success.js +2 -1
- package/dist/core/workflow-model/normalize-job-success.js.map +1 -1
- package/dist/db/db.d.ts +2 -2
- package/dist/db/schema/sync-states.d.ts +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -11
- package/dist/index.js.map +1 -1
- package/dist/presentation/index.d.ts +1 -1
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +1 -1
- package/dist/presentation/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +4 -0
- package/dist/presentation/inter-module.d.ts.map +1 -0
- package/dist/presentation/inter-module.js +25 -0
- package/dist/presentation/inter-module.js.map +1 -0
- package/dist/presentation/routes/create-definition.d.ts +5 -6
- package/dist/presentation/routes/create-definition.d.ts.map +1 -1
- package/dist/presentation/routes/create-definition.js +7 -19
- package/dist/presentation/routes/create-definition.js.map +1 -1
- package/dist/presentation/routes/get-definition.d.ts +2 -1
- package/dist/presentation/routes/get-definition.d.ts.map +1 -1
- package/dist/presentation/routes/get-definition.js +26 -35
- package/dist/presentation/routes/get-definition.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +1 -2
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +5 -6
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/list-definitions.d.ts +2 -1
- package/dist/presentation/routes/list-definitions.d.ts.map +1 -1
- package/dist/presentation/routes/list-definitions.js +36 -45
- package/dist/presentation/routes/list-definitions.js.map +1 -1
- package/dist/presentation/routes/project-access.d.ts +10 -0
- package/dist/presentation/routes/project-access.d.ts.map +1 -0
- package/dist/presentation/routes/project-access.js +17 -0
- package/dist/presentation/routes/project-access.js.map +1 -0
- package/dist/temporal/activities/sync-activities.d.ts +3 -9
- package/dist/temporal/activities/sync-activities.d.ts.map +1 -1
- package/dist/temporal/activities/sync-activities.js +10 -14
- package/dist/temporal/activities/sync-activities.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +17195 -3337
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +16 -32
- package/src/core/entities/workflow-definition.ts +3 -6
- package/src/core/entities/workflow-model.ts +1 -162
- package/src/core/integrations.ts +39 -0
- package/src/core/sync-definitions.test.ts +19 -17
- package/src/core/sync-definitions.ts +26 -20
- package/src/core/workflow-model/constants.ts +1 -1
- package/src/core/workflow-model/normalize-job-checkout.ts +2 -5
- package/src/core/workflow-model/normalize-job-success.ts +2 -1
- package/src/db/definitions.test.ts +68 -7
- package/src/index.ts +12 -31
- package/src/presentation/index.ts +1 -1
- package/src/presentation/inter-module.ts +25 -0
- package/src/presentation/routes/create-definition.test.ts +59 -63
- package/src/presentation/routes/create-definition.ts +15 -34
- package/src/presentation/routes/get-definition.test.ts +14 -8
- package/src/presentation/routes/get-definition.ts +25 -28
- package/src/presentation/routes/index.test.ts +8 -1
- package/src/presentation/routes/index.ts +5 -7
- package/src/presentation/routes/list-definitions.test.ts +8 -7
- package/src/presentation/routes/list-definitions.ts +33 -36
- package/src/presentation/routes/project-access.ts +16 -0
- package/src/temporal/activities/sync-activities.test.ts +14 -6
- package/src/temporal/activities/sync-activities.ts +20 -33
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
+
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
4
|
import type {FastifyInstance} from 'fastify';
|
|
3
5
|
import Fastify from 'fastify';
|
|
4
6
|
import {serializerCompiler, validatorCompiler} from 'fastify-type-provider-zod';
|
|
5
|
-
import {buildCreateDefinitionRoute
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
vi.mock('@shipfox/api-projects', () => ({
|
|
10
|
-
ProjectNotFoundError: class ProjectNotFoundError extends Error {},
|
|
11
|
-
requireProjectAccess: vi.fn(({projectId}) =>
|
|
12
|
-
Promise.resolve({
|
|
13
|
-
project: {
|
|
14
|
-
id: projectId,
|
|
15
|
-
workspaceId: projectAccessState.workspaceId,
|
|
16
|
-
sourceConnectionId: projectAccessState.sourceConnectionId,
|
|
17
|
-
},
|
|
18
|
-
workspaceId: projectAccessState.workspaceId,
|
|
19
|
-
}),
|
|
20
|
-
),
|
|
21
|
-
}));
|
|
7
|
+
import {buildCreateDefinitionRoute} from './create-definition.js';
|
|
8
|
+
|
|
9
|
+
const getProjectById = vi.fn();
|
|
10
|
+
const projects = {getProjectById} as Pick<ProjectsModuleClient, 'getProjectById'>;
|
|
22
11
|
|
|
23
12
|
describe('POST /api/definitions', () => {
|
|
24
13
|
let app: FastifyInstance;
|
|
25
14
|
let workspaceId: string;
|
|
26
15
|
let projectId: string;
|
|
16
|
+
let sourceConnectionId: string;
|
|
27
17
|
|
|
28
18
|
beforeAll(async () => {
|
|
29
19
|
app = Fastify();
|
|
@@ -40,15 +30,20 @@ describe('POST /api/definitions', () => {
|
|
|
40
30
|
);
|
|
41
31
|
done();
|
|
42
32
|
});
|
|
43
|
-
app.post(
|
|
33
|
+
app.post(
|
|
34
|
+
'/api/definitions',
|
|
35
|
+
buildCreateDefinitionRoute({projects: projects as ProjectsModuleClient}),
|
|
36
|
+
);
|
|
44
37
|
await app.ready();
|
|
45
38
|
});
|
|
46
39
|
|
|
47
40
|
beforeEach(() => {
|
|
48
41
|
workspaceId = crypto.randomUUID();
|
|
49
42
|
projectId = crypto.randomUUID();
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
sourceConnectionId = crypto.randomUUID();
|
|
44
|
+
getProjectById.mockResolvedValue({
|
|
45
|
+
project: {id: projectId, workspaceId, sourceConnectionId},
|
|
46
|
+
} as never);
|
|
52
47
|
});
|
|
53
48
|
|
|
54
49
|
const validYaml = `
|
|
@@ -82,7 +77,7 @@ jobs:
|
|
|
82
77
|
});
|
|
83
78
|
|
|
84
79
|
test('skips connection snapshot loading when YAML has no integrations', async () => {
|
|
85
|
-
const
|
|
80
|
+
const getAgentToolsContext = vi.fn();
|
|
86
81
|
const appWithOptions = Fastify();
|
|
87
82
|
appWithOptions.setValidatorCompiler(validatorCompiler);
|
|
88
83
|
appWithOptions.setSerializerCompiler(serializerCompiler);
|
|
@@ -100,8 +95,11 @@ jobs:
|
|
|
100
95
|
appWithOptions.post(
|
|
101
96
|
'/api/definitions',
|
|
102
97
|
buildCreateDefinitionRoute({
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
projects: projects as ProjectsModuleClient,
|
|
99
|
+
integrations: {getAgentToolsContext} as Pick<
|
|
100
|
+
IntegrationsModuleClient,
|
|
101
|
+
'getAgentToolsContext'
|
|
102
|
+
> as IntegrationsModuleClient,
|
|
105
103
|
}),
|
|
106
104
|
);
|
|
107
105
|
await appWithOptions.ready();
|
|
@@ -117,35 +115,39 @@ jobs:
|
|
|
117
115
|
});
|
|
118
116
|
|
|
119
117
|
expect(res.statusCode).toBe(200);
|
|
120
|
-
expect(
|
|
118
|
+
expect(getAgentToolsContext).not.toHaveBeenCalled();
|
|
121
119
|
});
|
|
122
120
|
|
|
123
121
|
test('uses the project source connection as the default integration connection', async () => {
|
|
124
|
-
const
|
|
125
|
-
Promise.resolve(
|
|
126
|
-
new Map([
|
|
127
|
-
[
|
|
128
|
-
'github-main',
|
|
129
|
-
{
|
|
130
|
-
id: projectAccessState.sourceConnectionId,
|
|
131
|
-
provider: 'github',
|
|
132
|
-
capabilities: ['agent_tools'] as const,
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
]),
|
|
136
|
-
),
|
|
137
|
-
);
|
|
138
|
-
const getIntegrationConnectionById = vi.fn(() =>
|
|
122
|
+
const getAgentToolsContext = vi.fn(() =>
|
|
139
123
|
Promise.resolve({
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
124
|
+
selectionCatalogs: [
|
|
125
|
+
{
|
|
126
|
+
provider: 'github',
|
|
127
|
+
selectors: [
|
|
128
|
+
{
|
|
129
|
+
token: 'issue_read',
|
|
130
|
+
kind: 'family' as const,
|
|
131
|
+
sensitivity: 'read' as const,
|
|
132
|
+
sensitive: false,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
catalogs: [],
|
|
138
|
+
workspaceConnections: [
|
|
139
|
+
{
|
|
140
|
+
id: sourceConnectionId,
|
|
141
|
+
slug: 'github-main',
|
|
142
|
+
provider: 'github',
|
|
143
|
+
capabilities: ['agent_tools' as const],
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
defaultConnection: {
|
|
147
|
+
id: sourceConnectionId,
|
|
148
|
+
slug: 'github-main',
|
|
149
|
+
provider: 'github',
|
|
150
|
+
},
|
|
149
151
|
}),
|
|
150
152
|
);
|
|
151
153
|
const appWithOptions = Fastify();
|
|
@@ -165,18 +167,11 @@ jobs:
|
|
|
165
167
|
appWithOptions.post(
|
|
166
168
|
'/api/definitions',
|
|
167
169
|
buildCreateDefinitionRoute({
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
{token: 'issue_read', kind: 'family', sensitivity: 'read', sensitive: false},
|
|
174
|
-
],
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
]),
|
|
178
|
-
loadWorkspaceConnectionSnapshot,
|
|
179
|
-
getIntegrationConnectionById,
|
|
170
|
+
projects: projects as ProjectsModuleClient,
|
|
171
|
+
integrations: {getAgentToolsContext} as Pick<
|
|
172
|
+
IntegrationsModuleClient,
|
|
173
|
+
'getAgentToolsContext'
|
|
174
|
+
> as IntegrationsModuleClient,
|
|
180
175
|
}),
|
|
181
176
|
);
|
|
182
177
|
await appWithOptions.ready();
|
|
@@ -201,9 +196,10 @@ jobs:
|
|
|
201
196
|
});
|
|
202
197
|
|
|
203
198
|
expect(res.statusCode).toBe(200);
|
|
204
|
-
expect(
|
|
205
|
-
|
|
206
|
-
|
|
199
|
+
expect(getAgentToolsContext).toHaveBeenCalledWith({
|
|
200
|
+
workspaceId,
|
|
201
|
+
defaultConnectionId: sourceConnectionId,
|
|
202
|
+
});
|
|
207
203
|
});
|
|
208
204
|
|
|
209
205
|
test('invalid YAML syntax returns 400', async () => {
|
|
@@ -3,27 +3,24 @@ import {
|
|
|
3
3
|
definitionResponseSchema,
|
|
4
4
|
definitionValidationErrorSchema,
|
|
5
5
|
} from '@shipfox/api-definitions-dto';
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
GetIntegrationConnectionByIdFn,
|
|
9
|
-
LoadWorkspaceConnectionSnapshot,
|
|
10
|
-
} from '@shipfox/api-integration-core';
|
|
11
|
-
import {ProjectNotFoundError, requireProjectAccess} from '@shipfox/api-projects';
|
|
6
|
+
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
|
|
7
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
12
8
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
13
9
|
import {z} from 'zod';
|
|
14
10
|
import {DefinitionParseError} from '#core/errors.js';
|
|
15
11
|
import {hasAgentStepIntegrations} from '#core/has-agent-step-integrations.js';
|
|
12
|
+
import {loadIntegrationValidationContext} from '#core/integrations.js';
|
|
16
13
|
import {parseDefinition} from '#core/parse-definition.js';
|
|
17
14
|
import {upsertDefinition} from '#db/definitions.js';
|
|
18
15
|
import {toDefinitionDto} from '#presentation/dto/index.js';
|
|
16
|
+
import {requireProjectAccess} from './project-access.js';
|
|
19
17
|
|
|
20
18
|
export interface CreateDefinitionRouteOptions {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
getIntegrationConnectionById?: GetIntegrationConnectionByIdFn | undefined;
|
|
19
|
+
projects: ProjectsModuleClient;
|
|
20
|
+
integrations?: IntegrationsModuleClient;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
|
-
export function buildCreateDefinitionRoute(options: CreateDefinitionRouteOptions
|
|
23
|
+
export function buildCreateDefinitionRoute(options: CreateDefinitionRouteOptions) {
|
|
27
24
|
return defineRoute({
|
|
28
25
|
method: 'POST',
|
|
29
26
|
path: '/',
|
|
@@ -47,36 +44,22 @@ export function buildCreateDefinitionRoute(options: CreateDefinitionRouteOptions
|
|
|
47
44
|
status: 400,
|
|
48
45
|
});
|
|
49
46
|
}
|
|
50
|
-
if (error instanceof ProjectNotFoundError) {
|
|
51
|
-
throw new ClientError(error.message, 'project-not-found', {status: 404});
|
|
52
|
-
}
|
|
53
47
|
throw error;
|
|
54
48
|
},
|
|
55
49
|
handler: async (request) => {
|
|
56
50
|
const {project_id: projectId, config_path, source, yaml: yamlString, sha, ref} = request.body;
|
|
57
|
-
const
|
|
51
|
+
const project = await requireProjectAccess(request, projectId, options.projects);
|
|
58
52
|
|
|
59
53
|
const structurallyParsed = parseDefinition(yamlString);
|
|
60
|
-
const {
|
|
61
|
-
agentToolSelectionCatalogs,
|
|
62
|
-
loadWorkspaceConnectionSnapshot,
|
|
63
|
-
getIntegrationConnectionById,
|
|
64
|
-
} = options;
|
|
54
|
+
const {integrations} = options;
|
|
65
55
|
const parsed =
|
|
66
|
-
|
|
67
|
-
loadWorkspaceConnectionSnapshot !== undefined &&
|
|
68
|
-
hasAgentStepIntegrations(structurallyParsed.document)
|
|
56
|
+
integrations !== undefined && hasAgentStepIntegrations(structurallyParsed.document)
|
|
69
57
|
? parseDefinition(yamlString, {
|
|
70
|
-
integrationValidationContext:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
defaultConnectionSlug:
|
|
76
|
-
getIntegrationConnectionById === undefined
|
|
77
|
-
? undefined
|
|
78
|
-
: (await getIntegrationConnectionById(project.sourceConnectionId))?.slug,
|
|
79
|
-
},
|
|
58
|
+
integrationValidationContext: await loadIntegrationValidationContext(
|
|
59
|
+
integrations,
|
|
60
|
+
project.workspaceId,
|
|
61
|
+
project.sourceConnectionId,
|
|
62
|
+
),
|
|
80
63
|
})
|
|
81
64
|
: structurallyParsed;
|
|
82
65
|
|
|
@@ -97,5 +80,3 @@ export function buildCreateDefinitionRoute(options: CreateDefinitionRouteOptions
|
|
|
97
80
|
},
|
|
98
81
|
});
|
|
99
82
|
}
|
|
100
|
-
|
|
101
|
-
export const createDefinitionRoute = buildCreateDefinitionRoute();
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
3
|
import type {FastifyInstance} from 'fastify';
|
|
3
4
|
import Fastify from 'fastify';
|
|
4
5
|
import {serializerCompiler, validatorCompiler} from 'fastify-type-provider-zod';
|
|
5
6
|
import {definitionFactory} from '#test/index.js';
|
|
6
|
-
import {
|
|
7
|
+
import {buildGetDefinitionRoute} from './get-definition.js';
|
|
7
8
|
|
|
8
9
|
const projectAccessState = vi.hoisted(() => ({workspaceId: ''}));
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
requireProjectAccess: vi.fn(({projectId}) =>
|
|
11
|
+
const projects = {
|
|
12
|
+
getProjectById: vi.fn(({projectId}) =>
|
|
13
13
|
Promise.resolve({
|
|
14
|
-
project: {
|
|
15
|
-
|
|
14
|
+
project: {
|
|
15
|
+
id: projectId,
|
|
16
|
+
workspaceId: projectAccessState.workspaceId,
|
|
17
|
+
sourceConnectionId: crypto.randomUUID(),
|
|
18
|
+
sourceExternalRepositoryId: 'repo',
|
|
19
|
+
name: 'Project',
|
|
20
|
+
},
|
|
16
21
|
}),
|
|
17
22
|
),
|
|
18
|
-
|
|
23
|
+
requireProjectForWorkspace: vi.fn(),
|
|
24
|
+
} as unknown as ProjectsModuleClient;
|
|
19
25
|
|
|
20
26
|
describe('GET /api/definitions/:id', () => {
|
|
21
27
|
let app: FastifyInstance;
|
|
@@ -36,7 +42,7 @@ describe('GET /api/definitions/:id', () => {
|
|
|
36
42
|
);
|
|
37
43
|
done();
|
|
38
44
|
});
|
|
39
|
-
app.get('/api/definitions/:id',
|
|
45
|
+
app.get('/api/definitions/:id', buildGetDefinitionRoute(projects));
|
|
40
46
|
await app.ready();
|
|
41
47
|
});
|
|
42
48
|
|
|
@@ -1,37 +1,34 @@
|
|
|
1
1
|
import {definitionResponseSchema} from '@shipfox/api-definitions-dto';
|
|
2
|
-
import {
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
3
3
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
4
4
|
import {z} from 'zod';
|
|
5
5
|
import {getDefinitionById} from '#db/definitions.js';
|
|
6
6
|
import {toDefinitionDto} from '#presentation/dto/index.js';
|
|
7
|
+
import {requireProjectAccess} from './project-access.js';
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
export function buildGetDefinitionRoute(projects: ProjectsModuleClient) {
|
|
10
|
+
return defineRoute({
|
|
11
|
+
method: 'GET',
|
|
12
|
+
path: '/:id',
|
|
13
|
+
description: 'Get a definition by ID',
|
|
14
|
+
schema: {
|
|
15
|
+
params: z.object({
|
|
16
|
+
id: z.string().uuid(),
|
|
17
|
+
}),
|
|
18
|
+
response: {
|
|
19
|
+
200: definitionResponseSchema,
|
|
20
|
+
},
|
|
18
21
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
throw new ClientError('Definition not found', 'not-found', {status: 404});
|
|
23
|
-
}
|
|
24
|
-
throw error;
|
|
25
|
-
},
|
|
26
|
-
handler: async (request) => {
|
|
27
|
-
const {id} = request.params;
|
|
28
|
-
const definition = await getDefinitionById(id);
|
|
22
|
+
handler: async (request) => {
|
|
23
|
+
const {id} = request.params;
|
|
24
|
+
const definition = await getDefinitionById(id);
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
if (!definition) {
|
|
27
|
+
throw new ClientError('Definition not found', 'not-found', {status: 404});
|
|
28
|
+
}
|
|
29
|
+
await requireProjectAccess(request, definition.projectId, projects);
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
31
|
+
return toDefinitionDto(definition);
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import {AUTH_USER, buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
3
|
import {type AuthMethod, ClientError, closeApp, createApp} from '@shipfox/node-fastify';
|
|
3
4
|
import type {FastifyRequest} from 'fastify';
|
|
4
|
-
import {
|
|
5
|
+
import {createDefinitionRoutes} from './index.js';
|
|
6
|
+
|
|
7
|
+
const projects = {
|
|
8
|
+
getProjectById: vi.fn(),
|
|
9
|
+
requireProjectForWorkspace: vi.fn(),
|
|
10
|
+
} as unknown as ProjectsModuleClient;
|
|
11
|
+
const definitionRoutes = createDefinitionRoutes({projects});
|
|
5
12
|
|
|
6
13
|
const fakeUserAuth: AuthMethod = {
|
|
7
14
|
name: AUTH_USER,
|
|
@@ -4,25 +4,23 @@ import {
|
|
|
4
4
|
buildCreateDefinitionRoute,
|
|
5
5
|
type CreateDefinitionRouteOptions,
|
|
6
6
|
} from './create-definition.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import {buildGetDefinitionRoute} from './get-definition.js';
|
|
8
|
+
import {buildListDefinitionsRoute} from './list-definitions.js';
|
|
9
9
|
import {validateDefinitionRoute} from './validate-definition.js';
|
|
10
10
|
|
|
11
11
|
export interface DefinitionRouteOptions extends CreateDefinitionRouteOptions {}
|
|
12
12
|
|
|
13
|
-
export function createDefinitionRoutes(options: DefinitionRouteOptions
|
|
13
|
+
export function createDefinitionRoutes(options: DefinitionRouteOptions): RouteGroup[] {
|
|
14
14
|
return [
|
|
15
15
|
{
|
|
16
16
|
prefix: '/definitions',
|
|
17
17
|
auth: AUTH_USER,
|
|
18
18
|
routes: [
|
|
19
19
|
buildCreateDefinitionRoute(options),
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
buildListDefinitionsRoute(options.projects),
|
|
21
|
+
buildGetDefinitionRoute(options.projects),
|
|
22
22
|
validateDefinitionRoute,
|
|
23
23
|
],
|
|
24
24
|
},
|
|
25
25
|
];
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
export const definitionRoutes = createDefinitionRoutes();
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
3
|
import {encodeStringIdCursor} from '@shipfox/node-drizzle';
|
|
3
4
|
import type {FastifyInstance} from 'fastify';
|
|
4
5
|
import Fastify from 'fastify';
|
|
5
6
|
import {serializerCompiler, validatorCompiler} from 'fastify-type-provider-zod';
|
|
6
7
|
import {markDefinitionSyncState} from '#db/index.js';
|
|
7
8
|
import {definitionFactory} from '#test/index.js';
|
|
8
|
-
import {
|
|
9
|
+
import {buildListDefinitionsRoute} from './list-definitions.js';
|
|
9
10
|
|
|
10
11
|
const projectAccessState = vi.hoisted(() => ({
|
|
11
12
|
workspaceId: '',
|
|
@@ -13,20 +14,20 @@ const projectAccessState = vi.hoisted(() => ({
|
|
|
13
14
|
sourceExternalRepositoryId: '',
|
|
14
15
|
}));
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
requireProjectAccess: vi.fn(({projectId}) =>
|
|
17
|
+
const projects = {
|
|
18
|
+
getProjectById: vi.fn(({projectId}) =>
|
|
19
19
|
Promise.resolve({
|
|
20
20
|
project: {
|
|
21
21
|
id: projectId,
|
|
22
22
|
workspaceId: projectAccessState.workspaceId,
|
|
23
23
|
sourceConnectionId: projectAccessState.sourceConnectionId,
|
|
24
24
|
sourceExternalRepositoryId: projectAccessState.sourceExternalRepositoryId,
|
|
25
|
+
name: 'Project',
|
|
25
26
|
},
|
|
26
|
-
workspaceId: projectAccessState.workspaceId,
|
|
27
27
|
}),
|
|
28
28
|
),
|
|
29
|
-
|
|
29
|
+
requireProjectForWorkspace: vi.fn(),
|
|
30
|
+
} as unknown as ProjectsModuleClient;
|
|
30
31
|
|
|
31
32
|
describe('GET /api/definitions', () => {
|
|
32
33
|
let app: FastifyInstance;
|
|
@@ -50,7 +51,7 @@ describe('GET /api/definitions', () => {
|
|
|
50
51
|
);
|
|
51
52
|
done();
|
|
52
53
|
});
|
|
53
|
-
app.get('/api/definitions',
|
|
54
|
+
app.get('/api/definitions', buildListDefinitionsRoute(projects));
|
|
54
55
|
await app.ready();
|
|
55
56
|
});
|
|
56
57
|
|
|
@@ -2,48 +2,45 @@ import {
|
|
|
2
2
|
definitionListQuerySchema,
|
|
3
3
|
definitionListResponseSchema,
|
|
4
4
|
} from '@shipfox/api-definitions-dto';
|
|
5
|
-
import {
|
|
5
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
6
6
|
import {decodeStringIdCursor, encodeStringIdCursor} from '@shipfox/node-drizzle';
|
|
7
7
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
8
8
|
import {listDefinitions} from '#db/definitions.js';
|
|
9
9
|
import {getLatestDefinitionSyncState} from '#db/sync-states.js';
|
|
10
10
|
import {toDefinitionDto, toDefinitionSyncSummaryDto} from '#presentation/dto/index.js';
|
|
11
|
+
import {requireProjectAccess} from './project-access.js';
|
|
11
12
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
export function buildListDefinitionsRoute(projects: ProjectsModuleClient) {
|
|
14
|
+
return defineRoute({
|
|
15
|
+
method: 'GET',
|
|
16
|
+
path: '/',
|
|
17
|
+
description: 'List all definitions for a project',
|
|
18
|
+
schema: {
|
|
19
|
+
querystring: definitionListQuerySchema,
|
|
20
|
+
response: {
|
|
21
|
+
200: definitionListResponseSchema,
|
|
22
|
+
},
|
|
20
23
|
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
},
|
|
28
|
-
handler: async (request) => {
|
|
29
|
-
const {project_id: projectId, limit, cursor} = request.query;
|
|
30
|
-
const decodedCursor = decodeStringIdCursor(cursor);
|
|
31
|
-
if (cursor && !decodedCursor) {
|
|
32
|
-
throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
|
|
33
|
-
}
|
|
24
|
+
handler: async (request) => {
|
|
25
|
+
const {project_id: projectId, limit, cursor} = request.query;
|
|
26
|
+
const decodedCursor = decodeStringIdCursor(cursor);
|
|
27
|
+
if (cursor && !decodedCursor) {
|
|
28
|
+
throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
|
|
29
|
+
}
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
const project = await requireProjectAccess(request, projectId, projects);
|
|
32
|
+
const result = await listDefinitions({projectId, limit, cursor: decodedCursor});
|
|
33
|
+
const syncState = await getLatestDefinitionSyncState({
|
|
34
|
+
projectId,
|
|
35
|
+
sourceConnectionId: project.sourceConnectionId,
|
|
36
|
+
sourceExternalRepositoryId: project.sourceExternalRepositoryId,
|
|
37
|
+
});
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
39
|
+
return {
|
|
40
|
+
definitions: result.definitions.map(toDefinitionDto),
|
|
41
|
+
sync: toDefinitionSyncSummaryDto(syncState),
|
|
42
|
+
next_cursor: result.nextCursor ? encodeStringIdCursor(result.nextCursor) : null,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {requireWorkspaceAccess} from '@shipfox/api-auth-context';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
3
|
+
import {ClientError} from '@shipfox/node-fastify';
|
|
4
|
+
import type {FastifyRequest} from 'fastify';
|
|
5
|
+
|
|
6
|
+
export async function requireProjectAccess(
|
|
7
|
+
request: FastifyRequest,
|
|
8
|
+
projectId: string,
|
|
9
|
+
projects: ProjectsModuleClient,
|
|
10
|
+
) {
|
|
11
|
+
const {project} = await projects.getProjectById({projectId});
|
|
12
|
+
if (project === null)
|
|
13
|
+
throw new ClientError('Project not found', 'project-not-found', {status: 404});
|
|
14
|
+
requireWorkspaceAccess({request, workspaceId: project.workspaceId});
|
|
15
|
+
return project;
|
|
16
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '@shipfox/
|
|
1
|
+
import type {IntegrationSourceControlService} from '@shipfox/api-integration-core';
|
|
2
|
+
import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
4
|
+
import {isErrorReported} from '@shipfox/node-error-monitoring';
|
|
5
5
|
import {ApplicationFailure} from '@temporalio/common';
|
|
6
6
|
import {sql} from 'drizzle-orm';
|
|
7
7
|
import {db, definitionSyncStates} from '#db/index.js';
|
|
@@ -142,6 +142,8 @@ describe('definition sync activities', () => {
|
|
|
142
142
|
type: 'unknown',
|
|
143
143
|
message: 'temporary outage',
|
|
144
144
|
});
|
|
145
|
+
const error = await result.catch((error: unknown) => error);
|
|
146
|
+
expect(isErrorReported(error)).toBe(false);
|
|
145
147
|
});
|
|
146
148
|
|
|
147
149
|
it('preserves retryable provider error codes for workflow-level failure persistence', async () => {
|
|
@@ -149,7 +151,11 @@ describe('definition sync activities', () => {
|
|
|
149
151
|
sourceControl({
|
|
150
152
|
resolveRepository: vi.fn(() => {
|
|
151
153
|
return Promise.reject(
|
|
152
|
-
|
|
154
|
+
createInterModuleKnownError(
|
|
155
|
+
integrationsInterModuleContract.methods.resolveSourceRepository,
|
|
156
|
+
'provider-failure',
|
|
157
|
+
{reason: 'timeout'},
|
|
158
|
+
),
|
|
153
159
|
);
|
|
154
160
|
}),
|
|
155
161
|
}),
|
|
@@ -165,8 +171,10 @@ describe('definition sync activities', () => {
|
|
|
165
171
|
await expect(result).rejects.toMatchObject({
|
|
166
172
|
nonRetryable: false,
|
|
167
173
|
type: 'provider-timeout',
|
|
168
|
-
message: '
|
|
174
|
+
message: 'integrations.resolveSourceRepository: provider-failure',
|
|
169
175
|
});
|
|
176
|
+
const error = await result.catch((error: unknown) => error);
|
|
177
|
+
expect(isErrorReported(error)).toBe(true);
|
|
170
178
|
});
|
|
171
179
|
});
|
|
172
180
|
|