@friggframework/core 2.0.0--canary.707.df80bfd.0 → 2.0.0--canary.680.d670696.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/CHANGELOG.md +3 -0
- package/CLAUDE.md +0 -28
- package/README.md +0 -105
- package/application/commands/integration-commands.js +2 -2
- package/application/commands/scheduler-commands.js +7 -11
- package/application/commands/user-commands.js +5 -17
- package/core/Worker.js +7 -31
- package/core/create-handler.js +0 -9
- package/credential/use-cases/credential-operations-for-user.js +25 -110
- package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
- package/database/config.js +24 -48
- package/database/documentdb-encryption-service.js +0 -1
- package/database/documentdb-utils.js +1 -6
- package/database/encryption/encryption-config.js +44 -13
- package/database/encryption/encryption-schema-registry.js +31 -88
- package/database/prisma.js +2 -4
- package/generated/prisma-mongodb/edge.js +4 -27
- package/generated/prisma-mongodb/index-browser.js +1 -24
- package/generated/prisma-mongodb/index.d.ts +48 -3228
- package/generated/prisma-mongodb/index.js +4 -27
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -65
- package/generated/prisma-mongodb/wasm.js +4 -27
- package/generated/prisma-postgresql/edge.js +4 -27
- package/generated/prisma-postgresql/index-browser.js +1 -24
- package/generated/prisma-postgresql/index.d.ts +51 -3503
- package/generated/prisma-postgresql/index.js +4 -27
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -61
- package/generated/prisma-postgresql/wasm.js +4 -27
- package/handlers/app-definition-loader.js +12 -110
- package/handlers/routers/auth.js +12 -40
- package/handlers/routers/health.js +2 -4
- package/handlers/routers/integration-defined-routers.js +92 -144
- package/handlers/routers/integration-webhook-routers.js +64 -102
- package/index.js +1 -64
- package/infrastructure/scheduler/index.js +6 -19
- package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
- package/integrations/index.js +0 -5
- package/integrations/integration-router.js +73 -115
- package/integrations/repositories/integration-repository-documentdb.js +3 -35
- package/integrations/repositories/integration-repository-interface.js +1 -17
- package/integrations/repositories/integration-repository-mongo.js +76 -59
- package/integrations/repositories/integration-repository-postgres.js +76 -62
- package/integrations/tests/doubles/test-integration-repository.js +1 -12
- package/integrations/use-cases/create-integration.js +20 -55
- package/integrations/use-cases/get-integrations-for-user.js +40 -80
- package/integrations/use-cases/index.js +1 -7
- package/integrations/use-cases/integration-operations-for-user.js +24 -55
- package/integrations/utils/map-integration-dto.js +1 -44
- package/management-api/management-api-config.js +2 -71
- package/management-api/meta-router.js +2 -21
- package/management-api/openapi/components.js +3 -50
- package/management-api/openapi/v2-operations.js +5 -17
- package/management-api/route-registry.js +6 -123
- package/management-api/v2/dependencies.js +15 -40
- package/management-api/v2/dto.js +0 -20
- package/management-api/v2/handlers/authorization.js +0 -1
- package/management-api/v2/handlers/credentials.js +2 -6
- package/management-api/v2/handlers/entities.js +7 -10
- package/management-api/v2/handlers/integrations.js +3 -6
- package/management-api/v2/request.js +1 -32
- package/modules/index.js +0 -8
- package/modules/module-factory.js +0 -12
- package/modules/module.js +2 -53
- package/modules/proxy/proxy-errors.js +1 -12
- package/modules/proxy/proxy-path.js +3 -7
- package/modules/proxy/proxy-policy.js +5 -34
- package/modules/repositories/module-repository-documentdb.js +6 -51
- package/modules/repositories/module-repository-interface.js +2 -19
- package/modules/repositories/module-repository-mongo.js +74 -67
- package/modules/repositories/module-repository-postgres.js +74 -66
- package/modules/repositories/module-repository.js +1 -2
- package/modules/requester/proxy-request.js +1 -2
- package/modules/requester/requester.js +3 -8
- package/modules/use-cases/entity-operations-for-user.js +13 -50
- package/modules/use-cases/entity-types.js +7 -31
- package/modules/use-cases/execute-entity-proxy-request.js +15 -107
- package/modules/use-cases/get-authorization-requirements.js +2 -2
- package/modules/use-cases/get-entities-for-user.js +13 -26
- package/modules/use-cases/get-entity-module-for-user.js +7 -34
- package/modules/use-cases/get-entity-options-by-id.js +4 -10
- package/modules/use-cases/get-entity-options-by-type.js +1 -1
- package/modules/use-cases/get-module-instance-from-type.js +1 -1
- package/modules/use-cases/get-module.js +4 -10
- package/modules/use-cases/multi-step-authorization.js +13 -40
- package/modules/use-cases/process-authorization-callback.js +19 -53
- package/modules/use-cases/refresh-entity-options.js +4 -10
- package/modules/use-cases/test-module-auth.js +4 -10
- package/package.json +5 -6
- package/prisma-mongodb/schema.prisma +0 -65
- package/prisma-postgresql/schema.prisma +0 -61
- package/queues/index.js +0 -25
- package/types/index.d.ts +0 -1
- package/types/modules/index.d.ts +1 -157
- package/user/repositories/user-repository-documentdb.js +2 -27
- package/user/repositories/user-repository-interface.js +0 -18
- package/user/repositories/user-repository-mongo.js +0 -24
- package/user/repositories/user-repository-postgres.js +0 -18
- package/user/use-cases/authenticate-user.js +1 -7
- package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
- package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
- package/user/user.js +1 -64
- package/database/organization-where.js +0 -18
- package/database/unique-violation.js +0 -25
- package/extensions/bootstrap-runner.js +0 -43
- package/extensions/extension-loader.js +0 -230
- package/extensions/index.js +0 -44
- package/extensions/initialize-app.js +0 -123
- package/extensions/prisma-composer.js +0 -270
- package/extensions/route-mounter.js +0 -103
- package/extensions/schema-composer.js +0 -188
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
- package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
- package/integrations/repositories/integration-not-found.js +0 -14
- package/integrations/schedule-config.js +0 -80
- package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/modules/entity-scope.js +0 -15
- package/modules/proxy/proxy-bases.js +0 -169
- package/modules/repositories/entity-not-found.js +0 -18
- package/modules/scope-config.js +0 -192
- package/modules/scope-policy.js +0 -112
- package/modules/scope-resolution.js +0 -288
- package/modules/scope-visibility.js +0 -247
- package/modules/tests/doubles/ownership-matching.js +0 -18
- package/modules/use-cases/authorize-entity-for-user.js +0 -61
- package/modules/use-cases/create-entity-from-credential.js +0 -113
- package/modules/use-cases/manage-access.js +0 -21
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
- package/providers/index.js +0 -23
- package/providers/resolve-provider.js +0 -93
- package/providers/runtime-detection.js +0 -124
- package/queues/providers/index.js +0 -11
- package/queues/providers/netlify-background-provider.js +0 -150
- package/queues/providers/qstash-queue-provider.js +0 -219
- package/queues/providers/sqs-queue-provider.js +0 -70
- package/queues/queue-client-interface.js +0 -55
- package/queues/queue-message-auth.js +0 -193
- package/queues/queue-provider-factory.js +0 -101
- package/queues/queue-provider.js +0 -65
- package/queues/sqs-queue-client.js +0 -81
- package/types/user/index.d.ts +0 -134
- package/user/adopter-jwt-config.js +0 -177
- package/user/adopter-jwt-errors.js +0 -59
- package/user/adopter-token-verifier.js +0 -122
- package/user/app-user-helpers.js +0 -97
- package/user/create-user-authentication.js +0 -64
- package/user/membership.js +0 -51
- package/user/repositories/organization-membership-record.js +0 -19
- package/user/repositories/organization-membership-repository-documentdb.js +0 -106
- package/user/repositories/organization-membership-repository-factory.js +0 -42
- package/user/repositories/organization-membership-repository-interface.js +0 -57
- package/user/repositories/organization-membership-repository-mongo.js +0 -13
- package/user/repositories/organization-membership-repository-postgres.js +0 -29
- package/user/repositories/organization-membership-repository-prisma.js +0 -81
- package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
- package/user/use-cases/ensure-organization-membership.js +0 -92
- package/user/use-cases/find-individual-users-by-organization.js +0 -54
|
@@ -1,169 +1,117 @@
|
|
|
1
1
|
const { createAppHandler } = require('./../app-handler-helpers');
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
loadAppDefinition,
|
|
4
|
+
} = require('../app-definition-loader');
|
|
3
5
|
const express = require('express');
|
|
4
6
|
const { Router } = express;
|
|
5
7
|
const { loadRouterFromObject } = require('../backend-utils');
|
|
6
8
|
const { getExtensionRoutes } = require('../../integrations/extension');
|
|
7
9
|
|
|
10
|
+
const handlers = {};
|
|
11
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
12
|
+
|
|
8
13
|
const routeKey = (method, path) => `${(method || 'ANY').toUpperCase()} ${path}`;
|
|
9
14
|
|
|
10
15
|
// Serverless function keys must be alphanumeric; binding keys are developer-chosen.
|
|
11
16
|
const sanitizeBindingKey = (name) => String(name).replace(/[^A-Za-z0-9]/g, '');
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//todo: this should be in a use case class
|
|
28
|
-
for (const IntegrationClass of integrationClasses) {
|
|
29
|
-
const router = Router();
|
|
30
|
-
const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
|
|
31
|
-
// Track (method, path) tuples to fail fast on conflicts between Definition.routes,
|
|
32
|
-
// extension routes, or two extensions claiming the same path.
|
|
33
|
-
const claimedRoutes = new Map();
|
|
34
|
-
const claim = (method, path, source) => {
|
|
35
|
-
const key = routeKey(method, path);
|
|
36
|
-
if (claimedRoutes.has(key)) {
|
|
37
|
-
const prev = claimedRoutes.get(key);
|
|
38
|
-
throw new Error(
|
|
39
|
-
`Integration "${IntegrationClass.Definition.name}" route conflict: ` +
|
|
40
|
-
`${key} declared by ${prev} and ${source}`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
claimedRoutes.set(key, source);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
console.log(
|
|
47
|
-
`\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
const routes = IntegrationClass.Definition.routes || [];
|
|
51
|
-
for (const routeDef of routes) {
|
|
52
|
-
if (typeof routeDef === 'function') {
|
|
53
|
-
router.use(basePath, routeDef(IntegrationClass));
|
|
54
|
-
console.log(`│ ANY ${basePath}/* (function handler)`);
|
|
55
|
-
} else if (routeDef instanceof express.Router) {
|
|
56
|
-
router.use(basePath, routeDef);
|
|
57
|
-
console.log(`│ ANY ${basePath}/* (express router)`);
|
|
58
|
-
} else if (typeof routeDef === 'object') {
|
|
59
|
-
claim(routeDef.method, routeDef.path, 'Definition.routes');
|
|
60
|
-
router.use(
|
|
61
|
-
basePath,
|
|
62
|
-
loadRouterFromObject(IntegrationClass, routeDef)
|
|
63
|
-
);
|
|
64
|
-
const method = (routeDef.method || 'ANY').toUpperCase();
|
|
65
|
-
const fullPath = `${basePath}${routeDef.path}`;
|
|
66
|
-
console.log(`│ ${method} ${fullPath}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Each extension binding gets its own namespaced handler (/{bindingName}),
|
|
71
|
-
// so two modules' extensions can share a path like /webhooks without colliding.
|
|
72
|
-
const bindingGroups = new Map();
|
|
73
|
-
for (const extRoute of getExtensionRoutes(IntegrationClass)) {
|
|
74
|
-
const namespacedPath = `/${extRoute.bindingName}${extRoute.path}`;
|
|
75
|
-
claim(
|
|
76
|
-
extRoute.method,
|
|
77
|
-
namespacedPath,
|
|
78
|
-
`extension "${extRoute.extensionName}" (binding "${extRoute.bindingName}")`
|
|
79
|
-
);
|
|
80
|
-
if (!bindingGroups.has(extRoute.bindingName)) {
|
|
81
|
-
bindingGroups.set(extRoute.bindingName, {
|
|
82
|
-
router: Router(),
|
|
83
|
-
useDatabase: extRoute.useDatabase,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
const group = bindingGroups.get(extRoute.bindingName);
|
|
87
|
-
group.router.use(
|
|
88
|
-
`${basePath}/${extRoute.bindingName}`,
|
|
89
|
-
loadRouterFromObject(IntegrationClass, extRoute)
|
|
90
|
-
);
|
|
91
|
-
console.log(
|
|
92
|
-
`│ ${extRoute.method.toUpperCase()} ${basePath}/${
|
|
93
|
-
extRoute.bindingName
|
|
94
|
-
}${extRoute.path} (extension: ${
|
|
95
|
-
extRoute.extensionName
|
|
96
|
-
}, useDatabase: ${extRoute.useDatabase})`
|
|
18
|
+
//todo: this should be in a use case class
|
|
19
|
+
for (const IntegrationClass of integrationClasses) {
|
|
20
|
+
const router = Router();
|
|
21
|
+
const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
|
|
22
|
+
// Track (method, path) tuples to fail fast on conflicts between Definition.routes,
|
|
23
|
+
// extension routes, or two extensions claiming the same path.
|
|
24
|
+
const claimedRoutes = new Map();
|
|
25
|
+
const claim = (method, path, source) => {
|
|
26
|
+
const key = routeKey(method, path);
|
|
27
|
+
if (claimedRoutes.has(key)) {
|
|
28
|
+
const prev = claimedRoutes.get(key);
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Integration "${IntegrationClass.Definition.name}" route conflict: ` +
|
|
31
|
+
`${key} declared by ${prev} and ${source}`
|
|
97
32
|
);
|
|
98
33
|
}
|
|
99
|
-
|
|
34
|
+
claimedRoutes.set(key, source);
|
|
35
|
+
};
|
|
100
36
|
|
|
101
|
-
|
|
102
|
-
key: `${IntegrationClass.Definition.name}`,
|
|
103
|
-
eventName: `HTTP Event: ${IntegrationClass.Definition.name}`,
|
|
104
|
-
router,
|
|
105
|
-
useDatabase: true,
|
|
106
|
-
});
|
|
107
|
-
keys.add(`${IntegrationClass.Definition.name}`);
|
|
37
|
+
console.log(`\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`);
|
|
108
38
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
router: group.router,
|
|
127
|
-
useDatabase: group.useDatabase,
|
|
128
|
-
});
|
|
129
|
-
keys.add(fnKey);
|
|
39
|
+
const routes = IntegrationClass.Definition.routes || [];
|
|
40
|
+
for (const routeDef of routes) {
|
|
41
|
+
if (typeof routeDef === 'function') {
|
|
42
|
+
router.use(basePath, routeDef(IntegrationClass));
|
|
43
|
+
console.log(`│ ANY ${basePath}/* (function handler)`);
|
|
44
|
+
} else if (routeDef instanceof express.Router) {
|
|
45
|
+
router.use(basePath, routeDef);
|
|
46
|
+
console.log(`│ ANY ${basePath}/* (express router)`);
|
|
47
|
+
} else if (typeof routeDef === 'object') {
|
|
48
|
+
claim(routeDef.method, routeDef.path, 'Definition.routes');
|
|
49
|
+
router.use(
|
|
50
|
+
basePath,
|
|
51
|
+
loadRouterFromObject(IntegrationClass, routeDef)
|
|
52
|
+
);
|
|
53
|
+
const method = (routeDef.method || 'ANY').toUpperCase();
|
|
54
|
+
const fullPath = `${basePath}${routeDef.path}`;
|
|
55
|
+
console.log(`│ ${method} ${fullPath}`);
|
|
130
56
|
}
|
|
131
57
|
}
|
|
132
|
-
return entries;
|
|
133
|
-
}
|
|
134
58
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
59
|
+
// Each extension binding gets its own namespaced handler (/{bindingName}),
|
|
60
|
+
// so two modules' extensions can share a path like /webhooks without colliding.
|
|
61
|
+
const bindingGroups = new Map();
|
|
62
|
+
for (const extRoute of getExtensionRoutes(IntegrationClass)) {
|
|
63
|
+
const namespacedPath = `/${extRoute.bindingName}${extRoute.path}`;
|
|
64
|
+
claim(
|
|
65
|
+
extRoute.method,
|
|
66
|
+
namespacedPath,
|
|
67
|
+
`extension "${extRoute.extensionName}" (binding "${extRoute.bindingName}")`
|
|
68
|
+
);
|
|
69
|
+
if (!bindingGroups.has(extRoute.bindingName)) {
|
|
70
|
+
bindingGroups.set(extRoute.bindingName, {
|
|
71
|
+
router: Router(),
|
|
72
|
+
useDatabase: extRoute.useDatabase,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const group = bindingGroups.get(extRoute.bindingName);
|
|
76
|
+
group.router.use(
|
|
77
|
+
`${basePath}/${extRoute.bindingName}`,
|
|
78
|
+
loadRouterFromObject(IntegrationClass, extRoute)
|
|
79
|
+
);
|
|
80
|
+
console.log(
|
|
81
|
+
`│ ${extRoute.method.toUpperCase()} ${basePath}/${extRoute.bindingName}${extRoute.path} (extension: ${extRoute.extensionName}, useDatabase: ${extRoute.useDatabase})`
|
|
82
|
+
);
|
|
147
83
|
}
|
|
148
|
-
|
|
149
|
-
}
|
|
84
|
+
console.log('│');
|
|
150
85
|
|
|
151
|
-
|
|
152
|
-
// see devtools integration-builder). Built at require time on purpose: a
|
|
153
|
-
// route conflict fails the cold start, not the first request.
|
|
154
|
-
const handlers = {};
|
|
155
|
-
for (const entry of buildIntegrationRouters(loadAppDefinition().integrations)) {
|
|
156
|
-
handlers[entry.key] = {
|
|
86
|
+
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
157
87
|
handler: createAppHandler(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
entry.useDatabase
|
|
88
|
+
`HTTP Event: ${IntegrationClass.Definition.name}`,
|
|
89
|
+
router
|
|
161
90
|
),
|
|
162
91
|
};
|
|
92
|
+
|
|
93
|
+
for (const [bindingName, group] of bindingGroups) {
|
|
94
|
+
// Wire contract: integration-builder.js (devtools) derives the identical
|
|
95
|
+
// function key for the serverless config. Keep both in sync.
|
|
96
|
+
const fnKey = `${IntegrationClass.Definition.name}__${sanitizeBindingKey(
|
|
97
|
+
bindingName
|
|
98
|
+
)}`;
|
|
99
|
+
// Distinct binding keys can sanitize to the same fnKey — fail loud rather than overwrite.
|
|
100
|
+
if (Object.prototype.hasOwnProperty.call(handlers, fnKey)) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Integration "${IntegrationClass.Definition.name}" extension handler conflict: ` +
|
|
103
|
+
`binding "${bindingName}" sanitizes to "${fnKey}", which is already taken. ` +
|
|
104
|
+
`Use binding keys that are distinct after stripping non-alphanumeric characters.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
handlers[fnKey] = {
|
|
108
|
+
handler: createAppHandler(
|
|
109
|
+
`HTTP Event: ${IntegrationClass.Definition.name} extension ${bindingName}`,
|
|
110
|
+
group.router,
|
|
111
|
+
group.useDatabase
|
|
112
|
+
),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
163
115
|
}
|
|
164
116
|
|
|
165
|
-
module.exports = {
|
|
166
|
-
handlers,
|
|
167
|
-
buildIntegrationRouters,
|
|
168
|
-
createIntegrationDefinedRouter,
|
|
169
|
-
};
|
|
117
|
+
module.exports = { handlers };
|
|
@@ -6,120 +6,82 @@ const {
|
|
|
6
6
|
IntegrationEventDispatcher,
|
|
7
7
|
} = require('../integration-event-dispatcher');
|
|
8
8
|
|
|
9
|
+
const handlers = {};
|
|
10
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
11
|
+
|
|
9
12
|
// `webhooks.received` is counted at the DB-connected ON_WEBHOOK dispatch (see
|
|
10
13
|
// usage-rollup-subscriber), not in this DB-free receipt handler.
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* enabled. Pure: no handler wrapping, so the AWS handlers below and other
|
|
15
|
-
* hosts (provider-netlify's webhooks function) mount the same routers.
|
|
16
|
-
*
|
|
17
|
-
* @param {Array<Function>} integrationClasses
|
|
18
|
-
* @returns {Array<{ key: string, eventName: string, router: import('express').Router }>}
|
|
19
|
-
*/
|
|
20
|
-
function buildWebhookRouters(integrationClasses) {
|
|
21
|
-
const entries = [];
|
|
22
|
-
for (const IntegrationClass of integrationClasses) {
|
|
23
|
-
const webhookConfig = IntegrationClass.Definition.webhooks;
|
|
24
|
-
|
|
25
|
-
// Skip if webhooks not enabled
|
|
26
|
-
if (
|
|
27
|
-
!webhookConfig ||
|
|
28
|
-
(typeof webhookConfig === 'object' && !webhookConfig.enabled)
|
|
29
|
-
) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const router = Router();
|
|
34
|
-
const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
|
|
15
|
+
for (const IntegrationClass of integrationClasses) {
|
|
16
|
+
const webhookConfig = IntegrationClass.Definition.webhooks;
|
|
35
17
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
// Skip if webhooks not enabled
|
|
19
|
+
if (
|
|
20
|
+
!webhookConfig ||
|
|
21
|
+
(typeof webhookConfig === 'object' && !webhookConfig.enabled)
|
|
22
|
+
) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const integrationInstance = new IntegrationClass();
|
|
44
|
-
const dispatcher = new IntegrationEventDispatcher(
|
|
45
|
-
integrationInstance
|
|
46
|
-
);
|
|
47
|
-
await dispatcher.dispatchHttp({
|
|
48
|
-
event: 'WEBHOOK_RECEIVED',
|
|
49
|
-
req,
|
|
50
|
-
res,
|
|
51
|
-
next,
|
|
52
|
-
});
|
|
53
|
-
} catch (error) {
|
|
54
|
-
next(error);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
console.log(`│ POST ${basePath}`);
|
|
26
|
+
const router = Router();
|
|
27
|
+
const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
|
|
58
28
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const integrationInstance = new IntegrationClass();
|
|
63
|
-
const dispatcher = new IntegrationEventDispatcher(
|
|
64
|
-
integrationInstance
|
|
65
|
-
);
|
|
66
|
-
// Logs only: the id comes from an unauthenticated URL, so it must
|
|
67
|
-
// not reach the telemetry context that drives usage attribution.
|
|
68
|
-
await runInContext(
|
|
69
|
-
{
|
|
70
|
-
[LOGGER_SCOPE_KEY]: {
|
|
71
|
-
integrationId: req.params.integrationId,
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
() =>
|
|
75
|
-
dispatcher.dispatchHttp({
|
|
76
|
-
event: 'WEBHOOK_RECEIVED',
|
|
77
|
-
req,
|
|
78
|
-
res,
|
|
79
|
-
next,
|
|
80
|
-
})
|
|
81
|
-
);
|
|
82
|
-
} catch (error) {
|
|
83
|
-
next(error);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
console.log(`│ POST ${basePath}/:integrationId`);
|
|
87
|
-
console.log('│');
|
|
29
|
+
console.log(
|
|
30
|
+
`\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`
|
|
31
|
+
);
|
|
88
32
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
33
|
+
// General webhook route (no integration ID)
|
|
34
|
+
router.post(basePath, async (req, res, next) => {
|
|
35
|
+
try {
|
|
36
|
+
const integrationInstance = new IntegrationClass();
|
|
37
|
+
const dispatcher = new IntegrationEventDispatcher(
|
|
38
|
+
integrationInstance
|
|
39
|
+
);
|
|
40
|
+
await dispatcher.dispatchHttp({
|
|
41
|
+
event: 'WEBHOOK_RECEIVED',
|
|
42
|
+
req,
|
|
43
|
+
res,
|
|
44
|
+
next,
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
next(error);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
console.log(`│ POST ${basePath}`);
|
|
97
51
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
52
|
+
// Integration-specific webhook route (with integration ID)
|
|
53
|
+
router.post(`${basePath}/:integrationId`, async (req, res, next) => {
|
|
54
|
+
try {
|
|
55
|
+
const integrationInstance = new IntegrationClass();
|
|
56
|
+
const dispatcher = new IntegrationEventDispatcher(
|
|
57
|
+
integrationInstance
|
|
58
|
+
);
|
|
59
|
+
// Logs only: the id comes from an unauthenticated URL, so it must
|
|
60
|
+
// not reach the telemetry context that drives usage attribution.
|
|
61
|
+
await runInContext(
|
|
62
|
+
{ [LOGGER_SCOPE_KEY]: { integrationId: req.params.integrationId } },
|
|
63
|
+
() =>
|
|
64
|
+
dispatcher.dispatchHttp({
|
|
65
|
+
event: 'WEBHOOK_RECEIVED',
|
|
66
|
+
req,
|
|
67
|
+
res,
|
|
68
|
+
next,
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
next(error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
console.log(`│ POST ${basePath}/:integrationId`);
|
|
76
|
+
console.log('│');
|
|
113
77
|
|
|
114
|
-
|
|
115
|
-
for (const entry of buildWebhookRouters(loadAppDefinition().integrations)) {
|
|
116
|
-
handlers[entry.key] = {
|
|
78
|
+
handlers[`${IntegrationClass.Definition.name}Webhook`] = {
|
|
117
79
|
handler: createAppHandler(
|
|
118
|
-
|
|
119
|
-
|
|
80
|
+
`HTTP Event: ${IntegrationClass.Definition.name} Webhook`,
|
|
81
|
+
router,
|
|
120
82
|
false // shouldUseDatabase = false
|
|
121
83
|
),
|
|
122
84
|
};
|
|
123
85
|
}
|
|
124
86
|
|
|
125
|
-
module.exports = { handlers
|
|
87
|
+
module.exports = { handlers };
|
package/index.js
CHANGED
|
@@ -25,14 +25,6 @@ const {
|
|
|
25
25
|
UserRepositoryMongo,
|
|
26
26
|
UserRepositoryPostgres,
|
|
27
27
|
} = require('./user/repositories/user-repository-factory');
|
|
28
|
-
const {
|
|
29
|
-
createOrganizationMembershipRepository,
|
|
30
|
-
OrganizationMembershipRepositoryMongo,
|
|
31
|
-
OrganizationMembershipRepositoryPostgres,
|
|
32
|
-
} = require('./user/repositories/organization-membership-repository-factory');
|
|
33
|
-
const {
|
|
34
|
-
EnsureOrganizationMembership,
|
|
35
|
-
} = require('./user/use-cases/ensure-organization-membership');
|
|
36
28
|
const {
|
|
37
29
|
GetUserFromXFriggHeaders,
|
|
38
30
|
} = require('./user/use-cases/get-user-from-x-frigg-headers');
|
|
@@ -71,7 +63,6 @@ const {
|
|
|
71
63
|
Options,
|
|
72
64
|
createIntegrationRouter,
|
|
73
65
|
checkRequiredParams,
|
|
74
|
-
getModulesDefinitionFromIntegrationClasses,
|
|
75
66
|
LoadIntegrationContextUseCase,
|
|
76
67
|
} = require('./integrations/index');
|
|
77
68
|
const {
|
|
@@ -109,34 +100,11 @@ const {
|
|
|
109
100
|
ModuleFactory,
|
|
110
101
|
getEntityTypeRegistry,
|
|
111
102
|
EntityRegistryError,
|
|
112
|
-
resolveScope,
|
|
113
|
-
listBindingScopes,
|
|
114
|
-
OwnershipConfigError,
|
|
115
103
|
} = require('./modules/index');
|
|
116
|
-
const {
|
|
117
|
-
ManagementApiConfigError,
|
|
118
|
-
} = require('./management-api/management-api-config');
|
|
119
104
|
const application = require('./application');
|
|
120
105
|
const utils = require('./utils');
|
|
121
106
|
|
|
122
|
-
const {
|
|
123
|
-
QueuerUtil,
|
|
124
|
-
QueueProvider,
|
|
125
|
-
QueueClientInterface,
|
|
126
|
-
createQueueProvider,
|
|
127
|
-
QUEUE_PROVIDERS,
|
|
128
|
-
} = require('./queues');
|
|
129
|
-
const {
|
|
130
|
-
resolveProvider,
|
|
131
|
-
determineProviderName,
|
|
132
|
-
providerPackageName,
|
|
133
|
-
KNOWN_PROVIDERS,
|
|
134
|
-
isDeployedRuntime,
|
|
135
|
-
} = require('./providers');
|
|
136
|
-
|
|
137
|
-
// App-level core extensions (appDefinition.coreExtensions): Prisma models, encrypted
|
|
138
|
-
// fields, admin routes and bootstrap hooks (friggframework/frigg#545).
|
|
139
|
-
const extensions = require('./extensions');
|
|
107
|
+
const { QueuerUtil } = require('./queues');
|
|
140
108
|
|
|
141
109
|
module.exports = {
|
|
142
110
|
// assertions
|
|
@@ -163,10 +131,6 @@ module.exports = {
|
|
|
163
131
|
createUserRepository,
|
|
164
132
|
UserRepositoryMongo,
|
|
165
133
|
UserRepositoryPostgres,
|
|
166
|
-
createOrganizationMembershipRepository,
|
|
167
|
-
OrganizationMembershipRepositoryMongo,
|
|
168
|
-
OrganizationMembershipRepositoryPostgres,
|
|
169
|
-
EnsureOrganizationMembership,
|
|
170
134
|
GetUserFromXFriggHeaders,
|
|
171
135
|
GetUserFromAdopterJwt,
|
|
172
136
|
AuthenticateUser,
|
|
@@ -187,8 +151,6 @@ module.exports = {
|
|
|
187
151
|
Options,
|
|
188
152
|
checkRequiredParams,
|
|
189
153
|
createIntegrationRouter,
|
|
190
|
-
// Deprecated (ADR-059): use getEntityTypeRegistry.
|
|
191
|
-
getModulesDefinitionFromIntegrationClasses,
|
|
192
154
|
LoadIntegrationContextUseCase,
|
|
193
155
|
CreateProcess,
|
|
194
156
|
UpdateProcessState,
|
|
@@ -246,33 +208,8 @@ module.exports = {
|
|
|
246
208
|
ModuleFactory,
|
|
247
209
|
getEntityTypeRegistry,
|
|
248
210
|
EntityRegistryError,
|
|
249
|
-
resolveScope,
|
|
250
|
-
listBindingScopes,
|
|
251
|
-
OwnershipConfigError,
|
|
252
|
-
ManagementApiConfigError,
|
|
253
211
|
// queues
|
|
254
212
|
QueuerUtil,
|
|
255
|
-
QueueProvider,
|
|
256
|
-
QueueClientInterface,
|
|
257
|
-
createQueueProvider,
|
|
258
|
-
QUEUE_PROVIDERS,
|
|
259
|
-
|
|
260
|
-
// provider plugins (appDefinition.provider)
|
|
261
|
-
resolveProvider,
|
|
262
|
-
determineProviderName,
|
|
263
|
-
providerPackageName,
|
|
264
|
-
KNOWN_PROVIDERS,
|
|
265
|
-
isDeployedRuntime,
|
|
266
|
-
|
|
267
|
-
// app-level extensions
|
|
268
|
-
extensions,
|
|
269
|
-
loadExtensions: extensions.loadExtensions,
|
|
270
|
-
composeSchemas: extensions.composeSchemas,
|
|
271
|
-
composeExtensionPrisma: extensions.composeExtensionPrisma,
|
|
272
|
-
mountExtensionRoutes: extensions.mountExtensionRoutes,
|
|
273
|
-
createExtensionRoutesRouter: extensions.createExtensionRoutesRouter,
|
|
274
|
-
runExtensionBootstraps: extensions.runExtensionBootstraps,
|
|
275
|
-
initializeApp: extensions.initializeApp,
|
|
276
213
|
|
|
277
214
|
// utils
|
|
278
215
|
...utils,
|
|
@@ -5,19 +5,13 @@
|
|
|
5
5
|
* Follows hexagonal architecture with interface + adapters pattern.
|
|
6
6
|
*
|
|
7
7
|
* Providers:
|
|
8
|
-
* - eventbridge: AWS EventBridge Scheduler (production
|
|
9
|
-
* - netlify: poll-and-dispatch from a Netlify scheduled function
|
|
8
|
+
* - eventbridge: AWS EventBridge Scheduler (production)
|
|
10
9
|
* - mock: In-memory mock scheduler (local development)
|
|
11
|
-
*
|
|
12
|
-
* Adapters are lazy getters (friggframework/frigg#545, 1176a004): the
|
|
13
|
-
* EventBridge adapter requires @aws-sdk/client-scheduler, which is not a
|
|
14
|
-
* core dependency, and core/index -> application -> scheduler-commands ->
|
|
15
|
-
* this barrel runs on every require('@friggframework/core'). An eager
|
|
16
|
-
* require crashed any deployment without that SDK (e.g. a pruned Netlify
|
|
17
|
-
* function bundle).
|
|
18
10
|
*/
|
|
19
11
|
|
|
20
12
|
const { SchedulerServiceInterface } = require('./scheduler-service-interface');
|
|
13
|
+
const { EventBridgeSchedulerAdapter } = require('./eventbridge-scheduler-adapter');
|
|
14
|
+
const { MockSchedulerAdapter } = require('./mock-scheduler-adapter');
|
|
21
15
|
const {
|
|
22
16
|
createSchedulerService,
|
|
23
17
|
SCHEDULER_PROVIDERS,
|
|
@@ -28,16 +22,9 @@ module.exports = {
|
|
|
28
22
|
// Interface (Port)
|
|
29
23
|
SchedulerServiceInterface,
|
|
30
24
|
|
|
31
|
-
// Adapters
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
get MockSchedulerAdapter() {
|
|
36
|
-
return require('./mock-scheduler-adapter').MockSchedulerAdapter;
|
|
37
|
-
},
|
|
38
|
-
get NetlifySchedulerAdapter() {
|
|
39
|
-
return require('./netlify-scheduler-adapter').NetlifySchedulerAdapter;
|
|
40
|
-
},
|
|
25
|
+
// Adapters
|
|
26
|
+
EventBridgeSchedulerAdapter,
|
|
27
|
+
MockSchedulerAdapter,
|
|
41
28
|
|
|
42
29
|
// Factory
|
|
43
30
|
createSchedulerService,
|