@shipfox/api-integration-core 5.0.0 → 7.0.1
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 +83 -0
- package/dist/core/entities/provider.d.ts +7 -2
- package/dist/core/entities/provider.d.ts.map +1 -1
- package/dist/core/entities/provider.js.map +1 -1
- package/dist/core/errors.d.ts +7 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +8 -2
- package/dist/core/errors.js.map +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -5
- package/dist/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +9 -0
- package/dist/presentation/inter-module.d.ts.map +1 -0
- package/dist/presentation/inter-module.js +121 -0
- package/dist/presentation/inter-module.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/index.js +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts +5 -2
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js +53 -17
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +2 -3
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +2 -2
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/providers/gitea.d.ts.map +1 -1
- package/dist/providers/gitea.js +10 -8
- package/dist/providers/gitea.js.map +1 -1
- package/dist/providers/github.d.ts.map +1 -1
- package/dist/providers/github.js +18 -13
- package/dist/providers/github.js.map +1 -1
- package/dist/providers/jira.d.ts.map +1 -1
- package/dist/providers/jira.js +4 -1
- package/dist/providers/jira.js.map +1 -1
- package/dist/providers/linear.d.ts.map +1 -1
- package/dist/providers/linear.js +34 -29
- package/dist/providers/linear.js.map +1 -1
- package/dist/providers/sentry.d.ts.map +1 -1
- package/dist/providers/sentry.js +13 -11
- package/dist/providers/sentry.js.map +1 -1
- package/dist/providers/slack.d.ts.map +1 -1
- package/dist/providers/slack.js +34 -29
- package/dist/providers/slack.js.map +1 -1
- package/dist/providers/types.d.ts +12 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/webhook.d.ts.map +1 -1
- package/dist/providers/webhook.js +11 -9
- package/dist/providers/webhook.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +22 -17
- package/src/core/entities/provider.ts +7 -1
- package/src/core/errors.ts +12 -2
- package/src/index.test.ts +112 -0
- package/src/index.ts +102 -9
- package/src/presentation/inter-module.ts +155 -0
- package/src/presentation/routes/agent-tools-gateway/index.ts +1 -0
- package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.test.ts +36 -0
- package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts +64 -22
- package/src/presentation/routes/index.ts +4 -5
- package/src/providers/gitea.ts +11 -8
- package/src/providers/github.ts +17 -11
- package/src/providers/jira.ts +3 -0
- package/src/providers/linear.ts +30 -24
- package/src/providers/sentry.ts +15 -12
- package/src/providers/slack.ts +31 -25
- package/src/providers/types.ts +15 -0
- package/src/providers/webhook.ts +11 -9
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -4,6 +4,11 @@ import {
|
|
|
4
4
|
materializedAgentStepConfigSchema,
|
|
5
5
|
} from '@shipfox/api-agent-dto';
|
|
6
6
|
import {requireLeasedJobContext} from '@shipfox/api-auth-context';
|
|
7
|
+
import {
|
|
8
|
+
type WorkflowsModuleClient,
|
|
9
|
+
workflowsInterModuleContract,
|
|
10
|
+
} from '@shipfox/api-workflows-dto/inter-module';
|
|
11
|
+
import {isInterModuleKnownError} from '@shipfox/inter-module';
|
|
7
12
|
import {ClientError} from '@shipfox/node-fastify';
|
|
8
13
|
import type {IntegrationConnection} from '#core/entities/connection.js';
|
|
9
14
|
import type {IntegrationProviderKind} from '#core/entities/provider.js';
|
|
@@ -16,10 +21,49 @@ export type LeasedAgentStepLoader = (params: {
|
|
|
16
21
|
stepId: string;
|
|
17
22
|
attempt: number;
|
|
18
23
|
}) => Promise<{
|
|
19
|
-
step: {type: string; config: Record<string, unknown>};
|
|
20
24
|
workspaceId: string;
|
|
25
|
+
integrations?: MaterializedAgentIntegrationConfigDto[];
|
|
26
|
+
step?: {type: string; config: Record<string, unknown>};
|
|
21
27
|
}>;
|
|
22
28
|
|
|
29
|
+
export function createWorkflowsLeasedAgentStepLoader(
|
|
30
|
+
workflows: WorkflowsModuleClient,
|
|
31
|
+
): LeasedAgentStepLoader {
|
|
32
|
+
return async ({request, stepId, attempt}) => {
|
|
33
|
+
const leasedJob = requireLeasedJobContext(request);
|
|
34
|
+
try {
|
|
35
|
+
const context = await workflows.getLeasedAgentToolContext({
|
|
36
|
+
jobId: leasedJob.jobId,
|
|
37
|
+
jobExecutionId: leasedJob.jobExecutionId,
|
|
38
|
+
runnerSessionId: leasedJob.runnerSessionId,
|
|
39
|
+
stepId,
|
|
40
|
+
attempt,
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
workspaceId: context.workspaceId,
|
|
44
|
+
integrations: context.integrations,
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw toLeasedAgentToolClientError(error);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function toLeasedAgentToolClientError(error: unknown): unknown {
|
|
53
|
+
const method = workflowsInterModuleContract.methods.getLeasedAgentToolContext;
|
|
54
|
+
if (!isInterModuleKnownError(method, error)) return error;
|
|
55
|
+
|
|
56
|
+
const status = [
|
|
57
|
+
'step-attempt-mismatch',
|
|
58
|
+
'step-not-running',
|
|
59
|
+
'leased-step-not-agent',
|
|
60
|
+
'agent-step-config-invalid',
|
|
61
|
+
].includes(error.code)
|
|
62
|
+
? 409
|
|
63
|
+
: 404;
|
|
64
|
+
return new ClientError(error.code.replaceAll('-', ' '), error.code, {status});
|
|
65
|
+
}
|
|
66
|
+
|
|
23
67
|
export interface AuthorizedIntegrationTool {
|
|
24
68
|
mcpName: string;
|
|
25
69
|
integration: MaterializedAgentIntegrationConfigDto;
|
|
@@ -49,18 +93,13 @@ export async function resolveAuthorizedIntegrationTools(
|
|
|
49
93
|
});
|
|
50
94
|
}
|
|
51
95
|
|
|
52
|
-
const
|
|
96
|
+
const loaded = await params.loadLeasedAgentStep({
|
|
53
97
|
request: params.request,
|
|
54
98
|
stepId: leasedJob.currentStepId,
|
|
55
99
|
attempt: leasedJob.currentStepAttempt,
|
|
56
100
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
status: 409,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const integrations = parseAgentIntegrations(step.config);
|
|
101
|
+
const workspaceId = loaded.workspaceId;
|
|
102
|
+
const integrations = loaded.integrations ?? legacyIntegrations(loaded.step);
|
|
64
103
|
const authorizedTools: AuthorizedIntegrationToolMap = new Map();
|
|
65
104
|
|
|
66
105
|
for (const integration of integrations) {
|
|
@@ -102,6 +141,22 @@ export async function resolveAuthorizedIntegrationTools(
|
|
|
102
141
|
return authorizedTools;
|
|
103
142
|
}
|
|
104
143
|
|
|
144
|
+
function legacyIntegrations(step: {type: string; config: Record<string, unknown>} | undefined) {
|
|
145
|
+
if (step?.type !== 'agent') {
|
|
146
|
+
throw new ClientError('Current leased step is not an agent step', 'leased-step-not-agent', {
|
|
147
|
+
status: 409,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
return materializedAgentStepConfigSchema.parse(step.config).integrations ?? [];
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {
|
|
154
|
+
status: 409,
|
|
155
|
+
cause: error,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
105
160
|
export function sanitizeSlug(slug: string): string {
|
|
106
161
|
return slug.replaceAll('-', '_');
|
|
107
162
|
}
|
|
@@ -132,19 +187,6 @@ function toolInputSchema(tool: MaterializedAgentIntegrationToolConfigDto): Agent
|
|
|
132
187
|
return narrowMethodEnum(tool.inputSchema, authorizedMethods);
|
|
133
188
|
}
|
|
134
189
|
|
|
135
|
-
function parseAgentIntegrations(
|
|
136
|
-
config: Record<string, unknown>,
|
|
137
|
-
): MaterializedAgentIntegrationConfigDto[] {
|
|
138
|
-
try {
|
|
139
|
-
return materializedAgentStepConfigSchema.parse(config).integrations ?? [];
|
|
140
|
-
} catch (error) {
|
|
141
|
-
throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {
|
|
142
|
-
status: 409,
|
|
143
|
-
cause: error,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
190
|
async function loadAuthorizedConnection(params: {
|
|
149
191
|
integration: MaterializedAgentIntegrationConfigDto;
|
|
150
192
|
workspaceId: string;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type {WorkflowsModuleClient} from '@shipfox/api-workflows-dto/inter-module';
|
|
1
2
|
import type {RouteExport} from '@shipfox/node-fastify';
|
|
2
3
|
import type {IntegrationProviderRegistry} from '#core/providers/registry.js';
|
|
3
4
|
import type {IntegrationSourceControlService} from '#core/source-control-service.js';
|
|
4
5
|
import type {GetIntegrationConnectionByIdFn} from '#db/connections.js';
|
|
5
6
|
import {
|
|
6
7
|
createAgentToolsGatewayRoutes,
|
|
7
|
-
|
|
8
|
+
createWorkflowsLeasedAgentStepLoader,
|
|
8
9
|
} from './agent-tools-gateway/index.js';
|
|
9
10
|
import {createListIntegrationConnectionsRoute} from './list-connections.js';
|
|
10
11
|
import {createListIntegrationProvidersRoute} from './list-providers.js';
|
|
@@ -14,12 +15,10 @@ import {
|
|
|
14
15
|
createUpdateIntegrationConnectionRoute,
|
|
15
16
|
} from './manage-connections.js';
|
|
16
17
|
|
|
17
|
-
export type {LeasedAgentStepLoader} from './agent-tools-gateway/index.js';
|
|
18
|
-
|
|
19
18
|
export interface CreateIntegrationRoutesOptions {
|
|
20
19
|
agentTools?:
|
|
21
20
|
| {
|
|
22
|
-
|
|
21
|
+
workflows: WorkflowsModuleClient;
|
|
23
22
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
24
23
|
}
|
|
25
24
|
| undefined;
|
|
@@ -35,7 +34,7 @@ export function createIntegrationRoutes(
|
|
|
35
34
|
? [
|
|
36
35
|
createAgentToolsGatewayRoutes({
|
|
37
36
|
registry,
|
|
38
|
-
loadLeasedAgentStep: options.agentTools.
|
|
37
|
+
loadLeasedAgentStep: createWorkflowsLeasedAgentStepLoader(options.agentTools.workflows),
|
|
39
38
|
getIntegrationConnectionById: options.agentTools.getIntegrationConnectionById,
|
|
40
39
|
}),
|
|
41
40
|
]
|
package/src/providers/gitea.ts
CHANGED
|
@@ -79,15 +79,18 @@ async function loadGiteaModuleParts(): Promise<IntegrationModuleParts> {
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const integrationProvider = createGiteaIntegrationProvider({
|
|
83
|
+
getExistingGiteaConnection,
|
|
84
|
+
connectGiteaConnection,
|
|
85
|
+
publishSourcePush,
|
|
86
|
+
recordDeliveryOnly,
|
|
87
|
+
getIntegrationConnectionById,
|
|
88
|
+
coreDb: db,
|
|
89
|
+
});
|
|
90
|
+
|
|
82
91
|
return {
|
|
83
|
-
provider:
|
|
84
|
-
|
|
85
|
-
connectGiteaConnection,
|
|
86
|
-
publishSourcePush,
|
|
87
|
-
recordDeliveryOnly,
|
|
88
|
-
getIntegrationConnectionById,
|
|
89
|
-
coreDb: db,
|
|
90
|
-
}),
|
|
92
|
+
provider: integrationProvider,
|
|
93
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
91
94
|
database: {
|
|
92
95
|
db: giteaDb,
|
|
93
96
|
migrationsPath: giteaMigrationsPath,
|
package/src/providers/github.ts
CHANGED
|
@@ -116,18 +116,24 @@ async function loadGithubModuleParts(
|
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
const integrationProvider = createGithubIntegrationProvider({
|
|
120
|
+
getExistingGithubConnection,
|
|
121
|
+
connectGithubInstallation,
|
|
122
|
+
publishIntegrationEventReceived,
|
|
123
|
+
publishSourcePush,
|
|
124
|
+
recordDeliveryOnly,
|
|
125
|
+
getIntegrationConnectionById,
|
|
126
|
+
coreDb: db,
|
|
127
|
+
deleteSecrets: options.secrets?.deleteSecrets,
|
|
128
|
+
agentTools: {tokenProvider},
|
|
129
|
+
...(options.requireActiveWorkspaceMembership
|
|
130
|
+
? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}
|
|
131
|
+
: {}),
|
|
132
|
+
});
|
|
133
|
+
|
|
119
134
|
return {
|
|
120
|
-
provider:
|
|
121
|
-
|
|
122
|
-
connectGithubInstallation,
|
|
123
|
-
publishIntegrationEventReceived,
|
|
124
|
-
publishSourcePush,
|
|
125
|
-
recordDeliveryOnly,
|
|
126
|
-
getIntegrationConnectionById,
|
|
127
|
-
coreDb: db,
|
|
128
|
-
deleteSecrets: options.secrets?.deleteSecrets,
|
|
129
|
-
agentTools: {tokenProvider},
|
|
130
|
-
}),
|
|
135
|
+
provider: integrationProvider,
|
|
136
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
131
137
|
e2eRoutes: [
|
|
132
138
|
createGithubE2eRoutes({
|
|
133
139
|
getExistingGithubConnection,
|
package/src/providers/jira.ts
CHANGED
|
@@ -152,6 +152,9 @@ async function loadJiraModuleParts(
|
|
|
152
152
|
getExistingJiraConnection,
|
|
153
153
|
connectJiraInstallation,
|
|
154
154
|
disconnectJiraInstallation,
|
|
155
|
+
...(options.requireActiveWorkspaceMembership
|
|
156
|
+
? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}
|
|
157
|
+
: {}),
|
|
155
158
|
},
|
|
156
159
|
}),
|
|
157
160
|
database: {db: jiraDb, migrationsPath, migrationsTableName: JIRA_MIGRATIONS_TABLE},
|
package/src/providers/linear.ts
CHANGED
|
@@ -136,32 +136,38 @@ async function loadLinearModuleParts(
|
|
|
136
136
|
secrets,
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await deleteLinearInstallationByConnectionId(connection.id, {tx});
|
|
145
|
-
},
|
|
146
|
-
deleteConnectionSecrets: async (connection) => {
|
|
147
|
-
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
148
|
-
await (options.secrets?.linear?.deleteSecrets({
|
|
149
|
-
workspaceId: connection.workspaceId,
|
|
150
|
-
namespace: linearNamespaceSuffix(linearSecretsNamespace(connection.id)),
|
|
151
|
-
}) ?? Promise.resolve());
|
|
152
|
-
},
|
|
139
|
+
const integrationProvider = createLinearIntegrationProvider({
|
|
140
|
+
agentTools: {tokenStore, endpoint: linearConfig.LINEAR_MCP_ENDPOINT},
|
|
141
|
+
cleanup: {
|
|
142
|
+
deleteConnectionRecords: async (connection, {tx}) => {
|
|
143
|
+
await deleteLinearInstallationByConnectionId(connection.id, {tx});
|
|
153
144
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
recordDeliveryOnly,
|
|
161
|
-
getIntegrationConnectionById,
|
|
162
|
-
coreDb: db,
|
|
145
|
+
deleteConnectionSecrets: async (connection) => {
|
|
146
|
+
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
147
|
+
await (options.secrets?.linear?.deleteSecrets({
|
|
148
|
+
workspaceId: connection.workspaceId,
|
|
149
|
+
namespace: linearNamespaceSuffix(linearSecretsNamespace(connection.id)),
|
|
150
|
+
}) ?? Promise.resolve());
|
|
163
151
|
},
|
|
164
|
-
}
|
|
152
|
+
},
|
|
153
|
+
routes: {
|
|
154
|
+
tokenStore,
|
|
155
|
+
getExistingLinearConnection,
|
|
156
|
+
connectLinearInstallation,
|
|
157
|
+
disconnectLinearInstallation,
|
|
158
|
+
publishIntegrationEventReceived,
|
|
159
|
+
recordDeliveryOnly,
|
|
160
|
+
getIntegrationConnectionById,
|
|
161
|
+
coreDb: db,
|
|
162
|
+
...(options.requireActiveWorkspaceMembership
|
|
163
|
+
? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}
|
|
164
|
+
: {}),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
provider: integrationProvider,
|
|
170
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
165
171
|
e2eRoutes: [
|
|
166
172
|
createLinearE2eRoutes({
|
|
167
173
|
tokenStore,
|
package/src/providers/sentry.ts
CHANGED
|
@@ -84,19 +84,22 @@ async function loadSentryModuleParts(): Promise<IntegrationModuleParts> {
|
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
const integrationProvider = createSentryIntegrationProvider({
|
|
88
|
+
getSentryInstallation: ({installationUuid}) =>
|
|
89
|
+
getSentryInstallationByInstallationUuid(installationUuid),
|
|
90
|
+
getConnectionById,
|
|
91
|
+
connectSentryInstallation,
|
|
92
|
+
persistVerifiedUnclaimedInstallation,
|
|
93
|
+
coreDb: db,
|
|
94
|
+
publishIntegrationEventReceived,
|
|
95
|
+
recordDeliveryOnly,
|
|
96
|
+
getIntegrationConnectionById,
|
|
97
|
+
updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus,
|
|
98
|
+
});
|
|
99
|
+
|
|
87
100
|
return {
|
|
88
|
-
provider:
|
|
89
|
-
|
|
90
|
-
getSentryInstallationByInstallationUuid(installationUuid),
|
|
91
|
-
getConnectionById,
|
|
92
|
-
connectSentryInstallation,
|
|
93
|
-
persistVerifiedUnclaimedInstallation,
|
|
94
|
-
coreDb: db,
|
|
95
|
-
publishIntegrationEventReceived,
|
|
96
|
-
recordDeliveryOnly,
|
|
97
|
-
getIntegrationConnectionById,
|
|
98
|
-
updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus,
|
|
99
|
-
}),
|
|
101
|
+
provider: integrationProvider,
|
|
102
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
100
103
|
database: {
|
|
101
104
|
db: sentryDb,
|
|
102
105
|
migrationsPath: sentryMigrationsPath,
|
package/src/providers/slack.ts
CHANGED
|
@@ -138,33 +138,39 @@ async function loadSlackModuleParts(
|
|
|
138
138
|
secrets,
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
await deleteSlackInstallationByConnectionId(connection.id, {tx});
|
|
147
|
-
},
|
|
148
|
-
deleteConnectionSecrets: async (connection) => {
|
|
149
|
-
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
150
|
-
await (options.secrets?.slack?.deleteSecrets({
|
|
151
|
-
workspaceId: connection.workspaceId,
|
|
152
|
-
namespace: slackNamespaceSuffix(slackSecretsNamespace(connection.id)),
|
|
153
|
-
}) ?? Promise.resolve());
|
|
154
|
-
},
|
|
141
|
+
const integrationProvider = createSlackIntegrationProvider({
|
|
142
|
+
agentTools: {tokenStore},
|
|
143
|
+
cleanup: {
|
|
144
|
+
deleteConnectionRecords: async (connection, {tx}) => {
|
|
145
|
+
await deleteSlackInstallationByConnectionId(connection.id, {tx});
|
|
155
146
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
claimWebhookDelivery,
|
|
163
|
-
publishIntegrationEventReceived,
|
|
164
|
-
recordDeliveryOnly,
|
|
165
|
-
getIntegrationConnectionById,
|
|
147
|
+
deleteConnectionSecrets: async (connection) => {
|
|
148
|
+
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
149
|
+
await (options.secrets?.slack?.deleteSecrets({
|
|
150
|
+
workspaceId: connection.workspaceId,
|
|
151
|
+
namespace: slackNamespaceSuffix(slackSecretsNamespace(connection.id)),
|
|
152
|
+
}) ?? Promise.resolve());
|
|
166
153
|
},
|
|
167
|
-
}
|
|
154
|
+
},
|
|
155
|
+
routes: {
|
|
156
|
+
tokenStore,
|
|
157
|
+
getExistingSlackConnection,
|
|
158
|
+
connectSlackInstallation,
|
|
159
|
+
disconnectSlackInstallation,
|
|
160
|
+
coreDb: db,
|
|
161
|
+
claimWebhookDelivery,
|
|
162
|
+
publishIntegrationEventReceived,
|
|
163
|
+
recordDeliveryOnly,
|
|
164
|
+
getIntegrationConnectionById,
|
|
165
|
+
...(options.requireActiveWorkspaceMembership
|
|
166
|
+
? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}
|
|
167
|
+
: {}),
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
provider: integrationProvider,
|
|
173
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
168
174
|
e2eRoutes: [
|
|
169
175
|
createSlackE2eRoutes({
|
|
170
176
|
tokenStore,
|
package/src/providers/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type {UserContextMembership} from '@shipfox/api-auth-context';
|
|
2
|
+
import type {WebhookRequestProcessor, WebhookRouteId} from '@shipfox/api-integration-core-dto';
|
|
1
3
|
import type {RouteExport} from '@shipfox/node-fastify';
|
|
2
4
|
import type {ModuleDatabase, ModuleWorker} from '@shipfox/node-module';
|
|
3
5
|
import type {IntegrationProvider} from '#core/entities/provider.js';
|
|
@@ -17,6 +19,12 @@ export interface IntegrationModuleParts {
|
|
|
17
19
|
e2eRoutes?: RouteExport[] | undefined;
|
|
18
20
|
workers?: ModuleWorker[] | undefined;
|
|
19
21
|
startupTasks?: Array<() => Promise<void>> | undefined;
|
|
22
|
+
webhookProcessors?: WebhookProcessorRegistration[] | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface WebhookProcessorRegistration {
|
|
26
|
+
routeIds: readonly WebhookRouteId[];
|
|
27
|
+
processor: WebhookRequestProcessor;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
/**
|
|
@@ -32,6 +40,13 @@ export interface IntegrationProviderModule {
|
|
|
32
40
|
|
|
33
41
|
export interface IntegrationProviderModuleLoadOptions {
|
|
34
42
|
secrets?: IntegrationProviderSecrets | undefined;
|
|
43
|
+
requireActiveWorkspaceMembership?:
|
|
44
|
+
| ((input: {
|
|
45
|
+
workspaceId: string;
|
|
46
|
+
userId: string;
|
|
47
|
+
memberships: ReadonlyArray<UserContextMembership>;
|
|
48
|
+
}) => Promise<unknown>)
|
|
49
|
+
| undefined;
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
export interface IntegrationProviderSecrets {
|
package/src/providers/webhook.ts
CHANGED
|
@@ -15,16 +15,18 @@ export const webhookProviderModule: IntegrationProviderModule = {
|
|
|
15
15
|
enabled: config.INTEGRATIONS_ENABLE_WEBHOOK_PROVIDER,
|
|
16
16
|
load: async () => {
|
|
17
17
|
const {createWebhookIntegrationProvider} = await import('@shipfox/api-integration-webhook');
|
|
18
|
+
const integrationProvider = createWebhookIntegrationProvider({
|
|
19
|
+
coreDb: db,
|
|
20
|
+
createIntegrationConnection,
|
|
21
|
+
listIntegrationConnections,
|
|
22
|
+
getIntegrationConnectionById,
|
|
23
|
+
updateIntegrationConnectionLifecycleStatus,
|
|
24
|
+
deleteIntegrationConnection,
|
|
25
|
+
publishIntegrationEventReceived,
|
|
26
|
+
});
|
|
18
27
|
return {
|
|
19
|
-
provider:
|
|
20
|
-
|
|
21
|
-
createIntegrationConnection,
|
|
22
|
-
listIntegrationConnections,
|
|
23
|
-
getIntegrationConnectionById,
|
|
24
|
-
updateIntegrationConnectionLifecycleStatus,
|
|
25
|
-
deleteIntegrationConnection,
|
|
26
|
-
publishIntegrationEventReceived,
|
|
27
|
-
}),
|
|
28
|
+
provider: integrationProvider,
|
|
29
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
28
30
|
};
|
|
29
31
|
},
|
|
30
32
|
};
|