@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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 +66 -67
- package/CLAUDE.md +148 -124
- package/README.md +165 -130
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
- package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
- package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
- package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
- package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
- package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
- package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
- package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
- package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
- package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/admin-script-commands.js +348 -0
- package/application/commands/integration-commands.js +12 -6
- package/application/index.js +5 -3
- package/assertions/get.js +17 -7
- package/associations/association.js +59 -57
- package/core/CLAUDE.md +222 -156
- package/core/Worker.js +33 -12
- package/credential/credential-router.js +262 -0
- package/credential/repositories/credential-repository-documentdb.js +71 -1
- package/credential/repositories/credential-repository-factory.js +4 -6
- package/credential/repositories/credential-repository-interface.js +8 -2
- package/credential/repositories/credential-repository-mongo.js +45 -4
- package/credential/repositories/credential-repository-postgres.js +44 -3
- package/credential/repositories/credential-repository.js +3 -1
- package/credential/use-cases/delete-credential-for-user.js +50 -0
- package/credential/use-cases/get-credential-for-user.js +21 -3
- package/credential/use-cases/list-credentials-for-user.js +31 -0
- package/credential/use-cases/reauthorize-credential.js +103 -0
- package/credential/use-cases/update-authentication-status.js +5 -2
- package/database/MONGODB_TRANSACTION_FIX.md +72 -52
- package/database/config.js +13 -59
- package/database/documentdb-encryption-service.js +29 -13
- package/database/documentdb-utils.js +21 -12
- package/database/encryption/README.md +53 -36
- package/database/encryption/encryption-schema-registry.js +29 -23
- package/database/encryption/field-encryption-service.js +15 -4
- package/database/encryption/logger.js +3 -2
- package/database/encryption/prisma-encryption-extension.js +5 -8
- package/database/prisma.js +22 -14
- package/database/repositories/health-check-repository-factory.js +7 -5
- package/database/repositories/health-check-repository-interface.js +15 -5
- package/database/repositories/health-check-repository-postgres.js +1 -1
- package/database/use-cases/check-database-health-use-case.js +3 -2
- package/database/use-cases/check-database-state-use-case.js +8 -7
- package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
- package/database/use-cases/get-migration-status-use-case.js +4 -2
- package/database/use-cases/run-database-migration-use-case.js +41 -9
- package/database/use-cases/test-encryption-use-case.js +4 -5
- package/database/use-cases/trigger-database-migration-use-case.js +9 -8
- package/database/utils/mongodb-collection-utils.js +11 -3
- package/database/utils/mongodb-schema-init.js +10 -4
- package/database/utils/prisma-runner.js +144 -74
- package/database/utils/prisma-schema-parser.js +2 -2
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
- package/encrypt/Cryptor.js +54 -49
- package/encrypt/aes-encryption-key-provider.js +82 -0
- package/encrypt/encryption-key-provider-interface.js +50 -0
- package/errors/fetch-error.js +1 -1
- package/generated/prisma-mongodb/edge.js +59 -4
- package/generated/prisma-mongodb/index-browser.js +49 -1
- package/generated/prisma-mongodb/index.d.ts +4023 -61
- package/generated/prisma-mongodb/index.js +59 -4
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +87 -0
- package/generated/prisma-mongodb/wasm.js +59 -4
- package/generated/prisma-postgresql/edge.js +58 -4
- package/generated/prisma-postgresql/index-browser.js +48 -1
- package/generated/prisma-postgresql/index.d.ts +4738 -54
- package/generated/prisma-postgresql/index.js +58 -4
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +85 -0
- package/generated/prisma-postgresql/wasm.js +58 -4
- package/handlers/WEBHOOKS.md +104 -81
- package/handlers/app-definition-loader.js +53 -10
- package/handlers/app-handler-helpers.js +14 -3
- package/handlers/backend-utils.js +0 -1
- package/handlers/database-migration-handler.js +64 -45
- package/handlers/middleware/admin-auth.js +53 -0
- package/handlers/routers/HEALTHCHECK.md +170 -135
- package/handlers/routers/admin.js +474 -0
- package/handlers/routers/auth.js +51 -9
- package/handlers/routers/db-migration.handler.js +2 -3
- package/handlers/routers/db-migration.js +102 -66
- package/handlers/routers/docs.js +180 -0
- package/handlers/routers/health.js +84 -283
- package/handlers/routers/integration-defined-routers.js +44 -33
- package/handlers/routers/integration-webhook-routers.js +73 -52
- package/handlers/routers/middleware/loadUser.js +39 -0
- package/handlers/routers/middleware/requireAdmin.js +43 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
- package/handlers/routers/user.js +30 -14
- package/handlers/routers/websocket.js +18 -4
- package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
- package/handlers/workers/db-migration.js +74 -35
- package/handlers/workers/integration-defined-workers.js +31 -20
- package/index.js +50 -16
- package/infrastructure/scheduler/index.js +15 -11
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
- package/integrations/WEBHOOK-QUICKSTART.md +21 -14
- package/integrations/index.js +4 -0
- package/integrations/integration-base.js +9 -6
- package/integrations/integration-router.js +1828 -157
- package/integrations/options.js +47 -20
- package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
- package/integrations/repositories/integration-repository-documentdb.js +53 -24
- package/integrations/repositories/integration-repository-factory.js +6 -6
- package/integrations/repositories/integration-repository-interface.js +27 -9
- package/integrations/repositories/integration-repository-postgres.js +3 -3
- package/integrations/repositories/process-repository-factory.js +4 -7
- package/integrations/repositories/process-repository-interface.js +9 -5
- package/integrations/repositories/process-repository-mongo.js +7 -3
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/config-capturing-integration.js +9 -8
- package/integrations/tests/doubles/dummy-integration-class.js +81 -8
- package/integrations/tests/doubles/test-integration-repository.js +51 -11
- package/integrations/use-cases/create-integration.js +36 -32
- package/integrations/use-cases/create-process.js +20 -9
- package/integrations/use-cases/delete-integration-for-user.js +5 -4
- package/integrations/use-cases/execute-proxy-request.js +564 -0
- package/integrations/use-cases/get-integration-for-user.js +25 -10
- package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
- package/integrations/use-cases/get-integrations-for-user.js +4 -1
- package/integrations/use-cases/get-possible-integrations.js +1 -1
- package/integrations/use-cases/get-process.js +5 -6
- package/integrations/use-cases/index.js +3 -1
- package/integrations/use-cases/load-integration-context.js +1 -5
- package/integrations/use-cases/update-process-state.js +10 -8
- package/integrations/utils/map-integration-dto.js +9 -3
- package/jest-setup.js +1 -1
- package/jest-teardown.js +1 -1
- package/lambda/TimeoutCatcher.js +32 -30
- package/lambda/index.js +1 -1
- package/logs/index.js +3 -3
- package/modules/domain/entities/AuthorizationSession.js +131 -0
- package/modules/domain/entities/index.js +10 -0
- package/modules/module.js +4 -9
- package/modules/repositories/authorization-session-repository-factory.js +53 -0
- package/modules/repositories/authorization-session-repository-interface.js +75 -0
- package/modules/repositories/authorization-session-repository-mongo.js +169 -0
- package/modules/repositories/authorization-session-repository-postgres.js +170 -0
- package/modules/repositories/module-repository-documentdb.js +89 -41
- package/modules/repositories/module-repository-factory.js +4 -6
- package/modules/repositories/module-repository-interface.js +13 -0
- package/modules/repositories/module-repository-mongo.js +40 -5
- package/modules/repositories/module-repository-postgres.js +42 -5
- package/modules/repositories/module-repository.js +28 -0
- package/modules/requester/api-key.js +0 -2
- package/modules/requester/basic.js +0 -1
- package/modules/requester/oauth-2.js +3 -14
- package/modules/test/mock-api/definition.js +15 -10
- package/modules/test/mock-api/mocks/hubspot.js +36 -36
- package/modules/tests/doubles/test-module-factory.js +10 -4
- package/modules/tests/doubles/test-module-repository.js +1 -1
- package/modules/use-cases/delete-module-entity.js +23 -0
- package/modules/use-cases/get-authorization-requirements.js +110 -0
- package/modules/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +8 -6
- package/modules/use-cases/get-module-entity-by-id.js +16 -0
- package/modules/use-cases/get-module.js +12 -9
- package/modules/use-cases/process-authorization-callback.js +12 -6
- package/modules/use-cases/process-authorization-step.js +160 -0
- package/modules/use-cases/refresh-entity-options.js +8 -6
- package/modules/use-cases/start-authorization-session.js +86 -0
- package/modules/use-cases/test-module-auth.js +8 -6
- package/modules/use-cases/update-module-entity.js +27 -0
- package/modules/utils/map-module-dto.js +2 -2
- package/openapi/openapi-spec-generator.js +370 -0
- package/openapi/openapi-v1.yaml +1360 -0
- package/openapi/openapi-v2.yaml +1535 -0
- package/package.json +7 -9
- package/prisma-mongodb/schema.prisma +87 -0
- package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
- package/prisma-postgresql/schema.prisma +85 -0
- package/providers/index.js +13 -0
- package/providers/resolve-provider.js +93 -0
- package/queues/index.js +19 -0
- package/queues/providers/index.js +7 -0
- package/queues/providers/netlify-background-provider.js +76 -0
- package/queues/providers/qstash-queue-provider.js +122 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-provider-factory.js +93 -0
- package/queues/queue-provider.js +46 -0
- package/queues/queuer-util.js +33 -25
- package/syncs/repositories/sync-repository-documentdb.js +39 -19
- package/syncs/repositories/sync-repository-factory.js +4 -4
- package/syncs/repositories/sync-repository-interface.js +6 -2
- package/syncs/repositories/sync-repository-postgres.js +19 -13
- package/syncs/sync.js +104 -100
- package/token/repositories/token-repository-factory.js +4 -6
- package/types/assertions/index.d.ts +72 -72
- package/types/core/index.d.ts +98 -53
- package/types/errors/index.d.ts +61 -54
- package/types/eslint-config/index.d.ts +40 -40
- package/types/lambda/index.d.ts +26 -26
- package/types/logs/index.d.ts +4 -4
- package/types/prettier-config/index.d.ts +5 -5
- package/types/test-environment/index.d.ts +14 -14
- package/types/tsconfig.json +95 -93
- package/user/repositories/user-repository-factory.js +4 -4
- package/user/tests/doubles/test-user-repository.js +5 -2
- package/user/use-cases/authenticate-user.js +2 -4
- package/user/use-cases/authenticate-with-shared-secret.js +1 -1
- package/user/use-cases/create-individual-user.js +8 -7
- package/user/use-cases/create-organization-user.js +1 -1
- package/user/use-cases/create-token-for-user-id.js +1 -1
- package/user/use-cases/delete-user.js +47 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
- package/user/use-cases/get-user-from-bearer-token.js +7 -3
- package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
- package/user/use-cases/login-user.js +8 -8
- package/user/user.js +18 -4
- package/utils/backend-path.js +2 -2
- package/utils/index.js +4 -1
- package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
- package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
- package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
- package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
- package/websocket/repositories/websocket-connection-repository.js +26 -20
- package/websocket/websocket-message-sender-interface.js +38 -0
- package/database/adapters/lambda-invoker.js +0 -97
- package/database/repositories/migration-status-repository-s3.js +0 -137
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic OpenAPI Spec Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates OpenAPI specifications dynamically from appDefinition and installed modules.
|
|
5
|
+
* Supports both v1 (legacy) and v2 (current) API versions.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { generateOpenApiSpecV1, generateOpenApiSpecV2 } = require('./openapi-spec-generator');
|
|
9
|
+
* const v1Spec = generateOpenApiSpecV1(appDefinition, { serverUrl });
|
|
10
|
+
* const v2Spec = generateOpenApiSpecV2(appDefinition, { serverUrl });
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const yaml = require('js-yaml');
|
|
16
|
+
|
|
17
|
+
const V1_SPEC_PATH = path.join(__dirname, 'openapi-v1.yaml');
|
|
18
|
+
const V2_SPEC_PATH = path.join(__dirname, 'openapi-v2.yaml');
|
|
19
|
+
|
|
20
|
+
// Separate caches for each version
|
|
21
|
+
const cache = {
|
|
22
|
+
v1: { spec: null, modules: null },
|
|
23
|
+
v2: { spec: null, modules: null },
|
|
24
|
+
legacy: { spec: null, modules: null },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Load a YAML spec file
|
|
29
|
+
* @param {string} specPath - Path to the YAML file
|
|
30
|
+
* @returns {Object} Parsed spec object
|
|
31
|
+
*/
|
|
32
|
+
function loadSpecFile(specPath) {
|
|
33
|
+
if (!fs.existsSync(specPath)) {
|
|
34
|
+
throw new Error(`OpenAPI spec not found: ${specPath}`);
|
|
35
|
+
}
|
|
36
|
+
const specContent = fs.readFileSync(specPath, 'utf8');
|
|
37
|
+
return yaml.load(specContent);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load the base OpenAPI spec (defaults to v2)
|
|
42
|
+
* @deprecated Use loadV1Spec or loadV2Spec instead
|
|
43
|
+
*/
|
|
44
|
+
function loadBaseSpec() {
|
|
45
|
+
return loadSpecFile(V2_SPEC_PATH);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Load the v1 OpenAPI spec
|
|
50
|
+
*/
|
|
51
|
+
function loadV1Spec() {
|
|
52
|
+
return loadSpecFile(V1_SPEC_PATH);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Load the v2 OpenAPI spec
|
|
57
|
+
*/
|
|
58
|
+
function loadV2Spec() {
|
|
59
|
+
return loadSpecFile(V2_SPEC_PATH);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extract module metadata for OpenAPI documentation
|
|
64
|
+
* @param {Object} moduleDefinition - Module definition object
|
|
65
|
+
* @returns {Object} Module metadata
|
|
66
|
+
*/
|
|
67
|
+
function extractModuleMetadata(moduleDefinition) {
|
|
68
|
+
const Definition = moduleDefinition.Definition || moduleDefinition;
|
|
69
|
+
const name = Definition?.getName?.() || Definition?.name || 'unknown';
|
|
70
|
+
const displayName = Definition?.display?.name || name;
|
|
71
|
+
const description =
|
|
72
|
+
Definition?.display?.description || `${displayName} integration`;
|
|
73
|
+
const moduleName = Definition?.moduleName || name;
|
|
74
|
+
|
|
75
|
+
// Extract auth type from first module
|
|
76
|
+
const moduleKeys = Object.keys(Definition?.modules || {});
|
|
77
|
+
const firstModule =
|
|
78
|
+
moduleKeys.length > 0 ? Definition.modules[moduleKeys[0]] : null;
|
|
79
|
+
const authType =
|
|
80
|
+
firstModule?.definition?.getAuthType?.() ||
|
|
81
|
+
firstModule?.authType ||
|
|
82
|
+
'oauth2';
|
|
83
|
+
const stepCount = firstModule?.definition?.getAuthStepCount?.() || 1;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
name,
|
|
87
|
+
displayName,
|
|
88
|
+
description,
|
|
89
|
+
moduleName,
|
|
90
|
+
authType,
|
|
91
|
+
stepCount,
|
|
92
|
+
isMultiStep: stepCount > 1,
|
|
93
|
+
hasOptions: typeof Definition?.Options !== 'undefined',
|
|
94
|
+
hasEvents: typeof Definition?.events !== 'undefined',
|
|
95
|
+
capabilities: firstModule?.definition?.getCapabilities?.() || [],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Enrich spec with installed module information
|
|
101
|
+
* @param {Object} spec - OpenAPI spec object
|
|
102
|
+
* @param {Array} installedModules - Array of module metadata
|
|
103
|
+
* @returns {Object} Enriched spec
|
|
104
|
+
*/
|
|
105
|
+
function enrichSpecWithModules(spec, installedModules) {
|
|
106
|
+
if (!installedModules.length) return spec;
|
|
107
|
+
|
|
108
|
+
// Update ListEntityTypesResponse with actual modules
|
|
109
|
+
if (spec.components?.schemas?.ListEntityTypesResponse) {
|
|
110
|
+
spec.components.schemas.ListEntityTypesResponse.properties.types.example =
|
|
111
|
+
installedModules.map((m) => ({
|
|
112
|
+
type: m.name,
|
|
113
|
+
name: m.displayName,
|
|
114
|
+
description: m.description,
|
|
115
|
+
authType: m.authType,
|
|
116
|
+
isMultiStep: m.isMultiStep,
|
|
117
|
+
stepCount: m.stepCount,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Update IntegrationOption examples
|
|
122
|
+
if (spec.components?.schemas?.IntegrationOption) {
|
|
123
|
+
const examples = installedModules.slice(0, 3).map((m) => ({
|
|
124
|
+
type: m.name,
|
|
125
|
+
name: m.displayName,
|
|
126
|
+
description: m.description,
|
|
127
|
+
hasAuth: true,
|
|
128
|
+
}));
|
|
129
|
+
if (
|
|
130
|
+
spec.components.schemas.ListIntegrationOptionsResponse?.properties
|
|
131
|
+
?.integrations
|
|
132
|
+
) {
|
|
133
|
+
spec.components.schemas.ListIntegrationOptionsResponse.properties.integrations.example =
|
|
134
|
+
examples;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Add module-specific enum values to parameters
|
|
139
|
+
const moduleNames = installedModules.map((m) => m.name);
|
|
140
|
+
if (moduleNames.length > 0) {
|
|
141
|
+
Object.values(spec.paths || {}).forEach((pathItem) => {
|
|
142
|
+
Object.values(pathItem).forEach((operation) => {
|
|
143
|
+
if (operation.parameters) {
|
|
144
|
+
operation.parameters.forEach((param) => {
|
|
145
|
+
if (
|
|
146
|
+
param.name === 'entityType' ||
|
|
147
|
+
param.name === 'typeName' ||
|
|
148
|
+
param.name === 'moduleType'
|
|
149
|
+
) {
|
|
150
|
+
param.schema = param.schema || { type: 'string' };
|
|
151
|
+
param.schema.enum = moduleNames;
|
|
152
|
+
param.schema.example = moduleNames[0];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Add installed modules section to spec info
|
|
161
|
+
const moduleList = installedModules
|
|
162
|
+
.map((m) => `- **${m.displayName}** (\`${m.name}\`): ${m.description}`)
|
|
163
|
+
.join('\n');
|
|
164
|
+
|
|
165
|
+
spec.info.description = `${
|
|
166
|
+
spec.info.description || ''
|
|
167
|
+
}\n\n## Installed Modules\n${moduleList}`;
|
|
168
|
+
|
|
169
|
+
return spec;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Extract installed modules from appDefinition
|
|
174
|
+
* @param {Object} appDefinition - App definition object
|
|
175
|
+
* @returns {Array} Array of module metadata
|
|
176
|
+
*/
|
|
177
|
+
function extractInstalledModules(appDefinition) {
|
|
178
|
+
const installedModules = [];
|
|
179
|
+
|
|
180
|
+
if (appDefinition?.integrations) {
|
|
181
|
+
appDefinition.integrations.forEach((integration) => {
|
|
182
|
+
try {
|
|
183
|
+
const metadata = extractModuleMetadata(integration);
|
|
184
|
+
if (metadata.name !== 'unknown') {
|
|
185
|
+
installedModules.push(metadata);
|
|
186
|
+
}
|
|
187
|
+
} catch (e) {
|
|
188
|
+
// Skip modules that can't be processed
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return installedModules;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Add server URL and generation metadata to spec
|
|
198
|
+
* @param {Object} spec - OpenAPI spec
|
|
199
|
+
* @param {Object} options - Options including serverUrl
|
|
200
|
+
* @param {Array} installedModules - Installed modules for metadata
|
|
201
|
+
* @returns {Object} Updated spec
|
|
202
|
+
*/
|
|
203
|
+
function finalizeSpec(spec, options, installedModules) {
|
|
204
|
+
const { serverUrl } = options;
|
|
205
|
+
|
|
206
|
+
// Add custom server URL if provided
|
|
207
|
+
if (serverUrl) {
|
|
208
|
+
spec.servers = [
|
|
209
|
+
{ url: serverUrl, description: 'Current server' },
|
|
210
|
+
...(spec.servers || []),
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Add generation metadata
|
|
215
|
+
spec.info['x-generated'] = {
|
|
216
|
+
timestamp: new Date().toISOString(),
|
|
217
|
+
moduleCount: installedModules.length,
|
|
218
|
+
modules: installedModules.map((m) => m.name),
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return spec;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate v1 (legacy) OpenAPI spec from appDefinition
|
|
226
|
+
* @param {Object} appDefinition - The app definition containing integrations
|
|
227
|
+
* @param {Object} options - Generation options
|
|
228
|
+
* @returns {Object} Complete v1 OpenAPI specification
|
|
229
|
+
*/
|
|
230
|
+
function generateOpenApiSpecV1(appDefinition = null, options = {}) {
|
|
231
|
+
const { useCache = true, serverUrl = null } = options;
|
|
232
|
+
const modulesKey = JSON.stringify(appDefinition?.integrations);
|
|
233
|
+
|
|
234
|
+
// Return cached spec if available
|
|
235
|
+
if (useCache && cache.v1.spec && cache.v1.modules === modulesKey) {
|
|
236
|
+
// Clone and update server URL if different
|
|
237
|
+
const spec = JSON.parse(JSON.stringify(cache.v1.spec));
|
|
238
|
+
if (serverUrl) {
|
|
239
|
+
spec.servers = [
|
|
240
|
+
{ url: serverUrl, description: 'Current server' },
|
|
241
|
+
...(spec.servers?.filter(
|
|
242
|
+
(s) => s.description !== 'Current server'
|
|
243
|
+
) || []),
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
return spec;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Load v1 spec
|
|
250
|
+
const spec = loadV1Spec();
|
|
251
|
+
|
|
252
|
+
// Extract and enrich with installed modules
|
|
253
|
+
const installedModules = extractInstalledModules(appDefinition);
|
|
254
|
+
if (installedModules.length > 0) {
|
|
255
|
+
enrichSpecWithModules(spec, installedModules);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Finalize spec
|
|
259
|
+
finalizeSpec(spec, { serverUrl }, installedModules);
|
|
260
|
+
|
|
261
|
+
// Cache result
|
|
262
|
+
if (useCache) {
|
|
263
|
+
cache.v1.spec = JSON.parse(JSON.stringify(spec));
|
|
264
|
+
cache.v1.modules = modulesKey;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return spec;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Generate v2 (current) OpenAPI spec from appDefinition
|
|
272
|
+
* @param {Object} appDefinition - The app definition containing integrations
|
|
273
|
+
* @param {Object} options - Generation options
|
|
274
|
+
* @returns {Object} Complete v2 OpenAPI specification
|
|
275
|
+
*/
|
|
276
|
+
function generateOpenApiSpecV2(appDefinition = null, options = {}) {
|
|
277
|
+
const { useCache = true, serverUrl = null } = options;
|
|
278
|
+
const modulesKey = JSON.stringify(appDefinition?.integrations);
|
|
279
|
+
|
|
280
|
+
// Return cached spec if available
|
|
281
|
+
if (useCache && cache.v2.spec && cache.v2.modules === modulesKey) {
|
|
282
|
+
// Clone and update server URL if different
|
|
283
|
+
const spec = JSON.parse(JSON.stringify(cache.v2.spec));
|
|
284
|
+
if (serverUrl) {
|
|
285
|
+
spec.servers = [
|
|
286
|
+
{ url: serverUrl, description: 'Current server' },
|
|
287
|
+
...(spec.servers?.filter(
|
|
288
|
+
(s) => s.description !== 'Current server'
|
|
289
|
+
) || []),
|
|
290
|
+
];
|
|
291
|
+
}
|
|
292
|
+
return spec;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Load v2 spec
|
|
296
|
+
const spec = loadV2Spec();
|
|
297
|
+
|
|
298
|
+
// Extract and enrich with installed modules
|
|
299
|
+
const installedModules = extractInstalledModules(appDefinition);
|
|
300
|
+
if (installedModules.length > 0) {
|
|
301
|
+
enrichSpecWithModules(spec, installedModules);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Finalize spec
|
|
305
|
+
finalizeSpec(spec, { serverUrl }, installedModules);
|
|
306
|
+
|
|
307
|
+
// Cache result
|
|
308
|
+
if (useCache) {
|
|
309
|
+
cache.v2.spec = JSON.parse(JSON.stringify(spec));
|
|
310
|
+
cache.v2.modules = modulesKey;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return spec;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Generate OpenAPI spec (defaults to v2 for backwards compatibility)
|
|
318
|
+
* @deprecated Use generateOpenApiSpecV1 or generateOpenApiSpecV2 instead
|
|
319
|
+
* @param {Object} appDefinition - The app definition containing integrations
|
|
320
|
+
* @param {Object} options - Generation options
|
|
321
|
+
* @returns {Object} Complete OpenAPI specification
|
|
322
|
+
*/
|
|
323
|
+
function generateOpenApiSpec(appDefinition = null, options = {}) {
|
|
324
|
+
return generateOpenApiSpecV2(appDefinition, options);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Clear all cached specs
|
|
329
|
+
*/
|
|
330
|
+
function clearCache() {
|
|
331
|
+
cache.v1.spec = null;
|
|
332
|
+
cache.v1.modules = null;
|
|
333
|
+
cache.v2.spec = null;
|
|
334
|
+
cache.v2.modules = null;
|
|
335
|
+
cache.legacy.spec = null;
|
|
336
|
+
cache.legacy.modules = null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get spec as YAML string
|
|
341
|
+
* @param {Object} appDefinition - App definition
|
|
342
|
+
* @param {Object} options - Options including version ('v1' or 'v2')
|
|
343
|
+
* @returns {string} YAML string
|
|
344
|
+
*/
|
|
345
|
+
function generateOpenApiYaml(appDefinition = null, options = {}) {
|
|
346
|
+
const { version = 'v2', ...restOptions } = options;
|
|
347
|
+
const spec =
|
|
348
|
+
version === 'v1'
|
|
349
|
+
? generateOpenApiSpecV1(appDefinition, restOptions)
|
|
350
|
+
: generateOpenApiSpecV2(appDefinition, restOptions);
|
|
351
|
+
return yaml.dump(spec);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
// Primary exports for v1/v2
|
|
356
|
+
generateOpenApiSpecV1,
|
|
357
|
+
generateOpenApiSpecV2,
|
|
358
|
+
|
|
359
|
+
// Legacy/utility exports
|
|
360
|
+
generateOpenApiSpec,
|
|
361
|
+
generateOpenApiYaml,
|
|
362
|
+
clearCache,
|
|
363
|
+
extractModuleMetadata,
|
|
364
|
+
|
|
365
|
+
// Internal utilities (exported for testing)
|
|
366
|
+
loadBaseSpec,
|
|
367
|
+
loadV1Spec,
|
|
368
|
+
loadV2Spec,
|
|
369
|
+
enrichSpecWithModules,
|
|
370
|
+
};
|