@friggframework/core 2.0.0--canary.686.df38e8e.0 → 2.0.0--canary.710.9045bbc.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 (217) hide show
  1. package/CHANGELOG.md +0 -16
  2. package/CLAUDE.md +41 -29
  3. package/README.md +0 -105
  4. package/application/commands/README.md +17 -11
  5. package/application/commands/entity-commands.js +18 -60
  6. package/application/commands/integration-commands.js +73 -20
  7. package/application/commands/scheduler-commands.js +7 -11
  8. package/application/commands/user-commands.js +5 -17
  9. package/application/index.js +2 -6
  10. package/core/Worker.js +7 -31
  11. package/core/create-handler.js +0 -9
  12. package/credential/repositories/credential-repository-documentdb.js +0 -34
  13. package/credential/repositories/credential-repository-interface.js +0 -15
  14. package/credential/repositories/credential-repository-mongo.js +0 -23
  15. package/credential/repositories/credential-repository-postgres.js +0 -28
  16. package/database/config.js +24 -48
  17. package/database/documentdb-encryption-service.js +22 -9
  18. package/database/documentdb-utils.js +0 -2
  19. package/database/encryption/README.md +17 -81
  20. package/database/encryption/documentdb-encryption-service.md +1 -78
  21. package/database/encryption/encryption-schema-registry.js +46 -123
  22. package/database/encryption/field-encryption-service.js +5 -69
  23. package/database/encryption/integration-mapping-encryption.js +1 -1
  24. package/database/prisma.js +38 -15
  25. package/database/use-cases/check-encryption-health-use-case.js +52 -43
  26. package/errors/index.js +4 -0
  27. package/errors/mapping-already-exists-error.js +14 -0
  28. package/generated/prisma-mongodb/edge.js +4 -43
  29. package/generated/prisma-mongodb/index-browser.js +1 -40
  30. package/generated/prisma-mongodb/index.d.ts +111 -4739
  31. package/generated/prisma-mongodb/index.js +4 -43
  32. package/generated/prisma-mongodb/package.json +1 -1
  33. package/generated/prisma-mongodb/schema.prisma +0 -92
  34. package/generated/prisma-mongodb/wasm.js +4 -43
  35. package/generated/prisma-postgresql/edge.js +4 -43
  36. package/generated/prisma-postgresql/index-browser.js +1 -40
  37. package/generated/prisma-postgresql/index.d.ts +33 -4966
  38. package/generated/prisma-postgresql/index.js +4 -43
  39. package/generated/prisma-postgresql/package.json +1 -1
  40. package/generated/prisma-postgresql/schema.prisma +0 -87
  41. package/generated/prisma-postgresql/wasm.js +4 -43
  42. package/handlers/WEBHOOKS.md +29 -0
  43. package/handlers/app-definition-loader.js +19 -121
  44. package/handlers/app-handler-helpers.js +3 -25
  45. package/handlers/backend-utils.js +25 -31
  46. package/handlers/middleware/admin-auth.js +1 -14
  47. package/handlers/routers/auth.js +10 -46
  48. package/handlers/routers/health.js +4 -13
  49. package/handlers/routers/integration-defined-routers.js +92 -144
  50. package/handlers/routers/integration-webhook-routers.js +64 -102
  51. package/handlers/workers/integration-defined-workers.js +2 -5
  52. package/index.js +3 -65
  53. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +67 -13
  54. package/infrastructure/scheduler/index.js +6 -19
  55. package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
  56. package/integrations/index.js +0 -1
  57. package/integrations/integration-base.js +29 -0
  58. package/integrations/integration-router.js +96 -131
  59. package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
  60. package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
  61. package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
  62. package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
  63. package/integrations/repositories/integration-mapping-repository.js +29 -0
  64. package/integrations/repositories/integration-repository-documentdb.js +35 -35
  65. package/integrations/repositories/integration-repository-interface.js +31 -17
  66. package/integrations/repositories/integration-repository-mongo.js +129 -60
  67. package/integrations/repositories/integration-repository-postgres.js +134 -62
  68. package/integrations/repositories/process-repository-mongo.js +1 -1
  69. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  70. package/integrations/use-cases/add-entities-to-integration.js +92 -0
  71. package/integrations/use-cases/create-integration.js +75 -72
  72. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  73. package/integrations/use-cases/index.js +1 -7
  74. package/integrations/use-cases/remove-entities-from-integration.js +70 -0
  75. package/integrations/utils/assert-entity-ids-input.js +42 -0
  76. package/integrations/utils/map-integration-dto.js +14 -43
  77. package/jest-setup.js +13 -2
  78. package/jest-teardown.js +5 -2
  79. package/jest.config.js +4 -0
  80. package/jest.db.config.js +23 -0
  81. package/modules/index.js +0 -14
  82. package/modules/module-factory.js +0 -12
  83. package/modules/module.js +2 -53
  84. package/modules/repositories/module-repository-documentdb.js +6 -51
  85. package/modules/repositories/module-repository-interface.js +2 -19
  86. package/modules/repositories/module-repository-mongo.js +74 -67
  87. package/modules/repositories/module-repository-postgres.js +74 -66
  88. package/modules/repositories/module-repository.js +1 -2
  89. package/modules/requester/requester.js +2 -27
  90. package/modules/use-cases/get-entities-for-user.js +13 -26
  91. package/modules/use-cases/get-entity-options-by-id.js +4 -10
  92. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  93. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  94. package/modules/use-cases/get-module.js +4 -10
  95. package/modules/use-cases/process-authorization-callback.js +19 -53
  96. package/modules/use-cases/refresh-entity-options.js +4 -10
  97. package/modules/use-cases/test-module-auth.js +4 -10
  98. package/modules/utils/map-module-dto.js +5 -3
  99. package/modules/utils/to-credential-id.js +14 -0
  100. package/package.json +7 -6
  101. package/prisma-mongodb/schema.prisma +0 -92
  102. package/prisma-postgresql/schema.prisma +0 -87
  103. package/queues/index.js +0 -25
  104. package/queues/queuer-util.js +6 -5
  105. package/types/index.d.ts +0 -2
  106. package/user/repositories/user-repository-documentdb.js +2 -27
  107. package/user/repositories/user-repository-interface.js +0 -18
  108. package/user/repositories/user-repository-mongo.js +0 -24
  109. package/user/repositories/user-repository-postgres.js +0 -18
  110. package/user/tests/doubles/test-user-repository.js +1 -1
  111. package/user/use-cases/authenticate-user.js +1 -7
  112. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  113. package/user/use-cases/get-user-from-bearer-token.js +7 -2
  114. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  115. package/user/user.js +1 -64
  116. package/credential/use-cases/credential-operations-for-user.js +0 -202
  117. package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
  118. package/database/encryption/encryption-config.js +0 -219
  119. package/database/encryption/test-support/fake-ciphertext.js +0 -15
  120. package/database/organization-where.js +0 -18
  121. package/database/unique-violation.js +0 -25
  122. package/extensions/bootstrap-runner.js +0 -43
  123. package/extensions/extension-loader.js +0 -187
  124. package/extensions/index.js +0 -41
  125. package/extensions/initialize-app.js +0 -123
  126. package/extensions/route-mounter.js +0 -103
  127. package/extensions/schema-composer.js +0 -188
  128. package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -220
  129. package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
  130. package/integrations/repositories/integration-not-found.js +0 -14
  131. package/integrations/schedule-config.js +0 -80
  132. package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
  133. package/integrations/use-cases/integration-operations-for-user.js +0 -256
  134. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  135. package/management-api/create-management-api-router.js +0 -49
  136. package/management-api/error-response.js +0 -116
  137. package/management-api/management-api-config.js +0 -151
  138. package/management-api/meta-router.js +0 -76
  139. package/management-api/openapi/build-openapi.js +0 -144
  140. package/management-api/openapi/components.js +0 -408
  141. package/management-api/openapi/v1-operations.js +0 -83
  142. package/management-api/openapi/v2-operations.js +0 -264
  143. package/management-api/route-registry.js +0 -411
  144. package/management-api/test-support/http-client.js +0 -39
  145. package/management-api/use-cases/get-management-api-meta.js +0 -57
  146. package/management-api/v1-deprecation.js +0 -120
  147. package/management-api/v2/create-v2-router.js +0 -85
  148. package/management-api/v2/dependencies.js +0 -281
  149. package/management-api/v2/dto.js +0 -143
  150. package/management-api/v2/handlers/authorization.js +0 -94
  151. package/management-api/v2/handlers/credentials.js +0 -33
  152. package/management-api/v2/handlers/entities.js +0 -77
  153. package/management-api/v2/handlers/integrations.js +0 -128
  154. package/management-api/v2/handlers/proxy.js +0 -34
  155. package/management-api/v2/request.js +0 -85
  156. package/management-api/v2/test-support/fake-dependencies.js +0 -103
  157. package/modules/domain/authorization-session.js +0 -144
  158. package/modules/entity-scope.js +0 -15
  159. package/modules/entity-type-registry.js +0 -311
  160. package/modules/proxy/proxy-errors.js +0 -20
  161. package/modules/proxy/proxy-path.js +0 -179
  162. package/modules/proxy/proxy-policy.js +0 -248
  163. package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
  164. package/modules/repositories/authorization-session-repository-factory.js +0 -31
  165. package/modules/repositories/authorization-session-repository-interface.js +0 -47
  166. package/modules/repositories/authorization-session-repository-prisma.js +0 -87
  167. package/modules/repositories/entity-not-found.js +0 -18
  168. package/modules/requester/proxy-request.js +0 -244
  169. package/modules/scope-config.js +0 -192
  170. package/modules/scope-policy.js +0 -112
  171. package/modules/scope-resolution.js +0 -288
  172. package/modules/scope-visibility.js +0 -247
  173. package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
  174. package/modules/tests/doubles/ownership-matching.js +0 -18
  175. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  176. package/modules/use-cases/create-entity-from-credential.js +0 -113
  177. package/modules/use-cases/entity-operations-for-user.js +0 -162
  178. package/modules/use-cases/entity-types.js +0 -139
  179. package/modules/use-cases/execute-entity-proxy-request.js +0 -286
  180. package/modules/use-cases/get-authorization-requirements.js +0 -100
  181. package/modules/use-cases/get-entity-module-for-user.js +0 -113
  182. package/modules/use-cases/manage-access.js +0 -21
  183. package/modules/use-cases/multi-step-authorization.js +0 -329
  184. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
  185. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  186. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  187. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
  188. package/providers/index.js +0 -23
  189. package/providers/resolve-provider.js +0 -93
  190. package/providers/runtime-detection.js +0 -124
  191. package/queues/providers/index.js +0 -11
  192. package/queues/providers/netlify-background-provider.js +0 -187
  193. package/queues/providers/qstash-queue-provider.js +0 -219
  194. package/queues/providers/sqs-queue-provider.js +0 -70
  195. package/queues/queue-client-interface.js +0 -55
  196. package/queues/queue-message-auth.js +0 -193
  197. package/queues/queue-provider-factory.js +0 -101
  198. package/queues/queue-provider.js +0 -65
  199. package/queues/sqs-queue-client.js +0 -81
  200. package/types/modules/index.d.ts +0 -197
  201. package/types/user/index.d.ts +0 -134
  202. package/user/adopter-jwt-config.js +0 -177
  203. package/user/adopter-jwt-errors.js +0 -59
  204. package/user/adopter-token-verifier.js +0 -122
  205. package/user/app-user-helpers.js +0 -97
  206. package/user/create-user-authentication.js +0 -64
  207. package/user/membership.js +0 -51
  208. package/user/repositories/organization-membership-record.js +0 -19
  209. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  210. package/user/repositories/organization-membership-repository-factory.js +0 -42
  211. package/user/repositories/organization-membership-repository-interface.js +0 -57
  212. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  213. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  214. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  215. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  216. package/user/use-cases/ensure-organization-membership.js +0 -92
  217. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -1,116 +0,0 @@
1
- const Boom = require('@hapi/boom');
2
-
3
- /**
4
- * Error responses for the HTTP boundary (handlers/app-handler-helpers.js).
5
- *
6
- * - v1 and other unversioned routes keep their historical body:
7
- * `{ error: message }` for 4xx, `{ error: 'Internal Server Error' }` for 5xx.
8
- * - Management API v2 and /api/meta answer `{ error: { code, message, details? } }`
9
- * and carry `Frigg-API-Version: 2` even when the error happened before any
10
- * v2 router ran (for example a body-parser failure).
11
- */
12
-
13
- const API_VERSION_HEADER = 'Frigg-API-Version';
14
-
15
- const CODE_BY_STATUS = {
16
- 400: 'BAD_REQUEST',
17
- 401: 'UNAUTHORIZED',
18
- 403: 'FORBIDDEN',
19
- 404: 'NOT_FOUND',
20
- 405: 'METHOD_NOT_ALLOWED',
21
- 409: 'CONFLICT',
22
- 410: 'GONE',
23
- 413: 'PAYLOAD_TOO_LARGE',
24
- 415: 'UNSUPPORTED_MEDIA_TYPE',
25
- 422: 'UNPROCESSABLE_ENTITY',
26
- 429: 'RATE_LIMITED',
27
- 500: 'INTERNAL_ERROR',
28
- 501: 'NOT_IMPLEMENTED',
29
- 502: 'BAD_GATEWAY',
30
- 503: 'SERVICE_UNAVAILABLE',
31
- 504: 'GATEWAY_TIMEOUT',
32
- };
33
-
34
- // body-parser (via http-errors) tags its failures with `type`.
35
- const CODE_BY_BODY_PARSER_TYPE = {
36
- 'entity.parse.failed': 'INVALID_JSON',
37
- 'entity.too.large': 'PAYLOAD_TOO_LARGE',
38
- 'encoding.unsupported': 'UNSUPPORTED_MEDIA_TYPE',
39
- 'charset.unsupported': 'UNSUPPORTED_MEDIA_TYPE',
40
- 'request.aborted': 'BAD_REQUEST',
41
- };
42
-
43
- /** True for paths that use the v2 error contract. */
44
- function isVersionedManagementApiPath(path = '') {
45
- return (
46
- path === '/api/v2' ||
47
- path.startsWith('/api/v2/') ||
48
- path === '/api/meta' ||
49
- path.startsWith('/api/meta/')
50
- );
51
- }
52
-
53
- /**
54
- * The HTTP status for an error. Boom errors carry theirs. Other errors are
55
- * trusted only when they declare a client error as safe to expose, as
56
- * http-errors (and so body-parser) does; anything else is a 500, so an
57
- * upstream FetchError's `statusCode` is never mistaken for ours.
58
- */
59
- function resolveStatusCode(err) {
60
- if (err?.isBoom) return err.output.statusCode;
61
- const status = err?.status ?? err?.statusCode;
62
- if (
63
- err?.expose === true &&
64
- Number.isInteger(status) &&
65
- status >= 400 &&
66
- status < 500
67
- ) {
68
- return status;
69
- }
70
- return 500;
71
- }
72
-
73
- /** Normalises any thrown value into a Boom error with the right status. */
74
- function toBoom(err) {
75
- if (err?.isBoom) return err;
76
- const error = err instanceof Error ? err : new Error(String(err));
77
- return Boom.boomify(error, { statusCode: resolveStatusCode(err) });
78
- }
79
-
80
- /** The v2 error body for a Boom error. */
81
- function toV2ErrorBody(boomError) {
82
- const statusCode = boomError.output.statusCode;
83
- const data =
84
- boomError.data && typeof boomError.data === 'object'
85
- ? boomError.data
86
- : {};
87
- const exposeServerError = statusCode >= 500 && data.expose === true;
88
-
89
- const code =
90
- (typeof data.code === 'string' && data.code) ||
91
- CODE_BY_BODY_PARSER_TYPE[boomError.type] ||
92
- CODE_BY_STATUS[statusCode] ||
93
- (statusCode >= 500 ? 'INTERNAL_ERROR' : 'BAD_REQUEST');
94
-
95
- const message =
96
- statusCode < 500 || exposeServerError
97
- ? boomError.message
98
- : 'Internal Server Error';
99
-
100
- const body = { code, message };
101
- if (
102
- data.details !== undefined &&
103
- (statusCode < 500 || exposeServerError)
104
- ) {
105
- body.details = data.details;
106
- }
107
- return { error: body };
108
- }
109
-
110
- module.exports = {
111
- API_VERSION_HEADER,
112
- isVersionedManagementApiPath,
113
- resolveStatusCode,
114
- toBoom,
115
- toV2ErrorBody,
116
- };
@@ -1,151 +0,0 @@
1
- /**
2
- * Resolves `appDefinition.managementApi` (ADR-052, ADR-053).
3
- *
4
- * ```js
5
- * managementApi: {
6
- * v1: false, // drop the deprecated v1 routes (default true)
7
- * proxy: {
8
- * enable: true, // entity proxy, off by default (ADR-052)
9
- * timeoutMs: 25000, // whole upstream call, lower only
10
- * maxRequestBytes: 1048576,
11
- * maxResponseBytes: 5242880, // lower only
12
- * scopes: ['individual'], // entity scopes the proxy accepts (ADR-056 §3.5)
13
- * modules: { // per-module allow-list override
14
- * hubspot: { allow: 'module' },
15
- * erp: { allow: [{ method: 'GET', path: '/v1/customers/**' }] },
16
- * },
17
- * },
18
- * }
19
- * ```
20
- *
21
- * Limits that are out of range fall back to their defaults. `proxy.scopes` is a
22
- * security setting, so it does not fall back: a value that is not a list of
23
- * known tenant scopes throws ManagementApiConfigError, and loadAppDefinition
24
- * calls this so the app stops at load.
25
- */
26
-
27
- const { BaseError } = require('../errors/base-error');
28
- const { SCOPES } = require('../modules/scope-resolution');
29
-
30
- /** Raised for an invalid `appDefinition.managementApi` setting. */
31
- class ManagementApiConfigError extends BaseError {
32
- constructor(message, options) {
33
- super(message, options);
34
- this.code = 'MANAGEMENT_API_CONFIG_INVALID';
35
- }
36
- }
37
-
38
- /**
39
- * The scopes `managementApi.proxy.scopes` may list. `global` is not one of
40
- * them: a global entity is the app owner's and is never a tenant's to proxy.
41
- */
42
- const PROXY_SCOPE_VALUES = Object.freeze([SCOPES.INDIVIDUAL, SCOPES.ORGANIZATION]);
43
-
44
- /**
45
- * Narrowest by default (ADR-056 §3.5): proxying through a shared credential
46
- * lets any member act on the organization's account, so an app opts in to
47
- * `organization` explicitly.
48
- */
49
- const DEFAULT_PROXY_SCOPES = Object.freeze([SCOPES.INDIVIDUAL]);
50
-
51
- // ADR-052 §7. API Gateway HTTP APIs time out at 30s and Lambda responses cap
52
- // at 6MB, so the timeout and response defaults are also ceilings: an app may
53
- // lower them, not raise them.
54
- const DEFAULT_PROXY_LIMITS = Object.freeze({
55
- timeoutMs: 25_000,
56
- maxRequestBytes: 1024 * 1024,
57
- maxResponseBytes: 5 * 1024 * 1024,
58
- });
59
-
60
- // The request-body limit has no platform ceiling below API Gateway's 10MB, so
61
- // an app may raise it up to that.
62
- const MAX_REQUEST_BYTES_CEILING = 10 * 1024 * 1024;
63
-
64
- const isPositiveNumber = (value) =>
65
- typeof value === 'number' && Number.isFinite(value) && value > 0;
66
-
67
- /** The value, capped at `ceiling`; the ceiling when the value is unusable. */
68
- function positiveCapped(value, ceiling) {
69
- return isPositiveNumber(value) ? Math.min(value, ceiling) : ceiling;
70
- }
71
-
72
- /** The value, capped at `ceiling`; `fallback` when the value is unusable. */
73
- function positiveOr(value, fallback, ceiling) {
74
- return isPositiveNumber(value) ? Math.min(value, ceiling) : fallback;
75
- }
76
-
77
- const proxyScopesError = (message) =>
78
- new ManagementApiConfigError(`managementApi.proxy.scopes: ${message}`);
79
-
80
- /**
81
- * `managementApi.proxy.scopes`: absent means the default; anything else must be
82
- * a non-empty list of PROXY_SCOPE_VALUES, each once.
83
- * @returns {ReadonlyArray<string>}
84
- * @throws {ManagementApiConfigError}
85
- */
86
- function resolveProxyScopes(scopes) {
87
- if (scopes === undefined) return DEFAULT_PROXY_SCOPES;
88
- const allowed = PROXY_SCOPE_VALUES.join(', ');
89
- if (!Array.isArray(scopes) || scopes.length === 0) {
90
- throw proxyScopesError(`must be a non-empty list drawn from ${allowed}`);
91
- }
92
- for (const scope of scopes) {
93
- if (scope === SCOPES.GLOBAL) {
94
- throw proxyScopesError(
95
- "'global' is not allowed: global entities belong to the app owner and are never proxied for a tenant"
96
- );
97
- }
98
- if (!PROXY_SCOPE_VALUES.includes(scope)) {
99
- throw proxyScopesError(`unknown scope ${JSON.stringify(scope)}; use ${allowed}`);
100
- }
101
- }
102
- if (new Set(scopes).size !== scopes.length) {
103
- throw proxyScopesError('lists a scope more than once');
104
- }
105
- return Object.freeze([...scopes]);
106
- }
107
-
108
- /**
109
- * @param {Object} [managementApi] - `appDefinition.managementApi`
110
- * @returns {{ v1: boolean, proxy: { enable: boolean, timeoutMs: number, maxRequestBytes: number, maxResponseBytes: number, scopes: ReadonlyArray<string>, modules: Object } }}
111
- * @throws {ManagementApiConfigError} for an invalid `proxy.scopes`
112
- */
113
- function resolveManagementApiConfig(managementApi) {
114
- const config =
115
- managementApi && typeof managementApi === 'object' ? managementApi : {};
116
- const proxy =
117
- config.proxy && typeof config.proxy === 'object' ? config.proxy : {};
118
-
119
- return {
120
- v1: config.v1 !== false,
121
- proxy: {
122
- enable: proxy.enable === true,
123
- timeoutMs: positiveCapped(
124
- proxy.timeoutMs,
125
- DEFAULT_PROXY_LIMITS.timeoutMs
126
- ),
127
- maxRequestBytes: positiveOr(
128
- proxy.maxRequestBytes,
129
- DEFAULT_PROXY_LIMITS.maxRequestBytes,
130
- MAX_REQUEST_BYTES_CEILING
131
- ),
132
- maxResponseBytes: positiveCapped(
133
- proxy.maxResponseBytes,
134
- DEFAULT_PROXY_LIMITS.maxResponseBytes
135
- ),
136
- scopes: resolveProxyScopes(proxy.scopes),
137
- modules:
138
- proxy.modules && typeof proxy.modules === 'object'
139
- ? proxy.modules
140
- : {},
141
- },
142
- };
143
- }
144
-
145
- module.exports = {
146
- resolveManagementApiConfig,
147
- DEFAULT_PROXY_LIMITS,
148
- DEFAULT_PROXY_SCOPES,
149
- PROXY_SCOPE_VALUES,
150
- ManagementApiConfigError,
151
- };
@@ -1,76 +0,0 @@
1
- const { Router } = require('express');
2
- const Boom = require('@hapi/boom');
3
- const { resolveManagementApiConfig } = require('./management-api-config');
4
- const {
5
- GetManagementApiMeta,
6
- } = require('./use-cases/get-management-api-meta');
7
- const { isValidAdminApiKey } = require('../handlers/middleware/admin-auth');
8
- const { version: coreVersion } = require('../package.json');
9
- const {
10
- MANAGEMENT_API_VERSIONS,
11
- PREFERRED_API_VERSION,
12
- DOCS_ROUTES,
13
- } = require('./route-registry');
14
- const { buildOpenApiDocument } = require('./openapi/build-openapi');
15
-
16
- /**
17
- * Version-neutral discovery routes (ADR-053 §4, §5), mounted on the DB-free
18
- * health Lambda. No authentication: the admin API key only adds the core
19
- * version to the response.
20
- *
21
- * @param {Object} params
22
- * @param {Object} params.appDefinition - the loaded app definition
23
- * (`managementApi` is read).
24
- */
25
- function createManagementApiMetaRouter({ appDefinition = {} } = {}) {
26
- const config = resolveManagementApiConfig(appDefinition.managementApi);
27
- const getMeta = new GetManagementApiMeta({ config, coreVersion });
28
-
29
- const router = Router();
30
-
31
- router.get('/api/meta', (req, res) => {
32
- const isAdmin = isValidAdminApiKey(req.get('x-frigg-admin-api-key'));
33
- res.set('Cache-Control', 'no-store');
34
- res.json(getMeta.execute({ isAdmin }));
35
- });
36
-
37
- // One OpenAPI document per major (ADR-053 §5). Static per deployment, so
38
- // built once; the entity proxy appears only when the app enables it.
39
- const documents = new Map();
40
- for (const major of Object.keys(MANAGEMENT_API_VERSIONS)) {
41
- router.get(`/api/meta/openapi/v${major}.json`, (_req, res) => {
42
- if (!documents.has(major)) {
43
- documents.set(
44
- major,
45
- buildOpenApiDocument(major, { proxy: config.proxy.enable })
46
- );
47
- }
48
- res.set('Cache-Control', 'public, max-age=300');
49
- res.json(documents.get(major));
50
- });
51
- }
52
-
53
- // Anything else under /api/meta is a v2-shaped 404, not Express's page.
54
- router.use('/api/meta', (req, _res, next) => {
55
- next(Boom.notFound(`No Management API meta route for ${req.method} ${req.originalUrl.split('?')[0]}`));
56
- });
57
-
58
- return router;
59
- }
60
-
61
- /**
62
- * GET /api/docs (DOCS_ROUTES): a redirect to the current OpenAPI document,
63
- * mounted next to the meta router on the DB-free health Lambda. Public, like
64
- * /api/meta.
65
- */
66
- function createDocsRedirectRouter() {
67
- const router = Router();
68
- for (const { path } of DOCS_ROUTES) {
69
- router.get(path, (_req, res) =>
70
- res.redirect(302, `/api/meta/openapi/v${PREFERRED_API_VERSION}.json`)
71
- );
72
- }
73
- return router;
74
- }
75
-
76
- module.exports = { createManagementApiMetaRouter, createDocsRedirectRouter };
@@ -1,144 +0,0 @@
1
- const {
2
- MANAGEMENT_API_VERSIONS,
3
- V1_ROUTES,
4
- META_ROUTES,
5
- V1_MIGRATION_DOCS_URL,
6
- listV2Routes,
7
- toGatewayPath,
8
- } = require('../route-registry');
9
- const { SCHEMAS, RESPONSES, SECURITY_SCHEMES } = require('./components');
10
- const { V2_OPERATIONS, META_OPERATIONS } = require('./v2-operations');
11
- const { V1_OPERATIONS } = require('./v1-operations');
12
- const { version: coreVersion } = require('../../package.json');
13
-
14
- const COMPATIBILITY_RULES = `
15
- Compatibility (ADR-053): within a major, changes are additive only. Clients
16
- must ignore response fields and enum values they do not know. Operations
17
- marked \`x-frigg-stability: beta\` may still change in a core minor.
18
-
19
- Errors: \`{ "error": { "code", "message", "details"? } }\`. Every response
20
- carries \`Frigg-API-Version: 2\`.`;
21
-
22
- const DESCRIPTIONS = {
23
- 1: `The original, unprefixed Management API. Deprecated (ADR-053): every response carries
24
- \`Deprecation\` and \`Link: <${V1_MIGRATION_DOCS_URL}>; rel="deprecation"\`. It is frozen,
25
- and \`managementApi.v1: false\` turns it off. Use v2.`,
26
- 2: `Integrations, entities, credentials, multi-step authorization and the entity proxy, under
27
- \`/api/v2\`. Discover what a deployment serves with \`GET /api/meta\`.
28
- ${COMPATIBILITY_RULES}`,
29
- };
30
-
31
- const TAGS = [
32
- { name: 'Integrations', description: 'Integrations between connected entities' },
33
- { name: 'Entities', description: 'Connected accounts, entity types and the entity proxy' },
34
- { name: 'Credentials', description: 'Stored credentials (always masked) and re-authorization' },
35
- { name: 'Authorization', description: 'Single- and multi-step authorization' },
36
- { name: 'Meta', description: 'Version-neutral discovery' },
37
- ];
38
-
39
- /** Path parameters, derived from the Express path. */
40
- function pathParameters(expressPath) {
41
- return [...expressPath.matchAll(/:([A-Za-z0-9_]+)/g)].map(([, name]) => ({
42
- name,
43
- in: 'path',
44
- required: true,
45
- schema: { type: 'string' },
46
- }));
47
- }
48
-
49
- function operationFor(route, docs, extra = {}) {
50
- if (!docs) {
51
- throw new Error(
52
- `No OpenAPI documentation for ${route.method} ${route.path} (${route.operationId || 'v1'})`
53
- );
54
- }
55
- const parameters = [...pathParameters(route.path), ...(docs.parameters || [])];
56
- return {
57
- operationId: route.operationId,
58
- ...(route.tag && { tags: [route.tag] }),
59
- ...docs,
60
- ...(parameters.length && { parameters }),
61
- ...(route.stability && { 'x-frigg-stability': route.stability }),
62
- ...extra,
63
- };
64
- }
65
-
66
- function addOperation(paths, route, operation) {
67
- const path = toGatewayPath(route.path);
68
- paths[path] = paths[path] || {};
69
- // An ANY route (router.all in v1) is documented as GET and POST, the two
70
- // methods callers use.
71
- const methods = route.method === 'ANY' ? ['get', 'post'] : [route.method.toLowerCase()];
72
- for (const method of methods) {
73
- paths[path][method] = {
74
- ...operation,
75
- ...(methods.length > 1 && {
76
- operationId: `${operation.operationId}${method === 'get' ? '' : 'Post'}`,
77
- }),
78
- };
79
- }
80
- }
81
-
82
- /**
83
- * Builds the OpenAPI 3 document for one Management API major from the route
84
- * registry (ADR-053 §5).
85
- *
86
- * @param {string} major - '1' or '2'
87
- * @param {Object} [options]
88
- * @param {boolean} [options.proxy=true] - include the entity proxy (served
89
- * documents follow managementApi.proxy.enable)
90
- * @returns {Object}
91
- */
92
- function buildOpenApiDocument(major, { proxy = true } = {}) {
93
- const info = MANAGEMENT_API_VERSIONS[major];
94
- if (!info) throw new Error(`Unknown Management API version: ${major}`);
95
-
96
- const paths = {};
97
- if (major === '1') {
98
- for (const route of V1_ROUTES) {
99
- const key = `${route.method} ${route.path}`;
100
- addOperation(
101
- paths,
102
- route,
103
- operationFor({ ...route, operationId: V1_OPERATIONS[key]?.operationId }, V1_OPERATIONS[key], {
104
- deprecated: true,
105
- })
106
- );
107
- }
108
- } else {
109
- for (const route of listV2Routes({ proxy })) {
110
- addOperation(paths, route, operationFor(route, V2_OPERATIONS[route.operationId]));
111
- }
112
- }
113
- for (const route of META_ROUTES) {
114
- addOperation(
115
- paths,
116
- route,
117
- operationFor({ ...route, tag: 'Meta' }, META_OPERATIONS[route.operationId])
118
- );
119
- }
120
-
121
- return {
122
- openapi: '3.0.3',
123
- info: {
124
- title: `Frigg Management API v${major}`,
125
- version: coreVersion,
126
- description: DESCRIPTIONS[major],
127
- license: { name: 'MIT' },
128
- },
129
- 'x-frigg-api-version': major,
130
- 'x-frigg-api-status': info.status,
131
- ...(info.deprecatedAt && { 'x-frigg-deprecated-at': info.deprecatedAt }),
132
- servers: [{ url: '/', description: 'This Frigg deployment' }],
133
- security: [{ bearerAuth: [] }, { friggApiKey: [] }],
134
- tags: TAGS,
135
- paths,
136
- components: {
137
- schemas: SCHEMAS,
138
- responses: RESPONSES,
139
- securitySchemes: SECURITY_SCHEMES,
140
- },
141
- };
142
- }
143
-
144
- module.exports = { buildOpenApiDocument };