@shipfox/api-definitions 5.0.0 → 6.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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +36 -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 +6 -10
- package/dist/temporal/activities/sync-activities.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +16 -14
- 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 +9 -6
- package/src/temporal/activities/sync-activities.ts +14 -29
- 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,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '@shipfox/api-integration-core';
|
|
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';
|
|
5
4
|
import {ApplicationFailure} from '@temporalio/common';
|
|
6
5
|
import {sql} from 'drizzle-orm';
|
|
7
6
|
import {db, definitionSyncStates} from '#db/index.js';
|
|
@@ -149,7 +148,11 @@ describe('definition sync activities', () => {
|
|
|
149
148
|
sourceControl({
|
|
150
149
|
resolveRepository: vi.fn(() => {
|
|
151
150
|
return Promise.reject(
|
|
152
|
-
|
|
151
|
+
createInterModuleKnownError(
|
|
152
|
+
integrationsInterModuleContract.methods.resolveSourceRepository,
|
|
153
|
+
'provider-failure',
|
|
154
|
+
{reason: 'timeout'},
|
|
155
|
+
),
|
|
153
156
|
);
|
|
154
157
|
}),
|
|
155
158
|
}),
|
|
@@ -165,7 +168,7 @@ describe('definition sync activities', () => {
|
|
|
165
168
|
await expect(result).rejects.toMatchObject({
|
|
166
169
|
nonRetryable: false,
|
|
167
170
|
type: 'provider-timeout',
|
|
168
|
-
message: '
|
|
171
|
+
message: 'integrations.resolveSourceRepository: provider-failure',
|
|
169
172
|
});
|
|
170
173
|
});
|
|
171
174
|
});
|