@things-factory/integration-base 10.1.117 → 10.1.118
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/dist-server/engine/connector/headless-connector.d.ts +1 -0
- package/dist-server/engine/connector/headless-connector.js +5 -1
- package/dist-server/engine/connector/headless-connector.js.map +1 -1
- package/dist-server/engine/index.d.ts +2 -0
- package/dist-server/engine/index.js +5 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/integration-runtime.d.ts +47 -0
- package/dist-server/engine/integration-runtime.js +78 -0
- package/dist-server/engine/integration-runtime.js.map +1 -0
- package/dist-server/engine/resource-pool/headless-pool.js +6 -1
- package/dist-server/engine/resource-pool/headless-pool.js.map +1 -1
- package/dist-server/engine/scenario-engine.js +4 -0
- package/dist-server/engine/scenario-engine.js.map +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +8 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/routers/scenario-schedule-run.d.ts +2 -1
- package/dist-server/routers/scenario-schedule-run.js +9 -0
- package/dist-server/routers/scenario-schedule-run.js.map +1 -1
- package/dist-server/service/connection/connection.js +3 -0
- package/dist-server/service/connection/connection.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario-engine.js","sourceRoot":"","sources":["../../server/engine/scenario-engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository, Domain, GraphqlLocalClient, pubsub } from '@things-factory/shell'\n\nimport { Scenario } from '../service/scenario/scenario'\nimport {\n ScenarioInstance,\n ScenarioInstanceRunResult,\n ScenarioInstanceStartResult,\n ScenarioInstanceStatus\n} from '../service/scenario-instance/scenario-instance-type'\n\nimport { PendingQueue } from './pending-queue'\nimport { User } from '@things-factory/auth-base'\nimport { resolveRunnableSteps } from '../service/scenario/resolve-steps'\n\nconst debug = require('debug')('things-factory:integration-base:scenario-engine')\n\nexport class ScenarioEngine {\n private static scenarioInstances = {} // TODO impl. by WeakMap\n private static pendingQueues = {}\n\n public static getPendingQueue(domain) {\n if (!ScenarioEngine.pendingQueues[domain.id]) {\n ScenarioEngine.pendingQueues[domain.id] = new PendingQueue(queue => {\n pubsub.publish('scenario-queue-state', {\n scenarioQueueState: {\n domain,\n queue\n }\n })\n })\n }\n\n return ScenarioEngine.pendingQueues[domain.id]\n }\n\n public static getScenarioInstance(domain, instanceName): ScenarioInstance {\n return ScenarioEngine.scenarioInstances[domain.id]?.[instanceName]\n }\n\n public static getScenarioInstances(domain, scenarioName?): ScenarioInstance[] {\n const instances: ScenarioInstance[] = Object.values(ScenarioEngine.scenarioInstances[domain.id] || {}) || []\n return instances.filter(instance => !scenarioName || instance.scenarioName == scenarioName)\n }\n\n public static async load(instanceName, scenarioConfig, context?) {\n const { domain: scenarioDomain } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n\n debug('load', domain.subdomain, instanceName)\n\n var scenarioInstances = ScenarioEngine.scenarioInstances[domain.id]\n if (!scenarioInstances) {\n scenarioInstances = ScenarioEngine.scenarioInstances[domain.id] = {}\n }\n\n var scenarioInstance = scenarioInstances[instanceName]\n if (scenarioInstance) {\n debug('load - duplicated', instanceName)\n return scenarioInstance\n }\n\n var instance = new ScenarioInstance(instanceName, scenarioConfig, {\n ...context,\n user:\n context?.user ||\n (await getRepository(User).findOne({\n where: {\n id: scenarioConfig.creatorId\n }\n })),\n client: GraphqlLocalClient.client,\n disposer: () => {\n this.unload(domain, instanceName)\n }\n })\n\n instance.start().catch(err => {\n /* IMPORTANT. if you didn't catch exception here, system will be exit */\n console.error(\n `An exception occurred while executing the scenario instance named \"${instanceName}\" on the domain \"${domain.name}\"`,\n err\n )\n })\n\n scenarioInstances[instanceName] = instance\n\n return instance\n }\n\n public static async run(instanceName, scenarioConfig, context?): Promise<ScenarioInstanceRunResult> {\n try {\n return await ScenarioEngine.runInstance(instanceName, scenarioConfig, context)\n } catch (err) {\n const domain = context?.domain || scenarioConfig?.domain\n console.error(\n `An exception occurred while executing the scenario instance named \"${instanceName}\" on the domain \"${domain?.name}\"`,\n err\n )\n }\n }\n\n /** Whether an instance by this name is loaded on this domain in this process. */\n public static isRunning(domainId: string, instanceName: string): boolean {\n return !!ScenarioEngine.scenarioInstances[domainId]?.[instanceName]\n }\n\n /**\n * Run a scenario across its iteration's domains **and wait**, saying what happened (ADR-0110 adoption 2, step 5).\n *\n * `loadIteration` returns at once and `run` swallows a failure, so a scheduled run could never tell the\n * scheduler's discipline that it failed. This one awaits every domain and returns the failures by domain.\n * A domain where an instance by that name is already loaded in this process is not run again and is named in\n * `alreadyRunning` — `run` would hand back the running instance and report nothing.\n */\n public static async runIteration(\n instanceName,\n scenarioConfig,\n context?\n ): Promise<{ domains: number; failed: { subdomain: string; error: string }[]; alreadyRunning: string[] }> {\n const { domain: scenarioDomain, iteration = 'SELF' } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n const targetDomains = await ScenarioEngine.getTargetDomains(domain, iteration)\n\n const failed: { subdomain: string; error: string }[] = []\n const alreadyRunning: string[] = []\n\n for (const target of targetDomains) {\n if (ScenarioEngine.isRunning(target.id, instanceName)) {\n alreadyRunning.push(target.subdomain)\n continue\n }\n try {\n await ScenarioEngine.runInstance(instanceName, { ...scenarioConfig, domain: target }, { ...context, domain: target })\n } catch (error) {\n failed.push({ subdomain: target.subdomain, error: String((error as any)?.message ?? error).slice(0, 300) })\n }\n }\n\n return { domains: targetDomains.length, failed, alreadyRunning }\n }\n\n /** Create and run one instance. Throws what the run throws. */\n private static async runInstance(instanceName, scenarioConfig, context?): Promise<ScenarioInstanceRunResult> {\n const { domain: scenarioDomain } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n\n debug('run', domain.subdomain, instanceName)\n\n var scenarioInstances = ScenarioEngine.scenarioInstances[domain.id]\n if (!scenarioInstances) {\n scenarioInstances = ScenarioEngine.scenarioInstances[domain.id] = {}\n }\n\n var scenarioInstance = scenarioInstances[instanceName]\n if (scenarioInstance) {\n debug('load - duplicated', instanceName)\n return scenarioInstance\n }\n\n var instance = new ScenarioInstance(instanceName, scenarioConfig, {\n ...context,\n user:\n context?.user ||\n (await getRepository(User).findOne({\n where: {\n id: scenarioConfig.creatorId\n }\n })),\n client: GraphqlLocalClient.client,\n disposer: () => {\n this.unload(domain, instanceName)\n }\n })\n\n scenarioInstances[instanceName] = instance\n\n return await instance.run()\n }\n\n /**\n * Loads and executes a scenario across multiple domains based on iteration\n * @param instanceName - The name for the scenario instance\n * @param scenarioConfig - The scenario configuration\n * @param context - The execution context\n * @returns Promise that resolves immediately, but scenarios execute sequentially\n */\n public static async loadIteration(instanceName, scenarioConfig, context?): Promise<ScenarioInstanceStartResult> {\n const { domain: scenarioDomain, iteration = 'SELF' } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n\n debug('load-iteration', domain.subdomain, instanceName, iteration)\n\n // Get target domains based on iteration\n const targetDomains = await ScenarioEngine.getTargetDomains(domain, iteration)\n\n // Start sequential execution in background\n ScenarioEngine.executeSequentially(instanceName, scenarioConfig, context, targetDomains)\n\n // Return immediately with execution info\n return {\n state: ScenarioInstanceStatus.STARTED,\n message: `Scenario iteration started for ${targetDomains.length} domains`,\n timestamp: new Date()\n }\n }\n\n /**\n * Executes scenarios sequentially across domains\n * @param instanceName - The name for the scenario instance\n * @param scenarioConfig - The scenario configuration\n * @param context - The execution context\n * @param targetDomains - Array of target domains\n */\n private static async executeSequentially(instanceName, scenarioConfig, context, targetDomains) {\n const results = []\n\n for (let i = 0; i < targetDomains.length; i++) {\n const targetDomain = targetDomains[i]\n\n try {\n console.log(`Executing scenario on domain ${targetDomain.subdomain} (${i + 1}/${targetDomains.length})`)\n\n const result = await ScenarioEngine.run(\n instanceName,\n {\n ...scenarioConfig,\n domain: targetDomain\n },\n {\n ...context,\n domain: targetDomain\n }\n )\n\n results.push(result)\n\n console.log(`Completed scenario on domain ${targetDomain.subdomain}`)\n } catch (error) {\n console.error(`Failed to execute scenario on domain ${targetDomain.subdomain}:`, error)\n }\n }\n\n return results[0]\n }\n\n /**\n * Gets target domains based on iteration\n * @param domain - The source domain\n * @param iteration - The iteration (SELF, CHILDREN, SELF & CHILDREN)\n * @returns Array of target domains\n */\n private static async getTargetDomains(domain: any, iteration: string): Promise<any[]> {\n switch (iteration) {\n case 'SELF':\n return [domain]\n\n case 'CHILDREN':\n const childDomains = await getRepository(Domain).find({\n where: { parent: { id: domain.id } }\n })\n return childDomains\n\n case 'SELF & CHILDREN':\n const allDomains = await getRepository(Domain).find({\n where: [{ id: domain.id }, { parent: { id: domain.id } }]\n })\n return allDomains\n\n default:\n return [domain]\n }\n }\n\n public static async unload(domain, instanceName) {\n var scenarioInstances = ScenarioEngine.scenarioInstances[domain.id]\n if (!scenarioInstances) {\n return\n }\n\n var instance = scenarioInstances[instanceName]\n if (!instance) {\n return\n }\n\n delete scenarioInstances[instanceName]\n\n instance.unload()\n }\n\n // will be deprecated soon\n public static async loadAll() {\n const SCENARIOS = await getRepository(Scenario).find({\n where: { active: true },\n relations: ['domain']\n })\n\n debug(\n 'load-all',\n SCENARIOS.map(scenario => `${scenario.domain.subdomain}: ${scenario.name}`)\n )\n\n // [버전관리] 부팅 시 자동 실행은 운영 실행이므로 released 스냅샷을 사용(없으면 draft 폴백).\n for (const scenario of SCENARIOS) {\n scenario.steps = await resolveRunnableSteps(scenario, { mode: 'released' })\n ScenarioEngine.loadIteration(scenario.name, scenario)\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scenario-engine.js","sourceRoot":"","sources":["../../server/engine/scenario-engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository, Domain, GraphqlLocalClient, pubsub } from '@things-factory/shell'\n\nimport { Scenario } from '../service/scenario/scenario'\nimport {\n ScenarioInstance,\n ScenarioInstanceRunResult,\n ScenarioInstanceStartResult,\n ScenarioInstanceStatus\n} from '../service/scenario-instance/scenario-instance-type'\n\nimport { PendingQueue } from './pending-queue'\nimport { IntegrationRuntime } from './integration-runtime'\nimport { User } from '@things-factory/auth-base'\nimport { resolveRunnableSteps } from '../service/scenario/resolve-steps'\n\nconst debug = require('debug')('things-factory:integration-base:scenario-engine')\n\nexport class ScenarioEngine {\n private static scenarioInstances = {} // TODO impl. by WeakMap\n private static pendingQueues = {}\n\n public static getPendingQueue(domain) {\n if (!ScenarioEngine.pendingQueues[domain.id]) {\n ScenarioEngine.pendingQueues[domain.id] = new PendingQueue(queue => {\n pubsub.publish('scenario-queue-state', {\n scenarioQueueState: {\n domain,\n queue\n }\n })\n })\n }\n\n return ScenarioEngine.pendingQueues[domain.id]\n }\n\n public static getScenarioInstance(domain, instanceName): ScenarioInstance {\n return ScenarioEngine.scenarioInstances[domain.id]?.[instanceName]\n }\n\n public static getScenarioInstances(domain, scenarioName?): ScenarioInstance[] {\n const instances: ScenarioInstance[] = Object.values(ScenarioEngine.scenarioInstances[domain.id] || {}) || []\n return instances.filter(instance => !scenarioName || instance.scenarioName == scenarioName)\n }\n\n public static async load(instanceName, scenarioConfig, context?) {\n IntegrationRuntime.assertEnabled(`scenario \"${instanceName}\"`)\n const { domain: scenarioDomain } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n\n debug('load', domain.subdomain, instanceName)\n\n var scenarioInstances = ScenarioEngine.scenarioInstances[domain.id]\n if (!scenarioInstances) {\n scenarioInstances = ScenarioEngine.scenarioInstances[domain.id] = {}\n }\n\n var scenarioInstance = scenarioInstances[instanceName]\n if (scenarioInstance) {\n debug('load - duplicated', instanceName)\n return scenarioInstance\n }\n\n var instance = new ScenarioInstance(instanceName, scenarioConfig, {\n ...context,\n user:\n context?.user ||\n (await getRepository(User).findOne({\n where: {\n id: scenarioConfig.creatorId\n }\n })),\n client: GraphqlLocalClient.client,\n disposer: () => {\n this.unload(domain, instanceName)\n }\n })\n\n instance.start().catch(err => {\n /* IMPORTANT. if you didn't catch exception here, system will be exit */\n console.error(\n `An exception occurred while executing the scenario instance named \"${instanceName}\" on the domain \"${domain.name}\"`,\n err\n )\n })\n\n scenarioInstances[instanceName] = instance\n\n return instance\n }\n\n public static async run(instanceName, scenarioConfig, context?): Promise<ScenarioInstanceRunResult> {\n try {\n return await ScenarioEngine.runInstance(instanceName, scenarioConfig, context)\n } catch (err) {\n const domain = context?.domain || scenarioConfig?.domain\n console.error(\n `An exception occurred while executing the scenario instance named \"${instanceName}\" on the domain \"${domain?.name}\"`,\n err\n )\n }\n }\n\n /** Whether an instance by this name is loaded on this domain in this process. */\n public static isRunning(domainId: string, instanceName: string): boolean {\n return !!ScenarioEngine.scenarioInstances[domainId]?.[instanceName]\n }\n\n /**\n * Run a scenario across its iteration's domains **and wait**, saying what happened (ADR-0110 adoption 2, step 5).\n *\n * `loadIteration` returns at once and `run` swallows a failure, so a scheduled run could never tell the\n * scheduler's discipline that it failed. This one awaits every domain and returns the failures by domain.\n * A domain where an instance by that name is already loaded in this process is not run again and is named in\n * `alreadyRunning` — `run` would hand back the running instance and report nothing.\n */\n public static async runIteration(\n instanceName,\n scenarioConfig,\n context?\n ): Promise<{ domains: number; failed: { subdomain: string; error: string }[]; alreadyRunning: string[] }> {\n IntegrationRuntime.assertEnabled(`scenario \"${instanceName}\"`)\n const { domain: scenarioDomain, iteration = 'SELF' } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n const targetDomains = await ScenarioEngine.getTargetDomains(domain, iteration)\n\n const failed: { subdomain: string; error: string }[] = []\n const alreadyRunning: string[] = []\n\n for (const target of targetDomains) {\n if (ScenarioEngine.isRunning(target.id, instanceName)) {\n alreadyRunning.push(target.subdomain)\n continue\n }\n try {\n await ScenarioEngine.runInstance(instanceName, { ...scenarioConfig, domain: target }, { ...context, domain: target })\n } catch (error) {\n failed.push({ subdomain: target.subdomain, error: String((error as any)?.message ?? error).slice(0, 300) })\n }\n }\n\n return { domains: targetDomains.length, failed, alreadyRunning }\n }\n\n /** Create and run one instance. Throws what the run throws. */\n private static async runInstance(instanceName, scenarioConfig, context?): Promise<ScenarioInstanceRunResult> {\n const { domain: scenarioDomain } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n\n debug('run', domain.subdomain, instanceName)\n\n var scenarioInstances = ScenarioEngine.scenarioInstances[domain.id]\n if (!scenarioInstances) {\n scenarioInstances = ScenarioEngine.scenarioInstances[domain.id] = {}\n }\n\n var scenarioInstance = scenarioInstances[instanceName]\n if (scenarioInstance) {\n debug('load - duplicated', instanceName)\n return scenarioInstance\n }\n\n var instance = new ScenarioInstance(instanceName, scenarioConfig, {\n ...context,\n user:\n context?.user ||\n (await getRepository(User).findOne({\n where: {\n id: scenarioConfig.creatorId\n }\n })),\n client: GraphqlLocalClient.client,\n disposer: () => {\n this.unload(domain, instanceName)\n }\n })\n\n scenarioInstances[instanceName] = instance\n\n return await instance.run()\n }\n\n /**\n * Loads and executes a scenario across multiple domains based on iteration\n * @param instanceName - The name for the scenario instance\n * @param scenarioConfig - The scenario configuration\n * @param context - The execution context\n * @returns Promise that resolves immediately, but scenarios execute sequentially\n */\n public static async loadIteration(instanceName, scenarioConfig, context?): Promise<ScenarioInstanceStartResult> {\n IntegrationRuntime.assertEnabled(`scenario \"${instanceName}\"`)\n const { domain: scenarioDomain, iteration = 'SELF' } = scenarioConfig\n const domain = context?.domain || scenarioDomain\n\n debug('load-iteration', domain.subdomain, instanceName, iteration)\n\n // Get target domains based on iteration\n const targetDomains = await ScenarioEngine.getTargetDomains(domain, iteration)\n\n // Start sequential execution in background\n ScenarioEngine.executeSequentially(instanceName, scenarioConfig, context, targetDomains)\n\n // Return immediately with execution info\n return {\n state: ScenarioInstanceStatus.STARTED,\n message: `Scenario iteration started for ${targetDomains.length} domains`,\n timestamp: new Date()\n }\n }\n\n /**\n * Executes scenarios sequentially across domains\n * @param instanceName - The name for the scenario instance\n * @param scenarioConfig - The scenario configuration\n * @param context - The execution context\n * @param targetDomains - Array of target domains\n */\n private static async executeSequentially(instanceName, scenarioConfig, context, targetDomains) {\n const results = []\n\n for (let i = 0; i < targetDomains.length; i++) {\n const targetDomain = targetDomains[i]\n\n try {\n console.log(`Executing scenario on domain ${targetDomain.subdomain} (${i + 1}/${targetDomains.length})`)\n\n const result = await ScenarioEngine.run(\n instanceName,\n {\n ...scenarioConfig,\n domain: targetDomain\n },\n {\n ...context,\n domain: targetDomain\n }\n )\n\n results.push(result)\n\n console.log(`Completed scenario on domain ${targetDomain.subdomain}`)\n } catch (error) {\n console.error(`Failed to execute scenario on domain ${targetDomain.subdomain}:`, error)\n }\n }\n\n return results[0]\n }\n\n /**\n * Gets target domains based on iteration\n * @param domain - The source domain\n * @param iteration - The iteration (SELF, CHILDREN, SELF & CHILDREN)\n * @returns Array of target domains\n */\n private static async getTargetDomains(domain: any, iteration: string): Promise<any[]> {\n switch (iteration) {\n case 'SELF':\n return [domain]\n\n case 'CHILDREN':\n const childDomains = await getRepository(Domain).find({\n where: { parent: { id: domain.id } }\n })\n return childDomains\n\n case 'SELF & CHILDREN':\n const allDomains = await getRepository(Domain).find({\n where: [{ id: domain.id }, { parent: { id: domain.id } }]\n })\n return allDomains\n\n default:\n return [domain]\n }\n }\n\n public static async unload(domain, instanceName) {\n var scenarioInstances = ScenarioEngine.scenarioInstances[domain.id]\n if (!scenarioInstances) {\n return\n }\n\n var instance = scenarioInstances[instanceName]\n if (!instance) {\n return\n }\n\n delete scenarioInstances[instanceName]\n\n instance.unload()\n }\n\n // will be deprecated soon\n public static async loadAll() {\n const SCENARIOS = await getRepository(Scenario).find({\n where: { active: true },\n relations: ['domain']\n })\n\n debug(\n 'load-all',\n SCENARIOS.map(scenario => `${scenario.domain.subdomain}: ${scenario.name}`)\n )\n\n // [버전관리] 부팅 시 자동 실행은 운영 실행이므로 released 스냅샷을 사용(없으면 draft 폴백).\n for (const scenario of SCENARIOS) {\n scenario.steps = await resolveRunnableSteps(scenario, { mode: 'released' })\n ScenarioEngine.loadIteration(scenario.name, scenario)\n }\n }\n}\n"]}
|
package/dist-server/index.d.ts
CHANGED
package/dist-server/index.js
CHANGED
|
@@ -8,7 +8,15 @@ tslib_1.__exportStar(require("./service/index.js"), exports);
|
|
|
8
8
|
require("./routes.js");
|
|
9
9
|
require("./restful/index.js"); /* APIs */
|
|
10
10
|
const index_js_1 = require("./engine/index.js");
|
|
11
|
+
const integration_runtime_js_1 = require("./engine/integration-runtime.js");
|
|
12
|
+
tslib_1.__exportStar(require("./engine/integration-runtime.js"), exports);
|
|
11
13
|
process.on('bootstrap-module-start', async ({ app, config, client }) => {
|
|
14
|
+
integration_runtime_js_1.IntegrationRuntime.markBooted();
|
|
15
|
+
if (!integration_runtime_js_1.IntegrationRuntime.enabled) {
|
|
16
|
+
/* Integration runs in another process; this one connects nothing and starts no scenario. */
|
|
17
|
+
console.log(`Scenario Engine is off in this process — ${integration_runtime_js_1.IntegrationRuntime.reason}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
12
20
|
try {
|
|
13
21
|
await index_js_1.ConnectionManager.ready();
|
|
14
22
|
await index_js_1.ScenarioEngine.loadAll();
|
package/dist-server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,gEAAqC;AACrC,4DAAiC;AACjC,iEAAsC;AACtC,6DAAkC;AAElC,uBAAoB;AACpB,8BAA2B,CAAC,UAAU;AAEtC,gDAAqE;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,gEAAqC;AACrC,4DAAiC;AACjC,iEAAsC;AACtC,6DAAkC;AAElC,uBAAoB;AACpB,8BAA2B,CAAC,UAAU;AAEtC,gDAAqE;AACrE,4EAAoE;AAEpE,0EAA+C;AAE/C,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAO,EAAE,EAAE;IACjF,2CAAkB,CAAC,UAAU,EAAE,CAAA;IAC/B,IAAI,CAAC,2CAAkB,CAAC,OAAO,EAAE,CAAC;QAChC,4FAA4F;QAC5F,OAAO,CAAC,GAAG,CAAC,4CAA4C,2CAAkB,CAAC,MAAM,EAAE,CAAC,CAAA;QACpF,OAAM;IACR,CAAC;IACD,IAAI,CAAC;QACH,MAAM,4BAAiB,CAAC,KAAK,EAAE,CAAA;QAC/B,MAAM,yBAAc,CAAC,OAAO,EAAE,CAAA;QAE9B,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;IAClD,CAAC;IAAC,OAAO,EAAE,EAAE,CAAC;QACZ,4BAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE,EAAE,CAAC,CAAA;IACjF,CAAC;AACH,CAAC,CAAC,CAAA","sourcesContent":["export * from './migrations/index.js'\nexport * from './engine/index.js'\nexport * from './controllers/index.js'\nexport * from './service/index.js'\n\nimport './routes.js'\nimport './restful/index.js' /* APIs */\n\nimport { ConnectionManager, ScenarioEngine } from './engine/index.js'\nimport { IntegrationRuntime } from './engine/integration-runtime.js'\n\nexport * from './engine/integration-runtime.js'\n\nprocess.on('bootstrap-module-start' as any, async ({ app, config, client }: any) => {\n IntegrationRuntime.markBooted()\n if (!IntegrationRuntime.enabled) {\n /* Integration runs in another process; this one connects nothing and starts no scenario. */\n console.log(`Scenario Engine is off in this process — ${IntegrationRuntime.reason}`)\n return\n }\n try {\n await ConnectionManager.ready()\n await ScenarioEngine.loadAll()\n\n console.log('Scenario Engine has just started.')\n } catch (ex) {\n ConnectionManager.logger.error('Just has failed to start Scenario Engine.', ex)\n }\n})\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { startLeasedRun, type LeasedRunAnswer } from '@things-factory/task-lease';
|
|
2
|
+
import { INTEGRATION_RUNTIME_OFF } from '../engine/integration-runtime';
|
|
2
3
|
/** Scheduled runs of one scenario are not closer together than this. */
|
|
3
4
|
export declare const SCHEDULE_FLOOR_MS = 5000;
|
|
4
5
|
export declare const scheduleLeaseName: (domainId: string, scenarioId: string) => string;
|
|
@@ -6,7 +7,7 @@ export type ScheduledRunAnswer = {
|
|
|
6
7
|
ran: true;
|
|
7
8
|
} | {
|
|
8
9
|
ran: false;
|
|
9
|
-
why: 'scenario-schedule-held' | 'scenario-schedule-lease-error';
|
|
10
|
+
why: 'scenario-schedule-held' | 'scenario-schedule-lease-error' | typeof INTEGRATION_RUNTIME_OFF;
|
|
10
11
|
detail?: string;
|
|
11
12
|
};
|
|
12
13
|
export interface ScheduledRunLog {
|
|
@@ -5,6 +5,7 @@ exports.scheduledRunAnswerOf = scheduledRunAnswerOf;
|
|
|
5
5
|
exports.startScheduledScenario = startScheduledScenario;
|
|
6
6
|
const task_lease_1 = require("@things-factory/task-lease");
|
|
7
7
|
const scenario_engine_1 = require("../engine/scenario-engine");
|
|
8
|
+
const integration_runtime_1 = require("../engine/integration-runtime");
|
|
8
9
|
/*
|
|
9
10
|
* A scheduled scenario run under the shared discipline (ADR-0110 adoption 2, step 5, 보탬 3).
|
|
10
11
|
*
|
|
@@ -40,6 +41,14 @@ function scheduledRunAnswerOf(answer) {
|
|
|
40
41
|
/** Start the scheduled run of one scenario, or say why not. The run itself goes on after this returns. */
|
|
41
42
|
async function startScheduledScenario(scenario, config, context, log, start = task_lease_1.startLeasedRun) {
|
|
42
43
|
const where = `${context.domain.subdomain}:${scenario.name}`;
|
|
44
|
+
/*
|
|
45
|
+
* Integration does not run in this process. Answered like any refusal — 200 with the reason — so the
|
|
46
|
+
* scheduler does not retry it every minute; the callback address belongs on the process where it runs.
|
|
47
|
+
*/
|
|
48
|
+
if (!integration_runtime_1.IntegrationRuntime.enabled) {
|
|
49
|
+
log.warn(`[scenario-schedule] "${where}" was called back on a process where integration is off — ${integration_runtime_1.IntegrationRuntime.reason}`);
|
|
50
|
+
return { ran: false, why: integration_runtime_1.INTEGRATION_RUNTIME_OFF, detail: integration_runtime_1.IntegrationRuntime.reason };
|
|
51
|
+
}
|
|
43
52
|
const answer = await start({
|
|
44
53
|
name: (0, exports.scheduleLeaseName)(context.domain.id, String(scenario.id)),
|
|
45
54
|
floorMs: exports.SCHEDULE_FLOOR_MS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario-schedule-run.js","sourceRoot":"","sources":["../../server/routers/scenario-schedule-run.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"scenario-schedule-run.js","sourceRoot":"","sources":["../../server/routers/scenario-schedule-run.ts"],"names":[],"mappings":"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","sourcesContent":["import { startLeasedRun, type LeasedRunAnswer } from '@things-factory/task-lease'\n\nimport { ScenarioEngine } from '../engine/scenario-engine'\nimport { IntegrationRuntime, INTEGRATION_RUNTIME_OFF } from '../engine/integration-runtime'\n\n/*\n * A scheduled scenario run under the shared discipline (ADR-0110 adoption 2, step 5, 보탬 3).\n *\n * The scheduler calls back on whichever instance sits behind its URL, on the cron's beat, whether or not the last\n * run has ended. Before this, every callback started a run: two instances both ran it, a run still going was\n * silently joined (the engine handed back the running instance and said STARTED), and a failing run was started\n * again on the next beat at full rate.\n *\n * one at a time lease `scenario-schedule:<domain>:<scenario>`; a callback that finds it held is refused\n * floor after a run, callbacks closer than SCHEDULE_FLOOR_MS are refused\n * backoff after a failed run, callbacks are refused for the backoff of the consecutive failures\n *\n * A refusal answers 200 with its reason. The scheduler retries any other answer every 60 seconds without end\n * (`retry_dlq`, `max_retry_count: -1`), which would turn one refused overlap into a stream of them.\n *\n * Only the scheduled path takes the lease. A person starting the scenario by hand, and the boot-time load, keep\n * the engine's own in-process rule; a scheduled run that finds such an instance loaded here says so.\n */\n\n/** Scheduled runs of one scenario are not closer together than this. */\nexport const SCHEDULE_FLOOR_MS = 5_000\n\nexport const scheduleLeaseName = (domainId: string, scenarioId: string) => `scenario-schedule:${domainId}:${scenarioId}`\n\nexport type ScheduledRunAnswer =\n | { ran: true }\n | { ran: false; why: 'scenario-schedule-held' | 'scenario-schedule-lease-error' | typeof INTEGRATION_RUNTIME_OFF; detail?: string }\n\nexport interface ScheduledRunLog {\n info: (what: string) => void\n warn: (what: string) => void\n error: (what: string) => void\n}\n\n/** What a leased-run answer means to the scheduler. */\nexport function scheduledRunAnswerOf(answer: LeasedRunAnswer): ScheduledRunAnswer {\n if (answer.started) return { ran: true }\n /* This package has strictNullChecks off, so `started` does not narrow the union by itself. */\n const refused = answer as Extract<LeasedRunAnswer, { started: false }>\n return refused.why === 'held'\n ? { ran: false, why: 'scenario-schedule-held' }\n : { ran: false, why: 'scenario-schedule-lease-error', detail: (refused as { error?: string }).error }\n}\n\n/** Start the scheduled run of one scenario, or say why not. The run itself goes on after this returns. */\nexport async function startScheduledScenario(\n scenario: { id?: string; name?: string },\n config: any,\n context: { domain: { id: string; subdomain: string }; user?: unknown },\n log: ScheduledRunLog,\n start: typeof startLeasedRun = startLeasedRun\n): Promise<ScheduledRunAnswer> {\n const where = `${context.domain.subdomain}:${scenario.name}`\n\n /*\n * Integration does not run in this process. Answered like any refusal — 200 with the reason — so the\n * scheduler does not retry it every minute; the callback address belongs on the process where it runs.\n */\n if (!IntegrationRuntime.enabled) {\n log.warn(`[scenario-schedule] \"${where}\" was called back on a process where integration is off — ${IntegrationRuntime.reason}`)\n return { ran: false, why: INTEGRATION_RUNTIME_OFF, detail: IntegrationRuntime.reason }\n }\n\n const answer = await start({\n name: scheduleLeaseName(context.domain.id, String(scenario.id)),\n floorMs: SCHEDULE_FLOOR_MS,\n work: async () => {\n const result = await ScenarioEngine.runIteration(scenario.name, config, context)\n if (result.alreadyRunning.length) {\n log.warn(\n `[scenario-schedule] \"${where}\" is already loaded on ${result.alreadyRunning.join(', ')} in this process — not started again`\n )\n }\n if (result.failed.length) {\n throw new Error(result.failed.map(one => `${one.subdomain}: ${one.error}`).join(' · '))\n }\n },\n onFailure: f =>\n log.error(\n `[scenario-schedule] \"${where}\" failed (${f.consecutive} in a row) — scheduled runs rest ${Math.round(f.restMs / 1000)}s — ${f.error}`\n ),\n onRecovered: r => log.info(`[scenario-schedule] \"${where}\" ran again after ${r.afterFailures} failed run(s)`),\n onLeaseError: e => log.error(`[scenario-schedule] \"${where}\" lease \"${e.name}\" — ${e.error}`)\n })\n\n const said = scheduledRunAnswerOf(answer)\n if (said.ran === false && said.why === 'scenario-schedule-held') {\n log.warn(`[scenario-schedule] \"${where}\" refused: still running, or resting after its last run`)\n }\n return said\n}\n"]}
|
|
@@ -8,6 +8,7 @@ const auth_base_1 = require("@things-factory/auth-base");
|
|
|
8
8
|
const env_1 = require("@things-factory/env");
|
|
9
9
|
const shell_1 = require("@things-factory/shell");
|
|
10
10
|
const engine_1 = require("../../engine");
|
|
11
|
+
const integration_runtime_1 = require("../../engine/integration-runtime");
|
|
11
12
|
const proxy_connector_1 = require("../../engine/connector/proxy-connector");
|
|
12
13
|
const domain_inheritance_1 = require("../../utils/domain-inheritance");
|
|
13
14
|
const ORMCONFIG = env_1.config.get('ormconfig', {});
|
|
@@ -45,6 +46,8 @@ let Connection = class Connection {
|
|
|
45
46
|
*
|
|
46
47
|
*/
|
|
47
48
|
async connect() {
|
|
49
|
+
/* Every connect — at boot, from the screen, from a scenario — comes through here. */
|
|
50
|
+
integration_runtime_1.IntegrationRuntime.assertEnabled(`connecting "${this.name}"`);
|
|
48
51
|
const { type, edge } = this;
|
|
49
52
|
const connector = edge ? proxy_connector_1.ProxyConnector.instance : engine_1.ConnectionManager.getConnector(type);
|
|
50
53
|
await connector.connect({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../server/service/connection/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, ObjectType, registerEnumType } from 'type-graphql'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { User, Appliance } from '@things-factory/auth-base'\nimport { config } from '@things-factory/env'\nimport { Domain, EnvVar, ScalarObject, encryptJsonTransformer, getRepository } from '@things-factory/shell'\n\nimport { ConnectionManager } from '../../engine'\nimport { ProxyConnector } from '../../engine/connector/proxy-connector'\nimport { lookupDomainEnvVar } from '../../utils/domain-inheritance'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum ConnectionStatus {\n CONNECTED = 'CONNECTED', // Represents an active, established connection.\n DISCONNECTED = 'DISCONNECTED' // Represents a terminated or inactive connection.\n}\n\nregisterEnumType(ConnectionStatus, {\n name: 'ConnectionStatus',\n description: 'Enumeration of possible states for a connection.'\n})\n\n/**\n * 자식 도메인이 부모의 Connection 정의를 inherit 할 때 동작.\n * - ISOLATE: 자식별로 새 instance 생성. cookies/세션·EnvVar 자격증명 격리. HeadlessConnector\n * 같은 stateful connector 의 자연 default.\n * - SHARE: 부모의 단일 instance 를 자식들이 공유. cookies/params 공통. 공용 외부 API\n * (stateless) connector 의 자연 default.\n *\n * null 이면 Connector.inheritanceMode default 적용, 그것도 없으면 ISOLATE.\n */\nexport enum ConnectionInheritanceMode {\n ISOLATE = 'isolate',\n SHARE = 'share'\n}\n\nregisterEnumType(ConnectionInheritanceMode, {\n name: 'ConnectionInheritanceMode',\n description:\n 'Child domain inheritance mode — ISOLATE: per-child instance with isolated session/credentials. SHARE: single parent instance reused by all children.'\n})\n\n@Entity()\n@Index('ix_connection_0', (connection: Connection) => [connection.domain, connection.name], { unique: true })\n@ObjectType({ description: 'Represents a configured connection to an external system or service.' })\nexport class Connection {\n /**\n * Unique identifier for the connection, generated in UUID format.\n */\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the connection.' })\n readonly id: string\n\n /**\n * Many-to-One relationship with the Domain entity.\n */\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain to which this connection belongs.' })\n domain: Domain\n\n /**\n * Stores the ID of the associated Domain.\n */\n @RelationId((connection: Connection) => connection.domain)\n domainId: string\n\n /**\n * The name of the connection.\n */\n @Column()\n @Field({ description: 'The name of the connection.' })\n name: string\n\n /**\n * Optional description for the connection.\n */\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'A detailed description of the connection.' })\n description: string\n\n /**\n * The type of the connection.\n */\n @Column()\n @Field({ nullable: true, description: 'The type of the connection (e.g., tcp, http, mqtt).' })\n type: string\n\n /**\n * Many-to-One relationship with the Appliance entity which delegate the connection. Optional field.\n */\n @ManyToOne(type => Appliance, { nullable: true })\n @Field({ nullable: true, description: 'The edge appliance that delegates this connection, if any.' })\n edge: Appliance\n\n /**\n * Stores the ID of the Appliance who delegate the connection.\n */\n @RelationId((connection: Connection) => connection.edge)\n edgeId: string\n\n /**\n * The endpoint for the connection.\n */\n @Column()\n @Field({ nullable: true, description: 'The endpoint URL or address for the connection.' })\n endpoint: string\n\n /**\n * Whether to automatically connect when the application starts\n */\n @Column({ nullable: true })\n @Field({\n nullable: true,\n description: 'Whether to automatically connect when the application starts'\n })\n active: boolean\n\n /**\n * Indicates whether the connection should be created on-demand when needed.\n */\n @Column({ default: false })\n @Field({\n nullable: true,\n description: 'When true, connection is created on-demand when needed and cleaned up after use.'\n })\n onDemand: boolean\n\n /**\n * 자식 도메인이 부모 도메인의 Connection 정의를 inherit 할 때의 인스턴스 정책.\n *\n * - `ISOLATE`: 자식 도메인마다 새 인스턴스 생성. cookies/세션 상태와 useDomainAttribute\n * 기반 자격증명이 자식 도메인 단위로 격리됨. 자식별 다른 자격증명·세션이 필요한 stateful\n * connector (예: HeadlessConnector — Allbaro / KISCON 등) 의 자연 default.\n *\n * - `SHARE`: 자식 도메인이 부모의 단일 인스턴스를 그대로 공유. cookies/세션/params 모두\n * 부모 인스턴스의 것이 자식에도 그대로 적용. useDomainAttribute 기반 자식 EnvVar\n * override 는 무효화됨. 공용 자원 (단일 브로커·서버) 또는 stateless connector\n * (HTTP, GraphQL) 의 자연 default.\n *\n * - `null`: Connector 가 선언한 default 적용. Connector 도 default 가 없으면 ISOLATE\n * 폴백 (격리·안전 우선).\n *\n * 우선순위: Connection.inheritanceMode → Connector.inheritanceMode → ISOLATE.\n *\n * **호환성**: 본 컬럼은 platform v10 시점에 도입. 그 이전에 만들어진 모든 Connection\n * 레코드는 NULL 값으로 들어가 위 폴백 체인에 따라 ISOLATE 가 적용되어 기존 거동\n * (자식별 격리 인스턴스 생성) 이 그대로 유지됨. SHARE 는 명시적으로 켜야만 발동되는\n * opt-in.\n *\n * **데이터 격리 주의**: SHARE 를 켤 경우 자식 도메인들은 같은 인스턴스의 cookies/세션을\n * 공유함. 자식 도메인별로 다른 외부 자격증명을 사용하려는 경우 SHARE 는 부적절.\n */\n @Column({ type: 'varchar', nullable: true })\n @Field(type => ConnectionInheritanceMode, {\n nullable: true,\n description:\n 'Inheritance mode for child domains. ISOLATE = per-child instance with isolated session. SHARE = shared parent instance. null = use Connector default (falls back to ISOLATE).'\n })\n inheritanceMode?: ConnectionInheritanceMode\n\n /**\n * The status of the connection, using the ConnectionStatus type.\n */\n @Field({ nullable: true, description: 'The current status of the connection (e.g., CONNECTED, DISCONNECTED).' })\n state: ConnectionStatus\n\n /**\n * Additional parameters for the connection, stored as a JSON string.\n *\n * [Caution]\n * 이 컬럼타입은 postgres 데이터베이스에서는 varchar 타입을 유지한다.\n * 이는 데이터베이스 타입을 변경하면 기존 데이터가 손실될 수 있기 때문이다.\n */\n // 비밀값은 이 칸에 두지 않는다 (ADR-0112). spec 이 `type: 'secret'` 인 칸은 쓰기에서 거절되고\n // EnvVar `Connection::<name>::<param>`(늘 암호문) 에 둔다. 읽기에서도 빠진다 — connection-secrets.ts.\n // 그래서 이 칸 전체를 암호화하지 않는다.\n @Column({\n type: DATABASE_TYPE == 'postgres' ? 'varchar' : 'simple-json',\n nullable: true,\n transformer:\n DATABASE_TYPE == 'postgres'\n ? {\n to: (value: any) => JSON.stringify(value),\n from: (value: string) => {\n try {\n return JSON.parse(value)\n } catch (error) {\n return null\n }\n }\n }\n : undefined\n })\n @Field(type => ScalarObject, { nullable: true, description: 'A key-value map of parameters for the connection.' })\n params: { [key: string]: any }\n\n /**\n * The date and time when the connection was created.\n */\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the connection was created.' })\n createdAt: Date\n\n /**\n * The date and time when the connection was last updated.\n */\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the connection was last updated.' })\n updatedAt: Date\n\n /**\n * Many-to-One relationship with the User entity who created the connection. Optional field.\n */\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who created the connection.' })\n creator: User\n\n /**\n * Stores the ID of the User who created the connection.\n */\n @RelationId((connection: Connection) => connection.creator)\n creatorId: string\n\n /**\n * Many-to-One relationship with the User entity who last updated the connection.\n * Optional field.\n */\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who last updated the connection.' })\n updater: User\n\n /**\n * Stores the ID of the User who last updated the connection.\n *\n */\n @RelationId((connection: Connection) => connection.updater)\n updaterId: string\n\n /**\n * Runtime marker — DB 컬럼이 아니라 메모리 전용 플래그.\n *\n * 자식 도메인이 자기 Connection 레코드 없이 부모의 정의를 inherit 할 때,\n * `ConnectionManager.getConnectionEntityByName` 의 parent fallback 분기가\n * 부모 entity 의 clone 에 이 marker (= 부모의 domainId) 를 부착한다.\n *\n * `getConnectionInstanceByName` 가 이 marker 가 truthy 인 entity 를 보면\n * onDemand 플래그와 동일하게 자식 도메인용 instance 를 자동 생성한다 — 운영자가\n * 부모 record 의 onDemand 를 별도로 켤 필요 없이 inheritance 가 자연스럽게 동작.\n *\n * inheritanceMode = ISOLATE 인 경우에만 부착.\n */\n __inheritedFrom?: string\n\n /**\n * Runtime marker — DB 컬럼이 아니라 메모리 전용 플래그. SHARE 모드의 짝.\n *\n * 자식 도메인이 부모의 Connection 정의를 inherit 할 때, inheritanceMode = SHARE 면\n * 자식이 부모의 단일 instance 를 그대로 사용. parent fallback 분기가 부모 entity 를\n * 그대로 반환하면서 이 marker (= 부모의 domainId) 를 부착.\n *\n * `getConnectionInstanceByName` 가 이 marker 를 보면 자식 instance 자동 생성 없이\n * `connections[__sharedFrom][name]` 를 그대로 반환 — 부모·자식이 같은 instance.\n *\n * inheritanceMode = SHARE 인 경우에만 부착. `__inheritedFrom` 과 mutually exclusive.\n */\n __sharedFrom?: string\n\n /**\n * Asynchronous method to establish the connection.\n *\n */\n async connect() {\n const { type, edge } = this\n const connector = edge ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n\n await connector.connect({\n ...this,\n params: await this.getResolvedParameters() // 🔐 해결된 파라미터 사용\n })\n }\n\n /**\n * @brief Asynchronous method to disconnect the connection.\n *\n */\n async disconnect() {\n try {\n const { type, edge } = this\n const connector = edge ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n await connector.disconnect(this)\n } finally {\n }\n }\n\n /**\n * Gets a parameter value with EnvVar override support.\n *\n * **Priority (`useDomainAttribute: true` 인 필드)**:\n * 1. **EnvVar** — ancestor closest-wins (자식 도메인이 가장 가까움)\n * 2. **params** 명시값 (parent Connection 의 default 역할)\n * 3. defaultValue\n *\n * **Priority (`useDomainAttribute: false` 또는 미지정 필드)**:\n * 1. **params** 명시값\n * 2. defaultValue\n *\n * 의미론: `useDomainAttribute: true` 는 \"이 필드는 도메인별 override 가능\" 선언.\n * 따라서 EnvVar 가 본 소스, params 는 모든 자손에 동일 적용되는 default.\n * 자식 도메인 EnvVar override 가 자연스럽게 동작 (시공사 → 프로젝트 자격증명 차별화 등).\n *\n * @param key - The parameter key\n * @param defaultValue - Optional default value\n * @returns The parameter value or default value\n */\n async getParameter(key: string, defaultValue?: any): Promise<any> {\n const connector = ConnectionManager.getConnector(this.type)\n const paramSpec = connector?.parameterSpec?.find(spec => spec.name === key)\n\n // 1. useDomainAttribute 필드는 EnvVar 우선 조회 (ancestor closest-wins)\n if (paramSpec?.useDomainAttribute) {\n const envVarKey = `Connection::${this.name}::${key}`\n const domain = await getRepository(Domain).findOne({ where: { id: this.domainId } })\n if (domain) {\n const value = await lookupDomainEnvVar(domain, envVarKey)\n if (value !== undefined) {\n // params 에 명시값이 있는데 EnvVar 가 override 한 경우 진단 로그\n const paramValue = this.params?.[key]\n if (paramValue !== null && paramValue !== undefined && paramValue !== '') {\n ConnectionManager.logger.info(\n `[domain-attr] Connection '${this.name}'.${key}: params default overridden by EnvVar (domain='${domain.subdomain || domain.id}')`\n )\n }\n return value\n }\n }\n }\n\n // 2. params 명시값 (default 역할)\n if (this.params?.[key] !== null && this.params?.[key] !== undefined && this.params?.[key] !== '') {\n return this.params[key]\n }\n\n // 3. defaultValue\n return defaultValue\n }\n\n /**\n * Gets all parameters with resolved values (params + Domain attributes).\n *\n * @returns Object with all resolved parameter values\n */\n async getResolvedParameters(): Promise<{ [key: string]: any }> {\n const resolved = {}\n\n const connector = ConnectionManager.getConnector(this.type)\n const paramSpecs = connector?.parameterSpec || []\n\n for (const spec of paramSpecs) {\n const key = spec.name\n resolved[key] = await this.getParameter(key)\n }\n\n return resolved\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../server/service/connection/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, ObjectType, registerEnumType } from 'type-graphql'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { User, Appliance } from '@things-factory/auth-base'\nimport { config } from '@things-factory/env'\nimport { Domain, EnvVar, ScalarObject, encryptJsonTransformer, getRepository } from '@things-factory/shell'\n\nimport { ConnectionManager } from '../../engine'\nimport { IntegrationRuntime } from '../../engine/integration-runtime'\nimport { ProxyConnector } from '../../engine/connector/proxy-connector'\nimport { lookupDomainEnvVar } from '../../utils/domain-inheritance'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum ConnectionStatus {\n CONNECTED = 'CONNECTED', // Represents an active, established connection.\n DISCONNECTED = 'DISCONNECTED' // Represents a terminated or inactive connection.\n}\n\nregisterEnumType(ConnectionStatus, {\n name: 'ConnectionStatus',\n description: 'Enumeration of possible states for a connection.'\n})\n\n/**\n * 자식 도메인이 부모의 Connection 정의를 inherit 할 때 동작.\n * - ISOLATE: 자식별로 새 instance 생성. cookies/세션·EnvVar 자격증명 격리. HeadlessConnector\n * 같은 stateful connector 의 자연 default.\n * - SHARE: 부모의 단일 instance 를 자식들이 공유. cookies/params 공통. 공용 외부 API\n * (stateless) connector 의 자연 default.\n *\n * null 이면 Connector.inheritanceMode default 적용, 그것도 없으면 ISOLATE.\n */\nexport enum ConnectionInheritanceMode {\n ISOLATE = 'isolate',\n SHARE = 'share'\n}\n\nregisterEnumType(ConnectionInheritanceMode, {\n name: 'ConnectionInheritanceMode',\n description:\n 'Child domain inheritance mode — ISOLATE: per-child instance with isolated session/credentials. SHARE: single parent instance reused by all children.'\n})\n\n@Entity()\n@Index('ix_connection_0', (connection: Connection) => [connection.domain, connection.name], { unique: true })\n@ObjectType({ description: 'Represents a configured connection to an external system or service.' })\nexport class Connection {\n /**\n * Unique identifier for the connection, generated in UUID format.\n */\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the connection.' })\n readonly id: string\n\n /**\n * Many-to-One relationship with the Domain entity.\n */\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain to which this connection belongs.' })\n domain: Domain\n\n /**\n * Stores the ID of the associated Domain.\n */\n @RelationId((connection: Connection) => connection.domain)\n domainId: string\n\n /**\n * The name of the connection.\n */\n @Column()\n @Field({ description: 'The name of the connection.' })\n name: string\n\n /**\n * Optional description for the connection.\n */\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'A detailed description of the connection.' })\n description: string\n\n /**\n * The type of the connection.\n */\n @Column()\n @Field({ nullable: true, description: 'The type of the connection (e.g., tcp, http, mqtt).' })\n type: string\n\n /**\n * Many-to-One relationship with the Appliance entity which delegate the connection. Optional field.\n */\n @ManyToOne(type => Appliance, { nullable: true })\n @Field({ nullable: true, description: 'The edge appliance that delegates this connection, if any.' })\n edge: Appliance\n\n /**\n * Stores the ID of the Appliance who delegate the connection.\n */\n @RelationId((connection: Connection) => connection.edge)\n edgeId: string\n\n /**\n * The endpoint for the connection.\n */\n @Column()\n @Field({ nullable: true, description: 'The endpoint URL or address for the connection.' })\n endpoint: string\n\n /**\n * Whether to automatically connect when the application starts\n */\n @Column({ nullable: true })\n @Field({\n nullable: true,\n description: 'Whether to automatically connect when the application starts'\n })\n active: boolean\n\n /**\n * Indicates whether the connection should be created on-demand when needed.\n */\n @Column({ default: false })\n @Field({\n nullable: true,\n description: 'When true, connection is created on-demand when needed and cleaned up after use.'\n })\n onDemand: boolean\n\n /**\n * 자식 도메인이 부모 도메인의 Connection 정의를 inherit 할 때의 인스턴스 정책.\n *\n * - `ISOLATE`: 자식 도메인마다 새 인스턴스 생성. cookies/세션 상태와 useDomainAttribute\n * 기반 자격증명이 자식 도메인 단위로 격리됨. 자식별 다른 자격증명·세션이 필요한 stateful\n * connector (예: HeadlessConnector — Allbaro / KISCON 등) 의 자연 default.\n *\n * - `SHARE`: 자식 도메인이 부모의 단일 인스턴스를 그대로 공유. cookies/세션/params 모두\n * 부모 인스턴스의 것이 자식에도 그대로 적용. useDomainAttribute 기반 자식 EnvVar\n * override 는 무효화됨. 공용 자원 (단일 브로커·서버) 또는 stateless connector\n * (HTTP, GraphQL) 의 자연 default.\n *\n * - `null`: Connector 가 선언한 default 적용. Connector 도 default 가 없으면 ISOLATE\n * 폴백 (격리·안전 우선).\n *\n * 우선순위: Connection.inheritanceMode → Connector.inheritanceMode → ISOLATE.\n *\n * **호환성**: 본 컬럼은 platform v10 시점에 도입. 그 이전에 만들어진 모든 Connection\n * 레코드는 NULL 값으로 들어가 위 폴백 체인에 따라 ISOLATE 가 적용되어 기존 거동\n * (자식별 격리 인스턴스 생성) 이 그대로 유지됨. SHARE 는 명시적으로 켜야만 발동되는\n * opt-in.\n *\n * **데이터 격리 주의**: SHARE 를 켤 경우 자식 도메인들은 같은 인스턴스의 cookies/세션을\n * 공유함. 자식 도메인별로 다른 외부 자격증명을 사용하려는 경우 SHARE 는 부적절.\n */\n @Column({ type: 'varchar', nullable: true })\n @Field(type => ConnectionInheritanceMode, {\n nullable: true,\n description:\n 'Inheritance mode for child domains. ISOLATE = per-child instance with isolated session. SHARE = shared parent instance. null = use Connector default (falls back to ISOLATE).'\n })\n inheritanceMode?: ConnectionInheritanceMode\n\n /**\n * The status of the connection, using the ConnectionStatus type.\n */\n @Field({ nullable: true, description: 'The current status of the connection (e.g., CONNECTED, DISCONNECTED).' })\n state: ConnectionStatus\n\n /**\n * Additional parameters for the connection, stored as a JSON string.\n *\n * [Caution]\n * 이 컬럼타입은 postgres 데이터베이스에서는 varchar 타입을 유지한다.\n * 이는 데이터베이스 타입을 변경하면 기존 데이터가 손실될 수 있기 때문이다.\n */\n // 비밀값은 이 칸에 두지 않는다 (ADR-0112). spec 이 `type: 'secret'` 인 칸은 쓰기에서 거절되고\n // EnvVar `Connection::<name>::<param>`(늘 암호문) 에 둔다. 읽기에서도 빠진다 — connection-secrets.ts.\n // 그래서 이 칸 전체를 암호화하지 않는다.\n @Column({\n type: DATABASE_TYPE == 'postgres' ? 'varchar' : 'simple-json',\n nullable: true,\n transformer:\n DATABASE_TYPE == 'postgres'\n ? {\n to: (value: any) => JSON.stringify(value),\n from: (value: string) => {\n try {\n return JSON.parse(value)\n } catch (error) {\n return null\n }\n }\n }\n : undefined\n })\n @Field(type => ScalarObject, { nullable: true, description: 'A key-value map of parameters for the connection.' })\n params: { [key: string]: any }\n\n /**\n * The date and time when the connection was created.\n */\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the connection was created.' })\n createdAt: Date\n\n /**\n * The date and time when the connection was last updated.\n */\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the connection was last updated.' })\n updatedAt: Date\n\n /**\n * Many-to-One relationship with the User entity who created the connection. Optional field.\n */\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who created the connection.' })\n creator: User\n\n /**\n * Stores the ID of the User who created the connection.\n */\n @RelationId((connection: Connection) => connection.creator)\n creatorId: string\n\n /**\n * Many-to-One relationship with the User entity who last updated the connection.\n * Optional field.\n */\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who last updated the connection.' })\n updater: User\n\n /**\n * Stores the ID of the User who last updated the connection.\n *\n */\n @RelationId((connection: Connection) => connection.updater)\n updaterId: string\n\n /**\n * Runtime marker — DB 컬럼이 아니라 메모리 전용 플래그.\n *\n * 자식 도메인이 자기 Connection 레코드 없이 부모의 정의를 inherit 할 때,\n * `ConnectionManager.getConnectionEntityByName` 의 parent fallback 분기가\n * 부모 entity 의 clone 에 이 marker (= 부모의 domainId) 를 부착한다.\n *\n * `getConnectionInstanceByName` 가 이 marker 가 truthy 인 entity 를 보면\n * onDemand 플래그와 동일하게 자식 도메인용 instance 를 자동 생성한다 — 운영자가\n * 부모 record 의 onDemand 를 별도로 켤 필요 없이 inheritance 가 자연스럽게 동작.\n *\n * inheritanceMode = ISOLATE 인 경우에만 부착.\n */\n __inheritedFrom?: string\n\n /**\n * Runtime marker — DB 컬럼이 아니라 메모리 전용 플래그. SHARE 모드의 짝.\n *\n * 자식 도메인이 부모의 Connection 정의를 inherit 할 때, inheritanceMode = SHARE 면\n * 자식이 부모의 단일 instance 를 그대로 사용. parent fallback 분기가 부모 entity 를\n * 그대로 반환하면서 이 marker (= 부모의 domainId) 를 부착.\n *\n * `getConnectionInstanceByName` 가 이 marker 를 보면 자식 instance 자동 생성 없이\n * `connections[__sharedFrom][name]` 를 그대로 반환 — 부모·자식이 같은 instance.\n *\n * inheritanceMode = SHARE 인 경우에만 부착. `__inheritedFrom` 과 mutually exclusive.\n */\n __sharedFrom?: string\n\n /**\n * Asynchronous method to establish the connection.\n *\n */\n async connect() {\n /* Every connect — at boot, from the screen, from a scenario — comes through here. */\n IntegrationRuntime.assertEnabled(`connecting \"${this.name}\"`)\n const { type, edge } = this\n const connector = edge ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n\n await connector.connect({\n ...this,\n params: await this.getResolvedParameters() // 🔐 해결된 파라미터 사용\n })\n }\n\n /**\n * @brief Asynchronous method to disconnect the connection.\n *\n */\n async disconnect() {\n try {\n const { type, edge } = this\n const connector = edge ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n await connector.disconnect(this)\n } finally {\n }\n }\n\n /**\n * Gets a parameter value with EnvVar override support.\n *\n * **Priority (`useDomainAttribute: true` 인 필드)**:\n * 1. **EnvVar** — ancestor closest-wins (자식 도메인이 가장 가까움)\n * 2. **params** 명시값 (parent Connection 의 default 역할)\n * 3. defaultValue\n *\n * **Priority (`useDomainAttribute: false` 또는 미지정 필드)**:\n * 1. **params** 명시값\n * 2. defaultValue\n *\n * 의미론: `useDomainAttribute: true` 는 \"이 필드는 도메인별 override 가능\" 선언.\n * 따라서 EnvVar 가 본 소스, params 는 모든 자손에 동일 적용되는 default.\n * 자식 도메인 EnvVar override 가 자연스럽게 동작 (시공사 → 프로젝트 자격증명 차별화 등).\n *\n * @param key - The parameter key\n * @param defaultValue - Optional default value\n * @returns The parameter value or default value\n */\n async getParameter(key: string, defaultValue?: any): Promise<any> {\n const connector = ConnectionManager.getConnector(this.type)\n const paramSpec = connector?.parameterSpec?.find(spec => spec.name === key)\n\n // 1. useDomainAttribute 필드는 EnvVar 우선 조회 (ancestor closest-wins)\n if (paramSpec?.useDomainAttribute) {\n const envVarKey = `Connection::${this.name}::${key}`\n const domain = await getRepository(Domain).findOne({ where: { id: this.domainId } })\n if (domain) {\n const value = await lookupDomainEnvVar(domain, envVarKey)\n if (value !== undefined) {\n // params 에 명시값이 있는데 EnvVar 가 override 한 경우 진단 로그\n const paramValue = this.params?.[key]\n if (paramValue !== null && paramValue !== undefined && paramValue !== '') {\n ConnectionManager.logger.info(\n `[domain-attr] Connection '${this.name}'.${key}: params default overridden by EnvVar (domain='${domain.subdomain || domain.id}')`\n )\n }\n return value\n }\n }\n }\n\n // 2. params 명시값 (default 역할)\n if (this.params?.[key] !== null && this.params?.[key] !== undefined && this.params?.[key] !== '') {\n return this.params[key]\n }\n\n // 3. defaultValue\n return defaultValue\n }\n\n /**\n * Gets all parameters with resolved values (params + Domain attributes).\n *\n * @returns Object with all resolved parameter values\n */\n async getResolvedParameters(): Promise<{ [key: string]: any }> {\n const resolved = {}\n\n const connector = ConnectionManager.getConnector(this.type)\n const paramSpecs = connector?.parameterSpec || []\n\n for (const spec of paramSpecs) {\n const key = spec.name\n resolved[key] = await this.getParameter(key)\n }\n\n return resolved\n }\n}\n"]}
|