@friggframework/core 2.0.0--canary.690.3fe2b27.0 → 2.0.0--canary.705.9cfd819.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.
Files changed (211) hide show
  1. package/CHANGELOG.md +0 -11
  2. package/CLAUDE.md +40 -28
  3. package/README.md +0 -105
  4. package/application/commands/entity-commands.js +18 -60
  5. package/application/commands/integration-commands.js +73 -20
  6. package/application/commands/scheduler-commands.js +7 -11
  7. package/application/commands/user-commands.js +5 -17
  8. package/application/index.js +2 -6
  9. package/core/Worker.js +7 -31
  10. package/core/create-handler.js +0 -9
  11. package/credential/repositories/credential-repository-documentdb.js +0 -34
  12. package/credential/repositories/credential-repository-interface.js +0 -15
  13. package/credential/repositories/credential-repository-mongo.js +0 -23
  14. package/credential/repositories/credential-repository-postgres.js +0 -28
  15. package/database/config.js +24 -48
  16. package/database/documentdb-encryption-service.js +22 -9
  17. package/database/documentdb-utils.js +0 -2
  18. package/database/encryption/README.md +14 -78
  19. package/database/encryption/documentdb-encryption-service.md +1 -78
  20. package/database/encryption/encryption-schema-registry.js +24 -121
  21. package/database/encryption/field-encryption-service.js +5 -69
  22. package/database/encryption/integration-mapping-encryption.js +1 -1
  23. package/database/prisma.js +38 -15
  24. package/database/use-cases/check-encryption-health-use-case.js +52 -43
  25. package/errors/index.js +4 -0
  26. package/errors/mapping-already-exists-error.js +14 -0
  27. package/generated/prisma-mongodb/edge.js +4 -43
  28. package/generated/prisma-mongodb/index-browser.js +1 -40
  29. package/generated/prisma-mongodb/index.d.ts +111 -4739
  30. package/generated/prisma-mongodb/index.js +4 -43
  31. package/generated/prisma-mongodb/package.json +1 -1
  32. package/generated/prisma-mongodb/schema.prisma +0 -92
  33. package/generated/prisma-mongodb/wasm.js +4 -43
  34. package/generated/prisma-postgresql/edge.js +4 -43
  35. package/generated/prisma-postgresql/index-browser.js +1 -40
  36. package/generated/prisma-postgresql/index.d.ts +33 -4966
  37. package/generated/prisma-postgresql/index.js +4 -43
  38. package/generated/prisma-postgresql/package.json +1 -1
  39. package/generated/prisma-postgresql/schema.prisma +0 -87
  40. package/generated/prisma-postgresql/wasm.js +4 -43
  41. package/handlers/WEBHOOKS.md +29 -0
  42. package/handlers/app-definition-loader.js +19 -128
  43. package/handlers/app-handler-helpers.js +3 -25
  44. package/handlers/backend-utils.js +25 -31
  45. package/handlers/middleware/admin-auth.js +1 -14
  46. package/handlers/routers/auth.js +10 -46
  47. package/handlers/routers/health.js +4 -13
  48. package/handlers/routers/integration-defined-routers.js +92 -144
  49. package/handlers/routers/integration-webhook-routers.js +64 -102
  50. package/handlers/workers/integration-defined-workers.js +2 -5
  51. package/index.js +3 -65
  52. package/infrastructure/scheduler/index.js +6 -19
  53. package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
  54. package/integrations/index.js +0 -1
  55. package/integrations/integration-base.js +29 -0
  56. package/integrations/integration-router.js +96 -131
  57. package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
  58. package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
  59. package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
  60. package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
  61. package/integrations/repositories/integration-mapping-repository.js +29 -0
  62. package/integrations/repositories/integration-repository-documentdb.js +35 -35
  63. package/integrations/repositories/integration-repository-interface.js +31 -17
  64. package/integrations/repositories/integration-repository-mongo.js +129 -60
  65. package/integrations/repositories/integration-repository-postgres.js +134 -62
  66. package/integrations/repositories/process-repository-mongo.js +1 -1
  67. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  68. package/integrations/use-cases/add-entities-to-integration.js +92 -0
  69. package/integrations/use-cases/create-integration.js +75 -72
  70. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  71. package/integrations/use-cases/index.js +1 -7
  72. package/integrations/use-cases/remove-entities-from-integration.js +70 -0
  73. package/integrations/utils/assert-entity-ids-input.js +42 -0
  74. package/integrations/utils/map-integration-dto.js +14 -43
  75. package/modules/index.js +0 -14
  76. package/modules/module-factory.js +0 -12
  77. package/modules/module.js +2 -53
  78. package/modules/repositories/module-repository-documentdb.js +6 -51
  79. package/modules/repositories/module-repository-interface.js +2 -19
  80. package/modules/repositories/module-repository-mongo.js +74 -67
  81. package/modules/repositories/module-repository-postgres.js +74 -66
  82. package/modules/repositories/module-repository.js +1 -2
  83. package/modules/requester/requester.js +5 -35
  84. package/modules/use-cases/get-entities-for-user.js +13 -26
  85. package/modules/use-cases/get-entity-options-by-id.js +4 -10
  86. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  87. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  88. package/modules/use-cases/get-module.js +4 -10
  89. package/modules/use-cases/process-authorization-callback.js +19 -53
  90. package/modules/use-cases/refresh-entity-options.js +4 -10
  91. package/modules/use-cases/test-module-auth.js +4 -10
  92. package/modules/utils/map-module-dto.js +5 -3
  93. package/modules/utils/to-credential-id.js +14 -0
  94. package/package.json +5 -6
  95. package/prisma-mongodb/schema.prisma +0 -92
  96. package/prisma-postgresql/schema.prisma +0 -87
  97. package/queues/index.js +0 -25
  98. package/types/index.d.ts +0 -2
  99. package/user/repositories/user-repository-documentdb.js +2 -27
  100. package/user/repositories/user-repository-interface.js +0 -18
  101. package/user/repositories/user-repository-mongo.js +0 -24
  102. package/user/repositories/user-repository-postgres.js +0 -18
  103. package/user/tests/doubles/test-user-repository.js +1 -1
  104. package/user/use-cases/authenticate-user.js +1 -7
  105. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  106. package/user/use-cases/get-user-from-bearer-token.js +7 -2
  107. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  108. package/user/user.js +1 -64
  109. package/credential/use-cases/credential-operations-for-user.js +0 -202
  110. package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
  111. package/database/encryption/encryption-config.js +0 -219
  112. package/database/encryption/test-support/fake-ciphertext.js +0 -15
  113. package/database/organization-where.js +0 -18
  114. package/database/unique-violation.js +0 -25
  115. package/extensions/bootstrap-runner.js +0 -43
  116. package/extensions/extension-loader.js +0 -187
  117. package/extensions/index.js +0 -41
  118. package/extensions/initialize-app.js +0 -123
  119. package/extensions/route-mounter.js +0 -103
  120. package/extensions/schema-composer.js +0 -188
  121. package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
  122. package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
  123. package/integrations/repositories/integration-not-found.js +0 -14
  124. package/integrations/schedule-config.js +0 -80
  125. package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
  126. package/integrations/use-cases/integration-operations-for-user.js +0 -256
  127. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  128. package/management-api/create-management-api-router.js +0 -49
  129. package/management-api/error-response.js +0 -116
  130. package/management-api/management-api-config.js +0 -151
  131. package/management-api/meta-router.js +0 -76
  132. package/management-api/openapi/build-openapi.js +0 -144
  133. package/management-api/openapi/components.js +0 -415
  134. package/management-api/openapi/v1-operations.js +0 -83
  135. package/management-api/openapi/v2-operations.js +0 -264
  136. package/management-api/route-registry.js +0 -411
  137. package/management-api/test-support/http-client.js +0 -39
  138. package/management-api/use-cases/get-management-api-meta.js +0 -57
  139. package/management-api/v1-deprecation.js +0 -120
  140. package/management-api/v2/create-v2-router.js +0 -85
  141. package/management-api/v2/dependencies.js +0 -281
  142. package/management-api/v2/dto.js +0 -143
  143. package/management-api/v2/handlers/authorization.js +0 -94
  144. package/management-api/v2/handlers/credentials.js +0 -33
  145. package/management-api/v2/handlers/entities.js +0 -77
  146. package/management-api/v2/handlers/integrations.js +0 -128
  147. package/management-api/v2/handlers/proxy.js +0 -34
  148. package/management-api/v2/request.js +0 -85
  149. package/management-api/v2/test-support/fake-dependencies.js +0 -103
  150. package/modules/domain/authorization-session.js +0 -144
  151. package/modules/entity-scope.js +0 -15
  152. package/modules/entity-type-registry.js +0 -311
  153. package/modules/proxy/proxy-bases.js +0 -169
  154. package/modules/proxy/proxy-errors.js +0 -31
  155. package/modules/proxy/proxy-path.js +0 -183
  156. package/modules/proxy/proxy-policy.js +0 -277
  157. package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
  158. package/modules/repositories/authorization-session-repository-factory.js +0 -31
  159. package/modules/repositories/authorization-session-repository-interface.js +0 -47
  160. package/modules/repositories/authorization-session-repository-prisma.js +0 -87
  161. package/modules/repositories/entity-not-found.js +0 -18
  162. package/modules/requester/proxy-request.js +0 -245
  163. package/modules/scope-config.js +0 -192
  164. package/modules/scope-policy.js +0 -112
  165. package/modules/scope-resolution.js +0 -288
  166. package/modules/scope-visibility.js +0 -247
  167. package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
  168. package/modules/tests/doubles/ownership-matching.js +0 -18
  169. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  170. package/modules/use-cases/create-entity-from-credential.js +0 -113
  171. package/modules/use-cases/entity-operations-for-user.js +0 -162
  172. package/modules/use-cases/entity-types.js +0 -139
  173. package/modules/use-cases/execute-entity-proxy-request.js +0 -331
  174. package/modules/use-cases/get-authorization-requirements.js +0 -100
  175. package/modules/use-cases/get-entity-module-for-user.js +0 -113
  176. package/modules/use-cases/manage-access.js +0 -21
  177. package/modules/use-cases/multi-step-authorization.js +0 -329
  178. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
  179. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  180. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  181. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
  182. package/providers/index.js +0 -23
  183. package/providers/resolve-provider.js +0 -93
  184. package/providers/runtime-detection.js +0 -124
  185. package/queues/providers/index.js +0 -11
  186. package/queues/providers/netlify-background-provider.js +0 -150
  187. package/queues/providers/qstash-queue-provider.js +0 -219
  188. package/queues/providers/sqs-queue-provider.js +0 -70
  189. package/queues/queue-client-interface.js +0 -55
  190. package/queues/queue-message-auth.js +0 -193
  191. package/queues/queue-provider-factory.js +0 -101
  192. package/queues/queue-provider.js +0 -65
  193. package/queues/sqs-queue-client.js +0 -81
  194. package/types/modules/index.d.ts +0 -214
  195. package/types/user/index.d.ts +0 -134
  196. package/user/adopter-jwt-config.js +0 -177
  197. package/user/adopter-jwt-errors.js +0 -59
  198. package/user/adopter-token-verifier.js +0 -122
  199. package/user/app-user-helpers.js +0 -97
  200. package/user/create-user-authentication.js +0 -64
  201. package/user/membership.js +0 -51
  202. package/user/repositories/organization-membership-record.js +0 -19
  203. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  204. package/user/repositories/organization-membership-repository-factory.js +0 -42
  205. package/user/repositories/organization-membership-repository-interface.js +0 -57
  206. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  207. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  208. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  209. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  210. package/user/use-cases/ensure-organization-membership.js +0 -92
  211. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -1,143 +0,0 @@
1
- /**
2
- * Response shapes for Management API v2. Use cases return domain records;
3
- * these functions decide what leaves the server. Credential material never
4
- * does (see maskCredential).
5
- */
6
-
7
- const idOf = (value) => {
8
- if (value === undefined || value === null) return null;
9
- if (typeof value === 'object') {
10
- const id = value.id ?? value._id;
11
- return id === undefined || id === null ? null : String(id);
12
- }
13
- return String(value);
14
- };
15
-
16
- /**
17
- * `{ scope, owner, organizationId, access }` (ADR-056 §3.4): whose a record is
18
- * and what the caller may do with it. Empty when the use case did not say.
19
- */
20
- function tenancyFields(tenancy) {
21
- if (!tenancy) return {};
22
- return {
23
- scope: tenancy.scope,
24
- owner: tenancy.owner,
25
- organizationId: tenancy.organizationId ?? null,
26
- access: tenancy.access,
27
- };
28
- }
29
-
30
- /** An entity (connected account). */
31
- function toEntityDto(entity) {
32
- const credential =
33
- entity.credential && typeof entity.credential === 'object'
34
- ? entity.credential
35
- : null;
36
- return {
37
- id: String(entity.id),
38
- type: entity.moduleName ?? null,
39
- name: entity.name ?? null,
40
- externalId: entity.externalId ?? null,
41
- credentialId: idOf(entity.credential ?? entity.credentialId),
42
- userId: idOf(entity.userId ?? entity.user),
43
- authIsValid:
44
- typeof credential?.authIsValid === 'boolean'
45
- ? credential.authIsValid
46
- : null,
47
- ...tenancyFields(entity.tenancy),
48
- };
49
- }
50
-
51
- /** An integration, from a hydrated instance or a stored DTO. */
52
- function toIntegrationDto(integration) {
53
- return {
54
- id: String(integration.id),
55
- entities: integration.entities ?? [],
56
- status: integration.status ?? null,
57
- config: integration.config ?? {},
58
- version: integration.version ?? null,
59
- messages: integration.messages ?? {},
60
- ...tenancyFields(integration.tenancy),
61
- };
62
- }
63
-
64
- // Fields every repository adds around the stored data; everything else on a
65
- // credential record came from the provider and is treated as secret.
66
- const CREDENTIAL_METADATA = new Set([
67
- 'id',
68
- '_id',
69
- 'userId',
70
- 'user',
71
- 'externalId',
72
- 'authIsValid',
73
- 'createdAt',
74
- 'updatedAt',
75
- 'type',
76
- 'entityIds',
77
- 'status',
78
- // Added by the credential use cases (ADR-056 §3.4); never provider data.
79
- 'tenancy',
80
- ]);
81
-
82
- /**
83
- * Masks one stored credential value. Strings keep at most their last four
84
- * characters, and only when long enough that four characters reveal nothing
85
- * useful; objects and arrays are hidden whole. Numbers, booleans and null
86
- * (expiry times, flags) are not secret and pass through.
87
- */
88
- function maskValue(value) {
89
- if (value === null || typeof value === 'number' || typeof value === 'boolean') {
90
- return value;
91
- }
92
- if (typeof value === 'string') {
93
- return value.length >= 16 ? `****${value.slice(-4)}` : '****';
94
- }
95
- return '****';
96
- }
97
-
98
- const toIso = (value) => {
99
- if (!value) return null;
100
- const date = value instanceof Date ? value : new Date(value);
101
- return Number.isNaN(date.getTime()) ? null : date.toISOString();
102
- };
103
-
104
- /**
105
- * A credential with every provider field masked. The keys stay visible so a
106
- * client can tell an OAuth credential from an API-key one; the values never
107
- * leave the server.
108
- */
109
- function maskCredential(credential) {
110
- const data = {};
111
- for (const [key, value] of Object.entries(credential)) {
112
- if (CREDENTIAL_METADATA.has(key) || value === undefined) continue;
113
- data[key] = maskValue(value);
114
- }
115
- const entityIds = Array.isArray(credential.entityIds)
116
- ? credential.entityIds.map(String)
117
- : [];
118
- return {
119
- id: String(credential.id),
120
- type: credential.type ?? null,
121
- externalId: credential.externalId ?? null,
122
- userId: idOf(credential.userId),
123
- authIsValid:
124
- typeof credential.authIsValid === 'boolean'
125
- ? credential.authIsValid
126
- : null,
127
- entityIds,
128
- entityCount: entityIds.length,
129
- createdAt: toIso(credential.createdAt),
130
- updatedAt: toIso(credential.updatedAt),
131
- ...tenancyFields(credential.tenancy),
132
- data,
133
- };
134
- }
135
-
136
- module.exports = {
137
- tenancyFields,
138
- toEntityDto,
139
- toIntegrationDto,
140
- maskCredential,
141
- maskValue,
142
- idOf,
143
- };
@@ -1,94 +0,0 @@
1
- const { toEntityDto, maskCredential } = require('../dto');
2
- const { objectBody, requireFields, parseStep, optionalString } = require('../request');
3
-
4
- /** The v2 body for a submitted step: masked records when complete. */
5
- function toAuthorizationResponse(result) {
6
- if (result.status !== 'complete') return result;
7
- return {
8
- status: 'complete',
9
- entity: result.entity ? toEntityDto(result.entity) : null,
10
- credential: result.credential ? maskCredential(result.credential) : null,
11
- ...(result.previousCredentialId && {
12
- previousCredentialId: result.previousCredentialId,
13
- }),
14
- };
15
- }
16
-
17
- /**
18
- * Management API v2 authorization handlers (multi-step authorize and
19
- * credential re-authorization), keyed by operationId.
20
- */
21
- function createAuthorizationHandlers(useCases) {
22
- const {
23
- getAuthorizationStep,
24
- submitAuthorizationStep,
25
- getCredentialReauthorization,
26
- reauthorizeCredentialForUser,
27
- } = useCases;
28
-
29
- const stepQuery = (req) => ({
30
- step: parseStep(req.query.step),
31
- sessionId: optionalString(req.query.sessionId, 'sessionId'),
32
- state: optionalString(req.query.state, 'state'),
33
- });
34
-
35
- const stepBody = (req) => {
36
- const body = objectBody(req);
37
- return {
38
- body,
39
- step: parseStep(body.step),
40
- sessionId: optionalString(body.sessionId, 'sessionId'),
41
- };
42
- };
43
-
44
- return {
45
- async getAuthorizationRequirements(req, res, user) {
46
- const entityType = optionalString(req.query.entityType, 'entityType');
47
- requireFields({ entityType }, ['entityType']);
48
- res.json(
49
- await getAuthorizationStep.execute({
50
- user,
51
- entityType,
52
- ...stepQuery(req),
53
- })
54
- );
55
- },
56
-
57
- async authorize(req, res, user) {
58
- const { body, step, sessionId } = stepBody(req);
59
- requireFields(body, ['entityType', 'data']);
60
- const result = await submitAuthorizationStep.execute({
61
- user,
62
- entityType: optionalString(body.entityType, 'entityType'),
63
- data: body.data,
64
- step,
65
- sessionId,
66
- scope: optionalString(body.scope, 'scope'),
67
- });
68
- res.json(toAuthorizationResponse(result));
69
- },
70
-
71
- async getCredentialReauthorizeRequirements(req, res, user) {
72
- res.json(
73
- await getCredentialReauthorization.execute(
74
- req.params.credentialId,
75
- user,
76
- stepQuery(req)
77
- )
78
- );
79
- },
80
-
81
- async reauthorizeCredential(req, res, user) {
82
- const { body, step, sessionId } = stepBody(req);
83
- requireFields(body, ['data']);
84
- const result = await reauthorizeCredentialForUser.execute(
85
- req.params.credentialId,
86
- user,
87
- { data: body.data, step, sessionId }
88
- );
89
- res.json(toAuthorizationResponse(result));
90
- },
91
- };
92
- }
93
-
94
- module.exports = { createAuthorizationHandlers, toAuthorizationResponse };
@@ -1,33 +0,0 @@
1
- const { maskCredential } = require('../dto');
2
- const { parseListFilters } = require('../request');
3
-
4
- /** Management API v2 credential handlers, keyed by operationId. */
5
- function createCredentialHandlers(useCases) {
6
- const { listCredentialsForUser, getCredentialForUser, deleteCredentialForUser } =
7
- useCases;
8
-
9
- return {
10
- async listCredentials(req, res, user) {
11
- const credentials = await listCredentialsForUser.execute(
12
- user,
13
- parseListFilters(req.query)
14
- );
15
- res.json({ credentials: credentials.map(maskCredential) });
16
- },
17
-
18
- async getCredential(req, res, user) {
19
- res.json(
20
- maskCredential(
21
- await getCredentialForUser.execute(req.params.credentialId, user)
22
- )
23
- );
24
- },
25
-
26
- async deleteCredential(req, res, user) {
27
- await deleteCredentialForUser.execute(req.params.credentialId, user);
28
- res.status(204).end();
29
- },
30
- };
31
- }
32
-
33
- module.exports = { createCredentialHandlers };
@@ -1,77 +0,0 @@
1
- const { toEntityDto } = require('../dto');
2
- const { objectBody, parseStep, parseListFilters } = require('../request');
3
-
4
- /** Management API v2 entity and entity-type handlers, keyed by operationId. */
5
- function createEntityHandlers(useCases) {
6
- const {
7
- listEntitiesForUser,
8
- getEntityForUser,
9
- deleteEntityForUser,
10
- testEntityAuthForUser,
11
- getEntityOptionsForUser,
12
- refreshEntityOptionsForUser,
13
- listEntityTypes,
14
- getEntityType,
15
- getAuthorizationRequirements,
16
- } = useCases;
17
-
18
- return {
19
- async listEntities(req, res, user) {
20
- const entities = await listEntitiesForUser.execute(
21
- user,
22
- parseListFilters(req.query)
23
- );
24
- res.json({ entities: entities.map(toEntityDto) });
25
- },
26
-
27
- async listEntityTypes(_req, res, user) {
28
- res.json({ types: await listEntityTypes.execute({ user }) });
29
- },
30
-
31
- async getEntityType(req, res, user) {
32
- res.json(await getEntityType.execute(req.params.entityType, { user }));
33
- },
34
-
35
- // Describes a step's requirements without starting an authorization
36
- // session; GET /api/v2/authorize starts one.
37
- async getEntityTypeRequirements(req, res, user) {
38
- res.json(
39
- await getAuthorizationRequirements.execute({
40
- entityType: req.params.entityType,
41
- step: parseStep(req.query.step),
42
- userId: user.getId(),
43
- })
44
- );
45
- },
46
-
47
- async getEntity(req, res, user) {
48
- res.json(toEntityDto(await getEntityForUser.execute(req.params.entityId, user)));
49
- },
50
-
51
- async deleteEntity(req, res, user) {
52
- await deleteEntityForUser.execute(req.params.entityId, user);
53
- res.status(204).end();
54
- },
55
-
56
- async testEntityAuth(req, res, user) {
57
- const ok = await testEntityAuthForUser.execute(req.params.entityId, user);
58
- res.json({ status: ok ? 'ok' : 'failed' });
59
- },
60
-
61
- async getEntityOptions(req, res, user) {
62
- res.json(await getEntityOptionsForUser.execute(req.params.entityId, user));
63
- },
64
-
65
- async refreshEntityOptions(req, res, user) {
66
- res.json(
67
- await refreshEntityOptionsForUser.execute(
68
- req.params.entityId,
69
- user,
70
- objectBody(req)
71
- )
72
- );
73
- },
74
- };
75
- }
76
-
77
- module.exports = { createEntityHandlers };
@@ -1,128 +0,0 @@
1
- const { toIntegrationDto } = require('../dto');
2
- const { objectBody, optionalString, parseListFilters } = require('../request');
3
-
4
- /**
5
- * Management API v2 integration handlers, keyed by registry operationId.
6
- * Each takes (req, res, user) after authentication and only calls use cases.
7
- */
8
- function createIntegrationHandlers(useCases) {
9
- const {
10
- getIntegrationsForUser,
11
- getPossibleIntegrations,
12
- createIntegrationForUser,
13
- getIntegrationInstanceForUser,
14
- updateIntegrationForUser,
15
- removeIntegrationForUser,
16
- sendIntegrationEventForUser,
17
- runIntegrationActionForUser,
18
- testIntegrationAuthForUser,
19
- } = useCases;
20
-
21
- const send = (req, user, event, payload) =>
22
- sendIntegrationEventForUser.execute(
23
- req.params.integrationId,
24
- user,
25
- event,
26
- payload
27
- );
28
-
29
- return {
30
- async listIntegrations(req, res, user) {
31
- const integrations = await getIntegrationsForUser.execute(
32
- user,
33
- parseListFilters(req.query)
34
- );
35
- res.json({ integrations: integrations.map(toIntegrationDto) });
36
- },
37
-
38
- async listIntegrationOptions(_req, res) {
39
- res.json({ integrations: await getPossibleIntegrations.execute() });
40
- },
41
-
42
- async createIntegration(req, res, user) {
43
- const { entities, config } = objectBody(req);
44
- const integration = await createIntegrationForUser.execute(
45
- { entities, config },
46
- user
47
- );
48
- res.status(201).json(toIntegrationDto(integration));
49
- },
50
-
51
- async getIntegration(req, res, user) {
52
- const instance = await getIntegrationInstanceForUser.execute(
53
- req.params.integrationId,
54
- user
55
- );
56
- res.json(toIntegrationDto(instance));
57
- },
58
-
59
- async updateIntegration(req, res, user) {
60
- const { config } = objectBody(req);
61
- const integration = await updateIntegrationForUser.execute(
62
- req.params.integrationId,
63
- user,
64
- config
65
- );
66
- res.json(toIntegrationDto(integration));
67
- },
68
-
69
- async deleteIntegration(req, res, user) {
70
- await removeIntegrationForUser.execute(req.params.integrationId, user);
71
- res.status(204).end();
72
- },
73
-
74
- async getIntegrationConfigOptions(req, res, user) {
75
- res.json(await send(req, user, 'GET_CONFIG_OPTIONS'));
76
- },
77
-
78
- async refreshIntegrationConfigOptions(req, res, user) {
79
- res.json(await send(req, user, 'REFRESH_CONFIG_OPTIONS', objectBody(req)));
80
- },
81
-
82
- async listIntegrationActions(req, res, user) {
83
- const actionType = optionalString(req.query.actionType, 'actionType');
84
- res.json(
85
- await send(req, user, 'GET_USER_ACTIONS', actionType ? { actionType } : {})
86
- );
87
- },
88
-
89
- async getIntegrationActionOptions(req, res, user) {
90
- res.json(
91
- await send(req, user, 'GET_USER_ACTION_OPTIONS', {
92
- actionId: req.params.actionId,
93
- data: objectBody(req),
94
- })
95
- );
96
- },
97
-
98
- async refreshIntegrationActionOptions(req, res, user) {
99
- res.json(
100
- await send(req, user, 'REFRESH_USER_ACTION_OPTIONS', {
101
- actionId: req.params.actionId,
102
- data: objectBody(req),
103
- })
104
- );
105
- },
106
-
107
- async runIntegrationAction(req, res, user) {
108
- res.json(
109
- await runIntegrationActionForUser.execute(
110
- req.params.integrationId,
111
- user,
112
- req.params.actionId,
113
- objectBody(req)
114
- )
115
- );
116
- },
117
-
118
- async testIntegrationAuth(req, res, user) {
119
- const { ok, errors } = await testIntegrationAuthForUser.execute(
120
- req.params.integrationId,
121
- user
122
- );
123
- res.json(ok ? { status: 'ok' } : { status: 'failed', errors });
124
- },
125
- };
126
- }
127
-
128
- module.exports = { createIntegrationHandlers };
@@ -1,34 +0,0 @@
1
- const { toV2ErrorBody } = require('../../error-response');
2
-
3
- /**
4
- * POST /api/v2/entities/:entityId/proxy (ADR-052). The HTTP status follows the
5
- * upstream status. Proxy failures answer in the api-proxy.schema.json error
6
- * shape, which is also the v2 error shape plus `success` and `status`.
7
- */
8
- function createProxyHandlers({ executeEntityProxyRequest }) {
9
- return {
10
- async proxyEntityRequest(req, res, user) {
11
- try {
12
- const result = await executeEntityProxyRequest.execute(
13
- req.params.entityId,
14
- user,
15
- req.body
16
- );
17
- res.status(result.status).json(result.body);
18
- } catch (error) {
19
- if (!error?.isBoom || !error.data?.proxy) throw error;
20
- const status = error.output.statusCode;
21
- if (error.data.retryAfter) {
22
- res.set('Retry-After', String(error.data.retryAfter));
23
- }
24
- res.status(status).json({
25
- success: false,
26
- status,
27
- ...toV2ErrorBody(error),
28
- });
29
- }
30
- },
31
- };
32
- }
33
-
34
- module.exports = { createProxyHandlers };
@@ -1,85 +0,0 @@
1
- const Boom = require('@hapi/boom');
2
- const { LIST_OWNERS } = require('../../modules/scope-visibility');
3
-
4
- /** The JSON body as an object, or a 400 VALIDATION_ERROR. */
5
- function objectBody(req) {
6
- const body = req.body;
7
- if (body === undefined || body === null || body === '') return {};
8
- if (typeof body !== 'object' || Array.isArray(body)) {
9
- throw Boom.badRequest('Request body must be a JSON object', {
10
- code: 'VALIDATION_ERROR',
11
- });
12
- }
13
- return body;
14
- }
15
-
16
- /** Fields that must be present (non-empty) in the body. */
17
- function requireFields(body, fields) {
18
- const missing = fields.filter(
19
- (field) =>
20
- body[field] === undefined || body[field] === null || body[field] === ''
21
- );
22
- if (missing.length) {
23
- throw Boom.badRequest(
24
- `Missing required field${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}`,
25
- { code: 'VALIDATION_ERROR', details: { missing } }
26
- );
27
- }
28
- return body;
29
- }
30
-
31
- /** A 1-based step number from a query or body value; 1 when absent. */
32
- function parseStep(value) {
33
- if (value === undefined || value === null || value === '') return 1;
34
- const step = Number(value);
35
- if (!Number.isInteger(step) || step < 1) {
36
- throw Boom.badRequest('step must be a positive integer', {
37
- code: 'INVALID_STEP',
38
- });
39
- }
40
- return step;
41
- }
42
-
43
- /** An optional string query/body value. */
44
- function optionalString(value, name) {
45
- if (value === undefined || value === null || value === '') return undefined;
46
- if (typeof value !== 'string') {
47
- throw Boom.badRequest(`${name} must be a string`, {
48
- code: 'VALIDATION_ERROR',
49
- details: { field: name },
50
- });
51
- }
52
- return value;
53
- }
54
-
55
- const LIST_SCOPES = ['individual', 'organization'];
56
-
57
- /**
58
- * `?scope=individual|organization` and `?owner=me|org` on a list (ADR-056
59
- * §3.4). Anything else is a 400.
60
- */
61
- function parseListFilters(query = {}) {
62
- const scope = optionalString(query.scope, 'scope');
63
- const owner = optionalString(query.owner, 'owner');
64
- if (scope !== undefined && !LIST_SCOPES.includes(scope)) {
65
- throw Boom.badRequest(`scope must be one of ${LIST_SCOPES.join(', ')}`, {
66
- code: 'VALIDATION_ERROR',
67
- details: { field: 'scope' },
68
- });
69
- }
70
- if (owner !== undefined && !Object.hasOwn(LIST_OWNERS, owner)) {
71
- throw Boom.badRequest('owner must be me or org', {
72
- code: 'VALIDATION_ERROR',
73
- details: { field: 'owner' },
74
- });
75
- }
76
- return { scope, owner };
77
- }
78
-
79
- module.exports = {
80
- objectBody,
81
- requireFields,
82
- parseStep,
83
- optionalString,
84
- parseListFilters,
85
- };