@things-factory/integration-base 10.0.0-beta.90 → 10.0.0-beta.95
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/connection-manager.d.ts +19 -0
- package/dist-server/engine/connection-manager.js +148 -16
- package/dist-server/engine/connection-manager.js.map +1 -1
- package/dist-server/engine/connector/headless-connector.d.ts +12 -0
- package/dist-server/engine/connector/headless-connector.js +74 -31
- package/dist-server/engine/connector/headless-connector.js.map +1 -1
- package/dist-server/engine/evaluate-template.d.ts +22 -3
- package/dist-server/engine/evaluate-template.js +43 -4
- package/dist-server/engine/evaluate-template.js.map +1 -1
- package/dist-server/engine/task/script.js +25 -17
- package/dist-server/engine/task/script.js.map +1 -1
- package/dist-server/engine/types.d.ts +32 -0
- package/dist-server/engine/types.js.map +1 -1
- package/dist-server/service/connection/connection-mutation.d.ts +3 -0
- package/dist-server/service/connection/connection-type.d.ts +4 -1
- package/dist-server/service/connection/connection-type.js +21 -0
- package/dist-server/service/connection/connection-type.js.map +1 -1
- package/dist-server/service/connection/connection.d.ts +80 -2
- package/dist-server/service/connection/connection.js +59 -15
- package/dist-server/service/connection/connection.js.map +1 -1
- package/dist-server/service/domain-attribute/domain-attribute-query.d.ts +17 -0
- package/dist-server/service/domain-attribute/domain-attribute-query.js +76 -0
- package/dist-server/service/domain-attribute/domain-attribute-query.js.map +1 -0
- package/dist-server/service/domain-attribute/domain-attribute-type.d.ts +15 -0
- package/dist-server/service/domain-attribute/domain-attribute-type.js +46 -0
- package/dist-server/service/domain-attribute/domain-attribute-type.js.map +1 -0
- package/dist-server/service/domain-attribute/index.d.ts +3 -0
- package/dist-server/service/domain-attribute/index.js +8 -0
- package/dist-server/service/domain-attribute/index.js.map +1 -0
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/service/index.js +3 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/scenario/scenario-mutation.js +10 -0
- package/dist-server/service/scenario/scenario-mutation.js.map +1 -1
- package/dist-server/service/scenario/scenario-query.d.ts +3 -3
- package/dist-server/service/scenario/scenario-query.js +9 -9
- package/dist-server/service/scenario/scenario-query.js.map +1 -1
- package/dist-server/service/scenario-instance/scenario-instance-type.js +79 -0
- package/dist-server/service/scenario-instance/scenario-instance-type.js.map +1 -1
- package/dist-server/service/step/step-mutation.js +15 -0
- package/dist-server/service/step/step-mutation.js.map +1 -1
- package/dist-server/service/step/step-query.js +11 -2
- package/dist-server/service/step/step-query.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/domain-inheritance.d.ts +27 -0
- package/dist-server/utils/domain-inheritance.js +67 -0
- package/dist-server/utils/domain-inheritance.js.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-type.js","sourceRoot":"","sources":["../../../server/service/connection/connection-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AAEpE,yDAAqD;AACrD,iDAAuE;AAEvE,6CAAyC;AACzC,
|
|
1
|
+
{"version":3,"file":"connection-type.js","sourceRoot":"","sources":["../../../server/service/connection/connection-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AAEpE,yDAAqD;AACrD,iDAAuE;AAEvE,6CAAyC;AACzC,6CAA0E;AAYnE,IAAM,eAAe,GAArB,MAAM,eAAe;CAoC3B,CAAA;AApCY,0CAAe;AAE1B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;sCAC/D,cAAM;+CAAA;AAGf;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;;2CACxE;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;6CACzD;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;oDACzD;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;6CACzD;AAMb;IAJC,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,kFAAkF;KAChG,CAAC;;iDACgB;AAMlB;IAJC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,sCAAyB,EAAE;QACxC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,yJAAyJ;KACvK,CAAC;;wDACyC;AAG3C;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,qBAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;sCACzG,qBAAS;6CAAA;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;;8CACxD;AAGxB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC;sCAC1E,IAAI;kDAAA;0BAnCL,eAAe;IAD3B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;GAC3E,eAAe,CAoC3B;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAoCzB,CAAA;AApCY,sCAAa;AAExB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;2CAC/C;AAGZ;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;;kDACrE;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;2CACrF;AAMb;IAJC,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,kFAAkF;KAChG,CAAC;;+CACe;AAMjB;IAJC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,sCAAyB,EAAE;QACxC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,iIAAiI;KAC/I,CAAC;;sDACyC;AAG3C;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;sCAC9G,qBAAS;2CAAA;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;;+CAC7E;AAMjB;IAJC,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,8DAA8D;KAC5E,CAAC;;6CACc;AAGhB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;6CACvF;wBAnCpB,aAAa;IADzB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;GACtD,aAAa,CAoCzB;AAGM,IAAM,eAAe,GAArB,MAAM,eAAe;CA6C3B,CAAA;AA7CY,0CAAe;AAE1B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;;2CAC/F;AAGV;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;6CAC9D;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;;oDAC9D;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;6CAC9D;AAMb;IAJC,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,kFAAkF;KAChG,CAAC;;iDACe;AAMjB;IAJC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,sCAAyB,EAAE;QACxC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,iIAAiI;KAC/I,CAAC;;wDACyC;AAG3C;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;sCACjG,qBAAS;6CAAA;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;iDAC9D;AAMjB;IAJC,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,8DAA8D;KAC5E,CAAC;;+CACc;AAGhB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;;+CACxE;AAM/B;IAJC,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,iFAAiF;KAC/F,CAAC;;+CACa;0BA5CJ,eAAe;IAD3B,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;GACvE,eAAe,CA6C3B;AAGM,IAAM,cAAc,GAApB,MAAM,cAAc;CAM1B,CAAA;AANY,wCAAc;AAEzB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,uBAAU,CAAC,EAAE,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;;6CAC3D;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;6CAC3D;yBALF,cAAc;IAD1B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;GACnD,cAAc,CAM1B","sourcesContent":["import { Field, ID, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Appliance } from '@things-factory/auth-base'\nimport { Domain, ObjectRef, ScalarObject } from '@things-factory/shell'\n\nimport { Connection } from './connection'\nimport { ConnectionInheritanceMode, ConnectionStatus } from './connection'\n\n/**\n * Connection의 params의 원 타입과 사용 시에 타입 불일치로 인해 임시적으로 생성한 타입으로\n * 추후, 타입 일치를 통해서 제거할 예정임.\n *\n */\nexport interface InputConnection extends Connection {\n params: { [key: string]: any }\n}\n\n@ObjectType({ description: 'Represents the state of a connection at a point in time.' })\nexport class ConnectionState {\n @Field(type => Domain, { description: 'The domain of the connection.' })\n domain?: Domain\n\n @Field({ nullable: true, description: 'The unique identifier of the connection.' })\n id?: string\n\n @Field({ nullable: true, description: 'The name of the connection.' })\n name?: string\n\n @Field({ nullable: true, description: 'The description of the connection.' })\n description?: string\n\n @Field({ nullable: true, description: 'The type of the connection.' })\n type?: string\n\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 @Field(type => ConnectionInheritanceMode, {\n nullable: true,\n description: 'Inheritance mode for child domains. ISOLATE = per-child instance. SHARE = shared parent instance. null = use connector default (falls back to ISOLATE).'\n })\n inheritanceMode?: ConnectionInheritanceMode\n\n @Field(type => Appliance, { nullable: true, description: 'The edge appliance associated with the connection.' })\n edge?: Appliance\n\n @Field({ nullable: true, description: 'The current status of the connection.' })\n state?: ConnectionStatus\n\n @Field({ nullable: true, description: 'The timestamp when this state was recorded.' })\n timestamp?: Date\n}\n\n@InputType({ description: 'Input for creating a new connection.' })\nexport class NewConnection {\n @Field({ description: 'The name for the new connection.' })\n name: string\n\n @Field({ nullable: true, description: 'A detailed description for the new connection.' })\n description?: string\n\n @Field({ nullable: true, description: 'The type of the new connection (e.g., tcp, http, mqtt).' })\n type?: string\n\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 @Field(type => ConnectionInheritanceMode, {\n nullable: true,\n description: 'Inheritance mode for child domains. ISOLATE = per-child instance. SHARE = shared parent instance. null = use connector default.'\n })\n inheritanceMode?: ConnectionInheritanceMode\n\n @Field(type => ObjectRef, { nullable: true, description: 'Reference to the edge appliance for the new connection.' })\n edge?: Appliance\n\n @Field({ nullable: true, description: 'The endpoint URL or address for the new connection.' })\n endpoint?: string\n\n @Field({\n nullable: true,\n description: 'Whether to automatically connect when the application starts'\n })\n active?: boolean\n\n @Field(type => ScalarObject, { nullable: true, description: 'A key-value map of parameters for the new connection.' })\n params?: { [key: string]: any }\n}\n\n@InputType({ description: 'Input for updating (patching) an existing connection.' })\nexport class ConnectionPatch {\n @Field(type => ID, { nullable: true, description: 'The unique identifier of the connection to update.' })\n id: string\n\n @Field({ nullable: true, description: 'The new name for the connection.' })\n name?: string\n\n @Field({ nullable: true, description: 'The new description for the connection.' })\n description?: string\n\n @Field({ nullable: true, description: 'The new type for the connection.' })\n type?: string\n\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 @Field(type => ConnectionInheritanceMode, {\n nullable: true,\n description: 'Inheritance mode for child domains. ISOLATE = per-child instance. SHARE = shared parent instance. null = use connector default.'\n })\n inheritanceMode?: ConnectionInheritanceMode\n\n @Field(type => ObjectRef, { nullable: true, description: 'The new edge appliance for the connection.' })\n edge?: Appliance\n\n @Field({ nullable: true, description: 'The new endpoint for the connection.' })\n endpoint?: string\n\n @Field({\n nullable: true,\n description: 'Whether to automatically connect when the application starts'\n })\n active?: boolean\n\n @Field(type => ScalarObject, { nullable: true, description: 'The new parameters for the connection.' })\n params?: { [key: string]: any }\n\n @Field({\n nullable: true,\n description: 'A flag indicating whether the connection is being created, updated, or deleted.'\n })\n cuFlag?: string\n}\n\n@ObjectType({ description: 'A paginated list of connections.' })\nexport class ConnectionList {\n @Field(type => [Connection], { description: 'The list of connection items.' })\n items: Connection[]\n\n @Field(type => Int, { description: 'The total number of connections.' })\n total: number\n}\n"]}
|
|
@@ -4,6 +4,19 @@ export declare enum ConnectionStatus {
|
|
|
4
4
|
CONNECTED = "CONNECTED",// Represents an active, established connection.
|
|
5
5
|
DISCONNECTED = "DISCONNECTED"
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* 자식 도메인이 부모의 Connection 정의를 inherit 할 때 동작.
|
|
9
|
+
* - ISOLATE: 자식별로 새 instance 생성. cookies/세션·EnvVar 자격증명 격리. HeadlessConnector
|
|
10
|
+
* 같은 stateful connector 의 자연 default.
|
|
11
|
+
* - SHARE: 부모의 단일 instance 를 자식들이 공유. cookies/params 공통. 공용 외부 API
|
|
12
|
+
* (stateless) connector 의 자연 default.
|
|
13
|
+
*
|
|
14
|
+
* null 이면 Connector.inheritanceMode default 적용, 그것도 없으면 ISOLATE.
|
|
15
|
+
*/
|
|
16
|
+
export declare enum ConnectionInheritanceMode {
|
|
17
|
+
ISOLATE = "isolate",
|
|
18
|
+
SHARE = "share"
|
|
19
|
+
}
|
|
7
20
|
export declare class Connection {
|
|
8
21
|
/**
|
|
9
22
|
* Unique identifier for the connection, generated in UUID format.
|
|
@@ -49,6 +62,32 @@ export declare class Connection {
|
|
|
49
62
|
* Indicates whether the connection should be created on-demand when needed.
|
|
50
63
|
*/
|
|
51
64
|
onDemand: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* 자식 도메인이 부모 도메인의 Connection 정의를 inherit 할 때의 인스턴스 정책.
|
|
67
|
+
*
|
|
68
|
+
* - `ISOLATE`: 자식 도메인마다 새 인스턴스 생성. cookies/세션 상태와 useDomainAttribute
|
|
69
|
+
* 기반 자격증명이 자식 도메인 단위로 격리됨. 자식별 다른 자격증명·세션이 필요한 stateful
|
|
70
|
+
* connector (예: HeadlessConnector — Allbaro / KISCON 등) 의 자연 default.
|
|
71
|
+
*
|
|
72
|
+
* - `SHARE`: 자식 도메인이 부모의 단일 인스턴스를 그대로 공유. cookies/세션/params 모두
|
|
73
|
+
* 부모 인스턴스의 것이 자식에도 그대로 적용. useDomainAttribute 기반 자식 EnvVar
|
|
74
|
+
* override 는 무효화됨. 공용 자원 (단일 브로커·서버) 또는 stateless connector
|
|
75
|
+
* (HTTP, GraphQL) 의 자연 default.
|
|
76
|
+
*
|
|
77
|
+
* - `null`: Connector 가 선언한 default 적용. Connector 도 default 가 없으면 ISOLATE
|
|
78
|
+
* 폴백 (격리·안전 우선).
|
|
79
|
+
*
|
|
80
|
+
* 우선순위: Connection.inheritanceMode → Connector.inheritanceMode → ISOLATE.
|
|
81
|
+
*
|
|
82
|
+
* **호환성**: 본 컬럼은 platform v10 시점에 도입. 그 이전에 만들어진 모든 Connection
|
|
83
|
+
* 레코드는 NULL 값으로 들어가 위 폴백 체인에 따라 ISOLATE 가 적용되어 기존 거동
|
|
84
|
+
* (자식별 격리 인스턴스 생성) 이 그대로 유지됨. SHARE 는 명시적으로 켜야만 발동되는
|
|
85
|
+
* opt-in.
|
|
86
|
+
*
|
|
87
|
+
* **데이터 격리 주의**: SHARE 를 켤 경우 자식 도메인들은 같은 인스턴스의 cookies/세션을
|
|
88
|
+
* 공유함. 자식 도메인별로 다른 외부 자격증명을 사용하려는 경우 SHARE 는 부적절.
|
|
89
|
+
*/
|
|
90
|
+
inheritanceMode?: ConnectionInheritanceMode;
|
|
52
91
|
/**
|
|
53
92
|
* The status of the connection, using the ConnectionStatus type.
|
|
54
93
|
*/
|
|
@@ -89,6 +128,33 @@ export declare class Connection {
|
|
|
89
128
|
*
|
|
90
129
|
*/
|
|
91
130
|
updaterId: string;
|
|
131
|
+
/**
|
|
132
|
+
* Runtime marker — DB 컬럼이 아니라 메모리 전용 플래그.
|
|
133
|
+
*
|
|
134
|
+
* 자식 도메인이 자기 Connection 레코드 없이 부모의 정의를 inherit 할 때,
|
|
135
|
+
* `ConnectionManager.getConnectionEntityByName` 의 parent fallback 분기가
|
|
136
|
+
* 부모 entity 의 clone 에 이 marker (= 부모의 domainId) 를 부착한다.
|
|
137
|
+
*
|
|
138
|
+
* `getConnectionInstanceByName` 가 이 marker 가 truthy 인 entity 를 보면
|
|
139
|
+
* onDemand 플래그와 동일하게 자식 도메인용 instance 를 자동 생성한다 — 운영자가
|
|
140
|
+
* 부모 record 의 onDemand 를 별도로 켤 필요 없이 inheritance 가 자연스럽게 동작.
|
|
141
|
+
*
|
|
142
|
+
* inheritanceMode = ISOLATE 인 경우에만 부착.
|
|
143
|
+
*/
|
|
144
|
+
__inheritedFrom?: string;
|
|
145
|
+
/**
|
|
146
|
+
* Runtime marker — DB 컬럼이 아니라 메모리 전용 플래그. SHARE 모드의 짝.
|
|
147
|
+
*
|
|
148
|
+
* 자식 도메인이 부모의 Connection 정의를 inherit 할 때, inheritanceMode = SHARE 면
|
|
149
|
+
* 자식이 부모의 단일 instance 를 그대로 사용. parent fallback 분기가 부모 entity 를
|
|
150
|
+
* 그대로 반환하면서 이 marker (= 부모의 domainId) 를 부착.
|
|
151
|
+
*
|
|
152
|
+
* `getConnectionInstanceByName` 가 이 marker 를 보면 자식 instance 자동 생성 없이
|
|
153
|
+
* `connections[__sharedFrom][name]` 를 그대로 반환 — 부모·자식이 같은 instance.
|
|
154
|
+
*
|
|
155
|
+
* inheritanceMode = SHARE 인 경우에만 부착. `__inheritedFrom` 과 mutually exclusive.
|
|
156
|
+
*/
|
|
157
|
+
__sharedFrom?: string;
|
|
92
158
|
/**
|
|
93
159
|
* Asynchronous method to establish the connection.
|
|
94
160
|
*
|
|
@@ -100,8 +166,20 @@ export declare class Connection {
|
|
|
100
166
|
*/
|
|
101
167
|
disconnect(): Promise<void>;
|
|
102
168
|
/**
|
|
103
|
-
* Gets a parameter value with
|
|
104
|
-
*
|
|
169
|
+
* Gets a parameter value with EnvVar override support.
|
|
170
|
+
*
|
|
171
|
+
* **Priority (`useDomainAttribute: true` 인 필드)**:
|
|
172
|
+
* 1. **EnvVar** — ancestor closest-wins (자식 도메인이 가장 가까움)
|
|
173
|
+
* 2. **params** 명시값 (parent Connection 의 default 역할)
|
|
174
|
+
* 3. defaultValue
|
|
175
|
+
*
|
|
176
|
+
* **Priority (`useDomainAttribute: false` 또는 미지정 필드)**:
|
|
177
|
+
* 1. **params** 명시값
|
|
178
|
+
* 2. defaultValue
|
|
179
|
+
*
|
|
180
|
+
* 의미론: `useDomainAttribute: true` 는 "이 필드는 도메인별 override 가능" 선언.
|
|
181
|
+
* 따라서 EnvVar 가 본 소스, params 는 모든 자손에 동일 적용되는 default.
|
|
182
|
+
* 자식 도메인 EnvVar override 가 자연스럽게 동작 (시공사 → 프로젝트 자격증명 차별화 등).
|
|
105
183
|
*
|
|
106
184
|
* @param key - The parameter key
|
|
107
185
|
* @param defaultValue - Optional default value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Connection = exports.ConnectionStatus = void 0;
|
|
3
|
+
exports.Connection = exports.ConnectionInheritanceMode = exports.ConnectionStatus = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
6
|
const typeorm_1 = require("typeorm");
|
|
@@ -9,6 +9,7 @@ const env_1 = require("@things-factory/env");
|
|
|
9
9
|
const shell_1 = require("@things-factory/shell");
|
|
10
10
|
const engine_1 = require("../../engine");
|
|
11
11
|
const proxy_connector_1 = require("../../engine/connector/proxy-connector");
|
|
12
|
+
const domain_inheritance_1 = require("../../utils/domain-inheritance");
|
|
12
13
|
const ORMCONFIG = env_1.config.get('ormconfig', {});
|
|
13
14
|
const DATABASE_TYPE = ORMCONFIG.type;
|
|
14
15
|
var ConnectionStatus;
|
|
@@ -20,6 +21,24 @@ var ConnectionStatus;
|
|
|
20
21
|
name: 'ConnectionStatus',
|
|
21
22
|
description: 'Enumeration of possible states for a connection.'
|
|
22
23
|
});
|
|
24
|
+
/**
|
|
25
|
+
* 자식 도메인이 부모의 Connection 정의를 inherit 할 때 동작.
|
|
26
|
+
* - ISOLATE: 자식별로 새 instance 생성. cookies/세션·EnvVar 자격증명 격리. HeadlessConnector
|
|
27
|
+
* 같은 stateful connector 의 자연 default.
|
|
28
|
+
* - SHARE: 부모의 단일 instance 를 자식들이 공유. cookies/params 공통. 공용 외부 API
|
|
29
|
+
* (stateless) connector 의 자연 default.
|
|
30
|
+
*
|
|
31
|
+
* null 이면 Connector.inheritanceMode default 적용, 그것도 없으면 ISOLATE.
|
|
32
|
+
*/
|
|
33
|
+
var ConnectionInheritanceMode;
|
|
34
|
+
(function (ConnectionInheritanceMode) {
|
|
35
|
+
ConnectionInheritanceMode["ISOLATE"] = "isolate";
|
|
36
|
+
ConnectionInheritanceMode["SHARE"] = "share";
|
|
37
|
+
})(ConnectionInheritanceMode || (exports.ConnectionInheritanceMode = ConnectionInheritanceMode = {}));
|
|
38
|
+
(0, type_graphql_1.registerEnumType)(ConnectionInheritanceMode, {
|
|
39
|
+
name: 'ConnectionInheritanceMode',
|
|
40
|
+
description: 'Child domain inheritance mode — ISOLATE: per-child instance with isolated session/credentials. SHARE: single parent instance reused by all children.'
|
|
41
|
+
});
|
|
23
42
|
let Connection = class Connection {
|
|
24
43
|
/**
|
|
25
44
|
* Asynchronous method to establish the connection.
|
|
@@ -47,32 +66,49 @@ let Connection = class Connection {
|
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
68
|
/**
|
|
50
|
-
* Gets a parameter value with
|
|
51
|
-
*
|
|
69
|
+
* Gets a parameter value with EnvVar override support.
|
|
70
|
+
*
|
|
71
|
+
* **Priority (`useDomainAttribute: true` 인 필드)**:
|
|
72
|
+
* 1. **EnvVar** — ancestor closest-wins (자식 도메인이 가장 가까움)
|
|
73
|
+
* 2. **params** 명시값 (parent Connection 의 default 역할)
|
|
74
|
+
* 3. defaultValue
|
|
75
|
+
*
|
|
76
|
+
* **Priority (`useDomainAttribute: false` 또는 미지정 필드)**:
|
|
77
|
+
* 1. **params** 명시값
|
|
78
|
+
* 2. defaultValue
|
|
79
|
+
*
|
|
80
|
+
* 의미론: `useDomainAttribute: true` 는 "이 필드는 도메인별 override 가능" 선언.
|
|
81
|
+
* 따라서 EnvVar 가 본 소스, params 는 모든 자손에 동일 적용되는 default.
|
|
82
|
+
* 자식 도메인 EnvVar override 가 자연스럽게 동작 (시공사 → 프로젝트 자격증명 차별화 등).
|
|
52
83
|
*
|
|
53
84
|
* @param key - The parameter key
|
|
54
85
|
* @param defaultValue - Optional default value
|
|
55
86
|
* @returns The parameter value or default value
|
|
56
87
|
*/
|
|
57
88
|
async getParameter(key, defaultValue) {
|
|
58
|
-
// 1. params에서 직접 값 확인 (null/undefined가 아닌 경우)
|
|
59
|
-
if (this.params?.[key] !== null && this.params?.[key] !== undefined && this.params?.[key] !== '') {
|
|
60
|
-
return this.params[key];
|
|
61
|
-
}
|
|
62
|
-
// 2. useDomainAttribute가 true인 경우에만 EnvVar에서 조회
|
|
63
89
|
const connector = engine_1.ConnectionManager.getConnector(this.type);
|
|
64
90
|
const paramSpec = connector?.parameterSpec?.find(spec => spec.name === key);
|
|
91
|
+
// 1. useDomainAttribute 필드는 EnvVar 우선 조회 (ancestor closest-wins)
|
|
65
92
|
if (paramSpec?.useDomainAttribute) {
|
|
66
93
|
const envVarKey = `Connection::${this.name}::${key}`;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
const domain = await (0, shell_1.getRepository)(shell_1.Domain).findOne({ where: { id: this.domainId } });
|
|
95
|
+
if (domain) {
|
|
96
|
+
const value = await (0, domain_inheritance_1.lookupDomainEnvVar)(domain, envVarKey);
|
|
97
|
+
if (value !== undefined) {
|
|
98
|
+
// params 에 명시값이 있는데 EnvVar 가 override 한 경우 진단 로그
|
|
99
|
+
const paramValue = this.params?.[key];
|
|
100
|
+
if (paramValue !== null && paramValue !== undefined && paramValue !== '') {
|
|
101
|
+
engine_1.ConnectionManager.logger.info(`[domain-attr] Connection '${this.name}'.${key}: params default overridden by EnvVar (domain='${domain.subdomain || domain.id}')`);
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
73
105
|
}
|
|
74
106
|
}
|
|
75
|
-
//
|
|
107
|
+
// 2. params 명시값 (default 역할)
|
|
108
|
+
if (this.params?.[key] !== null && this.params?.[key] !== undefined && this.params?.[key] !== '') {
|
|
109
|
+
return this.params[key];
|
|
110
|
+
}
|
|
111
|
+
// 3. defaultValue
|
|
76
112
|
return defaultValue;
|
|
77
113
|
}
|
|
78
114
|
/**
|
|
@@ -161,6 +197,14 @@ tslib_1.__decorate([
|
|
|
161
197
|
}),
|
|
162
198
|
tslib_1.__metadata("design:type", Boolean)
|
|
163
199
|
], Connection.prototype, "onDemand", void 0);
|
|
200
|
+
tslib_1.__decorate([
|
|
201
|
+
(0, typeorm_1.Column)({ type: 'varchar', nullable: true }),
|
|
202
|
+
(0, type_graphql_1.Field)(type => ConnectionInheritanceMode, {
|
|
203
|
+
nullable: true,
|
|
204
|
+
description: 'Inheritance mode for child domains. ISOLATE = per-child instance with isolated session. SHARE = shared parent instance. null = use Connector default (falls back to ISOLATE).'
|
|
205
|
+
}),
|
|
206
|
+
tslib_1.__metadata("design:type", String)
|
|
207
|
+
], Connection.prototype, "inheritanceMode", void 0);
|
|
164
208
|
tslib_1.__decorate([
|
|
165
209
|
(0, type_graphql_1.Field)({ nullable: true, description: 'The current status of the connection (e.g., CONNECTED, DISCONNECTED).' }),
|
|
166
210
|
tslib_1.__metadata("design:type", String)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../server/service/connection/connection.ts"],"names":[],"mappings":";;;;AAAA,+CAAsE;AACtE,qCASgB;AAEhB,yDAA2D;AAC3D,6CAA4C;AAC5C,iDAA2G;AAE3G,yCAAgD;AAChD,4EAAuE;AAEvE,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AAEpC,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,2CAAuB,CAAA;IACvB,iDAA6B,CAAA,CAAC,kDAAkD;AAClF,CAAC,EAHW,gBAAgB,gCAAhB,gBAAgB,QAG3B;AAED,IAAA,+BAAgB,EAAC,gBAAgB,EAAE;IACjC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,kDAAkD;CAChE,CAAC,CAAA;AAKK,IAAM,UAAU,GAAhB,MAAM,UAAU;IA4KrB;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;QAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,gCAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,0BAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QAEvF,MAAM,SAAS,CAAC,OAAO,CAAC;YACtB,GAAG,IAAI;YACP,MAAM,EAAE,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC,iBAAiB;SAC7D,CAAC,CAAA;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,gCAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,0BAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;YACvF,MAAM,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAClC,CAAC;gBAAS,CAAC;QACX,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,YAAY,CAAC,GAAW,EAAE,YAAkB;QAChD,8CAA8C;QAC9C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;YACjG,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;QAED,gDAAgD;QAChD,MAAM,SAAS,GAAG,0BAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC3D,MAAM,SAAS,GAAG,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,CAAA;QAE3E,IAAI,SAAS,EAAE,kBAAkB,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,eAAe,IAAI,CAAC,IAAI,KAAK,GAAG,EAAE,CAAA;YACpD,MAAM,gBAAgB,GAAG,IAAA,qBAAa,EAAS,cAAM,CAAC,CAAA;YACtD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC;gBAC5C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;aACxE,CAAC,CAAA;YAEF,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,MAAM,CAAC,KAAK,CAAA;YACrB,CAAC;QACH,CAAC;QAED,0CAA0C;QAC1C,OAAO,YAAY,CAAA;IACrB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,qBAAqB;QACzB,MAAM,QAAQ,GAAG,EAAE,CAAA;QAEnB,MAAM,SAAS,GAAG,0BAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC3D,MAAM,UAAU,GAAG,SAAS,EAAE,aAAa,IAAI,EAAE,CAAA;QAEjD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAA;YACrB,QAAQ,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;QAC9C,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AA3PY,gCAAU;AAMZ;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;;sCACzD;AAOnB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;sCAC/F,cAAM;IAEd;;OAEG;;0CAJW;AAMd;IADC,IAAA,oBAAU,EAAC,CAAC,UAAsB,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;;4CAC1C;AAOhB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;wCAC1C;AASZ;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;;+CACjE;AAOnB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;;wCAClF;AAOZ;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,qBAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAChD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAAC;sCAC/F,qBAAS;IAEf;;OAEG;;wCAJY;AAMf;IADC,IAAA,oBAAU,EAAC,CAAC,UAAsB,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;;0CAC1C;AAOd;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;;4CAC1E;AAUhB;IALC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,8DAA8D;KAC5E,CAAC;;0CACa;AAUf;IALC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,kFAAkF;KAChG,CAAC;;4CACe;AAMjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC;;yCACzF;AAwCvB;IAlBC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,aAAa,IAAI,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;QAC7D,QAAQ,EAAE,IAAI;QACd,WAAW,EACT,aAAa,IAAI,UAAU;YACzB,CAAC,CAAC;gBACE,EAAE,EAAE,CAAC,KAAU,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;gBACzC,IAAI,EAAE,CAAC,KAAa,EAAE,EAAE;oBACtB,IAAI,CAAC;wBACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;oBAC1B,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,OAAO,IAAI,CAAA;oBACb,CAAC;gBACH,CAAC;aACF;YACH,CAAC,CAAC,SAAS;KAChB,CAAC;IACD,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;;0CACpF;AAO9B;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;sCAC9E,IAAI;IAEf;;OAEG;;6CAJY;AAOf;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;sCACnF,IAAI;IAEf;;OAEG;;6CAJY;AAOf;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;sCACtE,gBAAI;IAEb;;OAEG;;2CAJU;AAMb;IADC,IAAA,oBAAU,EAAC,CAAC,UAAsB,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;;6CAC1C;AAQjB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;sCAC3E,gBAAI;IAEb;;;OAGG;;2CALU;AAOb;IADC,IAAA,oBAAU,EAAC,CAAC,UAAsB,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;;6CAC1C;qBA1KN,UAAU;IAHtB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,iBAAiB,EAAE,CAAC,UAAsB,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5G,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;GACvF,UAAU,CA2PtB","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'\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@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 * 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 // TODO: 암호화 처리가 필요하면, 이렇게 설정을 변경하라.\n // @Column({\n // type:\n // DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n // ? 'longtext'\n // : DATABASE_TYPE == 'oracle'\n // ? 'clob'\n // : DATABASE_TYPE == 'mssql'\n // ? 'nvarchar'\n // : 'varchar', // PostgreSQL, SQLite\n // length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined,\n // transformer: encryptJsonTransformer\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 * 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 fallback to EnvVar.\n * Priority: params → EnvVar (only for useDomainAttribute=true)\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 // 1. params에서 직접 값 확인 (null/undefined가 아닌 경우)\n if (this.params?.[key] !== null && this.params?.[key] !== undefined && this.params?.[key] !== '') {\n return this.params[key]\n }\n\n // 2. useDomainAttribute가 true인 경우에만 EnvVar에서 조회\n const connector = ConnectionManager.getConnector(this.type)\n const paramSpec = connector?.parameterSpec?.find(spec => spec.name === key)\n\n if (paramSpec?.useDomainAttribute) {\n const envVarKey = `Connection::${this.name}::${key}`\n const envVarRepository = getRepository<EnvVar>(EnvVar)\n const envVar = await envVarRepository.findOne({\n where: { domain: { id: this.domainId }, name: envVarKey, active: true }\n })\n\n if (envVar) {\n return envVar.value\n }\n }\n\n // 3. useDomainAttribute가 false인 경우 기본값 반환\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 { 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 // TODO: 암호화 처리가 필요하면, 이렇게 설정을 변경하라.\n // @Column({\n // type:\n // DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n // ? 'longtext'\n // : DATABASE_TYPE == 'oracle'\n // ? 'clob'\n // : DATABASE_TYPE == 'mssql'\n // ? 'nvarchar'\n // : 'varchar', // PostgreSQL, SQLite\n // length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined,\n // transformer: encryptJsonTransformer\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"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EnvVarResolution } from './domain-attribute-type';
|
|
2
|
+
/**
|
|
3
|
+
* EnvVar 키들의 해소 상태를 일괄 조회.
|
|
4
|
+
*
|
|
5
|
+
* Connection 파라미터 화면, Step 파라미터 편집기에서 useDomainAttribute params 의
|
|
6
|
+
* 4-상태(이 도메인 / 상속 / 미설정) 를 한 번에 가져오기 위한 query.
|
|
7
|
+
*
|
|
8
|
+
* 동작:
|
|
9
|
+
* 1. 현재 도메인부터 루트까지 ancestor id 를 closest-first 로 모은다.
|
|
10
|
+
* 2. 입력 keys 에 매칭되는 EnvVar 중 가장 가까운 도메인 값을 선택한다.
|
|
11
|
+
* 3. 키별로 결과를 만들되, 어디에도 없으면 status='absent'.
|
|
12
|
+
*
|
|
13
|
+
* 멀티-키 단일 쿼리 — N 키를 N 쿼리로 보내지 않도록.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DomainAttributeQuery {
|
|
16
|
+
envVarResolutions(keys: string[], context: ResolverContext): Promise<EnvVarResolution[]>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DomainAttributeQuery = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const shell_1 = require("@things-factory/shell");
|
|
8
|
+
const domain_inheritance_1 = require("../../utils/domain-inheritance");
|
|
9
|
+
const domain_attribute_type_1 = require("./domain-attribute-type");
|
|
10
|
+
/**
|
|
11
|
+
* EnvVar 키들의 해소 상태를 일괄 조회.
|
|
12
|
+
*
|
|
13
|
+
* Connection 파라미터 화면, Step 파라미터 편집기에서 useDomainAttribute params 의
|
|
14
|
+
* 4-상태(이 도메인 / 상속 / 미설정) 를 한 번에 가져오기 위한 query.
|
|
15
|
+
*
|
|
16
|
+
* 동작:
|
|
17
|
+
* 1. 현재 도메인부터 루트까지 ancestor id 를 closest-first 로 모은다.
|
|
18
|
+
* 2. 입력 keys 에 매칭되는 EnvVar 중 가장 가까운 도메인 값을 선택한다.
|
|
19
|
+
* 3. 키별로 결과를 만들되, 어디에도 없으면 status='absent'.
|
|
20
|
+
*
|
|
21
|
+
* 멀티-키 단일 쿼리 — N 키를 N 쿼리로 보내지 않도록.
|
|
22
|
+
*/
|
|
23
|
+
let DomainAttributeQuery = class DomainAttributeQuery {
|
|
24
|
+
async envVarResolutions(keys, context) {
|
|
25
|
+
const { domain } = context.state;
|
|
26
|
+
if (!domain || !Array.isArray(keys) || keys.length === 0) {
|
|
27
|
+
return (keys || []).map(key => ({ key, status: 'absent', hasValue: false }));
|
|
28
|
+
}
|
|
29
|
+
const ids = await (0, domain_inheritance_1.getDomainIdsWithAncestors)(domain);
|
|
30
|
+
const idIndex = new Map(ids.map((id, idx) => [id, idx]));
|
|
31
|
+
const rows = await (0, shell_1.getRepository)(shell_1.EnvVar).find({
|
|
32
|
+
where: { domain: { id: (0, typeorm_1.In)(ids) }, name: (0, typeorm_1.In)(keys), active: true },
|
|
33
|
+
relations: ['domain']
|
|
34
|
+
});
|
|
35
|
+
// key → 가장 가까운 도메인의 EnvVar row 선택
|
|
36
|
+
const closestByKey = new Map();
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
const cur = closestByKey.get(row.name);
|
|
39
|
+
const curRank = cur ? idIndex.get(cur.domainId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
40
|
+
const newRank = idIndex.get(row.domainId) ?? Number.MAX_SAFE_INTEGER;
|
|
41
|
+
if (!cur || newRank < curRank) {
|
|
42
|
+
closestByKey.set(row.name, row);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return keys.map(key => {
|
|
46
|
+
const hit = closestByKey.get(key);
|
|
47
|
+
if (!hit)
|
|
48
|
+
return { key, status: 'absent', hasValue: false };
|
|
49
|
+
const isLocal = hit.domainId === domain.id;
|
|
50
|
+
return {
|
|
51
|
+
key,
|
|
52
|
+
status: isLocal ? 'local' : 'inherited',
|
|
53
|
+
envVarId: hit.id,
|
|
54
|
+
sourceDomainId: hit.domainId,
|
|
55
|
+
sourceDomainName: hit.domain?.name,
|
|
56
|
+
value: hit.value ?? undefined,
|
|
57
|
+
hasValue: hit.value !== null && hit.value !== undefined && hit.value !== ''
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.DomainAttributeQuery = DomainAttributeQuery;
|
|
63
|
+
tslib_1.__decorate([
|
|
64
|
+
(0, type_graphql_1.Query)(returns => [domain_attribute_type_1.EnvVarResolution], {
|
|
65
|
+
description: 'Resolves a batch of EnvVar keys with ancestor closest-wins inheritance.'
|
|
66
|
+
}),
|
|
67
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('keys', () => [String])),
|
|
68
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
69
|
+
tslib_1.__metadata("design:type", Function),
|
|
70
|
+
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
71
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
72
|
+
], DomainAttributeQuery.prototype, "envVarResolutions", null);
|
|
73
|
+
exports.DomainAttributeQuery = DomainAttributeQuery = tslib_1.__decorate([
|
|
74
|
+
(0, type_graphql_1.Resolver)()
|
|
75
|
+
], DomainAttributeQuery);
|
|
76
|
+
//# sourceMappingURL=domain-attribute-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-attribute-query.js","sourceRoot":"","sources":["../../../server/service/domain-attribute/domain-attribute-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Query, Arg, Ctx } from 'type-graphql'\nimport { In } from 'typeorm'\n\nimport { Domain, EnvVar, getRepository } from '@things-factory/shell'\n\nimport { getDomainIdsWithAncestors } from '../../utils/domain-inheritance'\nimport { EnvVarResolution } from './domain-attribute-type'\n\n/**\n * EnvVar 키들의 해소 상태를 일괄 조회.\n *\n * Connection 파라미터 화면, Step 파라미터 편집기에서 useDomainAttribute params 의\n * 4-상태(이 도메인 / 상속 / 미설정) 를 한 번에 가져오기 위한 query.\n *\n * 동작:\n * 1. 현재 도메인부터 루트까지 ancestor id 를 closest-first 로 모은다.\n * 2. 입력 keys 에 매칭되는 EnvVar 중 가장 가까운 도메인 값을 선택한다.\n * 3. 키별로 결과를 만들되, 어디에도 없으면 status='absent'.\n *\n * 멀티-키 단일 쿼리 — N 키를 N 쿼리로 보내지 않도록.\n */\n@Resolver()\nexport class DomainAttributeQuery {\n @Query(returns => [EnvVarResolution], {\n description: 'Resolves a batch of EnvVar keys with ancestor closest-wins inheritance.'\n })\n async envVarResolutions(\n @Arg('keys', () => [String]) keys: string[],\n @Ctx() context: ResolverContext\n ): Promise<EnvVarResolution[]> {\n const { domain } = context.state\n if (!domain || !Array.isArray(keys) || keys.length === 0) {\n return (keys || []).map(key => ({ key, status: 'absent', hasValue: false }))\n }\n\n const ids = await getDomainIdsWithAncestors(domain)\n const idIndex = new Map<string, number>(ids.map((id, idx) => [id, idx]))\n\n const rows = await getRepository(EnvVar).find({\n where: { domain: { id: In(ids) }, name: In(keys), active: true },\n relations: ['domain']\n })\n\n // key → 가장 가까운 도메인의 EnvVar row 선택\n const closestByKey = new Map<string, EnvVar>()\n for (const row of rows) {\n const cur = closestByKey.get(row.name)\n const curRank = cur ? idIndex.get(cur.domainId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER\n const newRank = idIndex.get(row.domainId) ?? Number.MAX_SAFE_INTEGER\n if (!cur || newRank < curRank) {\n closestByKey.set(row.name, row)\n }\n }\n\n return keys.map(key => {\n const hit = closestByKey.get(key)\n if (!hit) return { key, status: 'absent', hasValue: false }\n\n const isLocal = hit.domainId === domain.id\n return {\n key,\n status: isLocal ? 'local' : 'inherited',\n envVarId: hit.id,\n sourceDomainId: hit.domainId,\n sourceDomainName: hit.domain?.name,\n value: hit.value ?? undefined,\n hasValue: hit.value !== null && hit.value !== undefined && hit.value !== ''\n }\n })\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EnvVar 의 ancestor 상속 기반 해소 상태.
|
|
3
|
+
* - status 'local' : 현재 도메인에 등록되어 있음
|
|
4
|
+
* - status 'inherited' : 부모(또는 조상) 도메인에서 상속됨
|
|
5
|
+
* - status 'absent' : 어디에도 없음
|
|
6
|
+
*/
|
|
7
|
+
export declare class EnvVarResolution {
|
|
8
|
+
key: string;
|
|
9
|
+
status: string;
|
|
10
|
+
envVarId?: string;
|
|
11
|
+
sourceDomainId?: string;
|
|
12
|
+
sourceDomainName?: string;
|
|
13
|
+
value?: string;
|
|
14
|
+
hasValue: boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnvVarResolution = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
/**
|
|
7
|
+
* EnvVar 의 ancestor 상속 기반 해소 상태.
|
|
8
|
+
* - status 'local' : 현재 도메인에 등록되어 있음
|
|
9
|
+
* - status 'inherited' : 부모(또는 조상) 도메인에서 상속됨
|
|
10
|
+
* - status 'absent' : 어디에도 없음
|
|
11
|
+
*/
|
|
12
|
+
let EnvVarResolution = class EnvVarResolution {
|
|
13
|
+
};
|
|
14
|
+
exports.EnvVarResolution = EnvVarResolution;
|
|
15
|
+
tslib_1.__decorate([
|
|
16
|
+
(0, type_graphql_1.Field)(),
|
|
17
|
+
tslib_1.__metadata("design:type", String)
|
|
18
|
+
], EnvVarResolution.prototype, "key", void 0);
|
|
19
|
+
tslib_1.__decorate([
|
|
20
|
+
(0, type_graphql_1.Field)(),
|
|
21
|
+
tslib_1.__metadata("design:type", String)
|
|
22
|
+
], EnvVarResolution.prototype, "status", void 0);
|
|
23
|
+
tslib_1.__decorate([
|
|
24
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'EnvVar id (only when found in any domain).' }),
|
|
25
|
+
tslib_1.__metadata("design:type", String)
|
|
26
|
+
], EnvVarResolution.prototype, "envVarId", void 0);
|
|
27
|
+
tslib_1.__decorate([
|
|
28
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
29
|
+
tslib_1.__metadata("design:type", String)
|
|
30
|
+
], EnvVarResolution.prototype, "sourceDomainId", void 0);
|
|
31
|
+
tslib_1.__decorate([
|
|
32
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
33
|
+
tslib_1.__metadata("design:type", String)
|
|
34
|
+
], EnvVarResolution.prototype, "sourceDomainName", void 0);
|
|
35
|
+
tslib_1.__decorate([
|
|
36
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Resolved value when found. Client decides masking based on propertySpec type.' }),
|
|
37
|
+
tslib_1.__metadata("design:type", String)
|
|
38
|
+
], EnvVarResolution.prototype, "value", void 0);
|
|
39
|
+
tslib_1.__decorate([
|
|
40
|
+
(0, type_graphql_1.Field)({ defaultValue: false }),
|
|
41
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
42
|
+
], EnvVarResolution.prototype, "hasValue", void 0);
|
|
43
|
+
exports.EnvVarResolution = EnvVarResolution = tslib_1.__decorate([
|
|
44
|
+
(0, type_graphql_1.ObjectType)({ description: 'Resolved state of an EnvVar key with ancestor closest-wins lookup.' })
|
|
45
|
+
], EnvVarResolution);
|
|
46
|
+
//# sourceMappingURL=domain-attribute-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-attribute-type.js","sourceRoot":"","sources":["../../../server/service/domain-attribute/domain-attribute-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAgD;AAEhD;;;;;GAKG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAqB5B,CAAA;AArBY,4CAAgB;AAE3B;IADC,IAAA,oBAAK,GAAE;;6CACG;AAGX;IADC,IAAA,oBAAK,GAAE;;gDACM;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;;kDACpE;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wDACH;AAGvB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0DACD;AAGzB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+EAA+E,EAAE,CAAC;;+CAC1G;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;;kDACd;2BApBN,gBAAgB;IAD5B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,oEAAoE,EAAE,CAAC;GACrF,gBAAgB,CAqB5B","sourcesContent":["import { Field, ObjectType } from 'type-graphql'\n\n/**\n * EnvVar 의 ancestor 상속 기반 해소 상태.\n * - status 'local' : 현재 도메인에 등록되어 있음\n * - status 'inherited' : 부모(또는 조상) 도메인에서 상속됨\n * - status 'absent' : 어디에도 없음\n */\n@ObjectType({ description: 'Resolved state of an EnvVar key with ancestor closest-wins lookup.' })\nexport class EnvVarResolution {\n @Field()\n key: string\n\n @Field()\n status: string\n\n @Field({ nullable: true, description: 'EnvVar id (only when found in any domain).' })\n envVarId?: string\n\n @Field({ nullable: true })\n sourceDomainId?: string\n\n @Field({ nullable: true })\n sourceDomainName?: string\n\n @Field({ nullable: true, description: 'Resolved value when found. Client decides masking based on propertySpec type.' })\n value?: string\n\n @Field({ defaultValue: false })\n hasValue: boolean\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvers = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const domain_attribute_query_1 = require("./domain-attribute-query");
|
|
6
|
+
exports.resolvers = [domain_attribute_query_1.DomainAttributeQuery];
|
|
7
|
+
tslib_1.__exportStar(require("./domain-attribute-type"), exports);
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/domain-attribute/index.ts"],"names":[],"mappings":";;;;AAAA,qEAA+D;AAElD,QAAA,SAAS,GAAG,CAAC,6CAAoB,CAAC,CAAA;AAE/C,kEAAuC","sourcesContent":["import { DomainAttributeQuery } from './domain-attribute-query'\n\nexport const resolvers = [DomainAttributeQuery]\n\nexport * from './domain-attribute-type'\n"]}
|
|
@@ -12,7 +12,7 @@ export * from './payload-log/payload-log';
|
|
|
12
12
|
export * from './state-register/state-register';
|
|
13
13
|
export declare const entities: any[];
|
|
14
14
|
export declare const schema: {
|
|
15
|
-
resolverClasses: (typeof import("./connection/connection-query").ConnectionQuery | typeof import("./connection/connection-mutation").ConnectionMutation | typeof import("./connection/connection-subscription").ConnectionSubscription | typeof import("./connector/connector-query").ConnectorQuery | typeof import("./scenario/scenario-query").ScenarioQuery | typeof import("./scenario/scenario-mutation").ScenarioMutation | typeof import("./scenario-instance/scenario-instance-query").ScenarioInstanceQuery | typeof import("./scenario-instance/scenario-instance-mutation").ScenarioInstanceMutation | typeof import("./scenario-instance/scenario-instance-subscription").ScenarioInstanceSubscription | typeof import("./scenario-queue/scenario-queue-subscription").ScenarioQueueSubscription | typeof import("./step/step-query").StepQuery | typeof import("./step/step-mutation").StepMutation | typeof import("./task-type/task-type-query").TaskTypeQuery | typeof import("./payload-log/payload-log-query").PayloadLogQuery | typeof import("./payload-log/payload-log-mutation").PayloadLogMutation | typeof import("./state-register/state-register-query").StateRegisterQuery | typeof import("./state-register/state-register-mutation").StateRegisterMutation | typeof import("./state-register/data-resolver").DataResolver | typeof import("./analysis/analysis-query").IntegrationAnalysisQuery)[];
|
|
15
|
+
resolverClasses: (typeof import("./connection/connection-query").ConnectionQuery | typeof import("./connection/connection-mutation").ConnectionMutation | typeof import("./connection/connection-subscription").ConnectionSubscription | typeof import("./connector/connector-query").ConnectorQuery | typeof import("./scenario/scenario-query").ScenarioQuery | typeof import("./scenario/scenario-mutation").ScenarioMutation | typeof import("./scenario-instance/scenario-instance-query").ScenarioInstanceQuery | typeof import("./scenario-instance/scenario-instance-mutation").ScenarioInstanceMutation | typeof import("./scenario-instance/scenario-instance-subscription").ScenarioInstanceSubscription | typeof import("./scenario-queue/scenario-queue-subscription").ScenarioQueueSubscription | typeof import("./step/step-query").StepQuery | typeof import("./step/step-mutation").StepMutation | typeof import("./task-type/task-type-query").TaskTypeQuery | typeof import("./payload-log/payload-log-query").PayloadLogQuery | typeof import("./payload-log/payload-log-mutation").PayloadLogMutation | typeof import("./state-register/state-register-query").StateRegisterQuery | typeof import("./state-register/state-register-mutation").StateRegisterMutation | typeof import("./state-register/data-resolver").DataResolver | typeof import("./analysis/analysis-query").IntegrationAnalysisQuery | typeof import("./domain-attribute/domain-attribute-query").DomainAttributeQuery)[];
|
|
16
16
|
};
|
|
17
17
|
export { PayloadType } from './payload-log/payload-log';
|
|
18
18
|
export { createPayloadLog } from './payload-log/payload-log-mutation';
|
|
@@ -12,6 +12,7 @@ const task_type_1 = require("./task-type");
|
|
|
12
12
|
const payload_log_1 = require("./payload-log");
|
|
13
13
|
const state_register_1 = require("./state-register");
|
|
14
14
|
const analysis_1 = require("./analysis");
|
|
15
|
+
const domain_attribute_1 = require("./domain-attribute");
|
|
15
16
|
tslib_1.__exportStar(require("./property-spec"), exports);
|
|
16
17
|
tslib_1.__exportStar(require("./task-type/task-type-type"), exports);
|
|
17
18
|
tslib_1.__exportStar(require("./connector/connector-type"), exports);
|
|
@@ -46,7 +47,8 @@ exports.schema = {
|
|
|
46
47
|
...step_1.resolvers,
|
|
47
48
|
...payload_log_1.resolvers,
|
|
48
49
|
...analysis_1.resolvers,
|
|
49
|
-
...state_register_1.resolvers
|
|
50
|
+
...state_register_1.resolvers,
|
|
51
|
+
...domain_attribute_1.resolvers
|
|
50
52
|
]
|
|
51
53
|
};
|
|
52
54
|
var payload_log_2 = require("./payload-log/payload-log");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,6CAA+F;AAC/F,2CAA4F;AAC5F,yCAAyF;AACzF,2DAAkH;AAClH,qDAAyG;AACzG,iCAA6E;AAC7E,2CAA0F;AAC1F,+CAAgG;AAChG,qDAAyG;AACzG,yCAAkE;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,6CAA+F;AAC/F,2CAA4F;AAC5F,yCAAyF;AACzF,2DAAkH;AAClH,qDAAyG;AACzG,iCAA6E;AAC7E,2CAA0F;AAC1F,+CAAgG;AAChG,qDAAyG;AACzG,yCAAkE;AAClE,yDAA0E;AAE1E,0DAA+B;AAC/B,qEAA0C;AAC1C,qEAA0C;AAC1C,kEAAuC;AACvC,uEAA4C;AAC5C,8DAAmC;AACnC,qFAA0D;AAC1D,+EAAoD;AACpD,2DAAgC;AAChC,wEAA6C;AAC7C,oEAAyC;AACzC,0EAA+C;AAElC,QAAA,QAAQ,GAAG;IACtB,GAAG,oBAAgB;IACnB,GAAG,oBAAiB;IACpB,GAAG,qBAAkB;IACrB,GAAG,mBAAgB;IACnB,GAAG,4BAAwB;IAC3B,GAAG,yBAAqB;IACxB,GAAG,eAAY;IACf,GAAG,sBAAkB;IACrB,GAAG,yBAAqB;CACzB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,GAAG,qBAAiB;QACpB,GAAG,qBAAkB;QACrB,GAAG,sBAAmB;QACtB,GAAG,oBAAiB;QACpB,GAAG,6BAAyB;QAC5B,GAAG,0BAAsB;QACzB,GAAG,gBAAa;QAChB,GAAG,uBAAmB;QACtB,GAAG,oBAAwB;QAC3B,GAAG,0BAAsB;QACzB,GAAG,4BAAwB;KAC5B;CACF,CAAA;AAED,yDAAuD;AAA9C,0GAAA,WAAW,OAAA;AACpB,2EAAqE;AAA5D,wHAAA,gBAAgB,OAAA","sourcesContent":["import { entities as ConnectionEntities, resolvers as ConnectionResolvers } from './connection'\nimport { entities as ConnectorEntities, resolvers as ConnectorResolvers } from './connector'\nimport { entities as ScenarioEntities, resolvers as ScenarioResolvers } from './scenario'\nimport { entities as ScenarioInstanceEntities, resolvers as ScenarioInstanceResolvers } from './scenario-instance'\nimport { entities as ScenarioQueueEntities, resolvers as ScenarioQueueResolvers } from './scenario-queue'\nimport { entities as StepEntities, resolvers as StepResolvers } from './step'\nimport { entities as TaskTypeEntities, resolvers as TaskTypeResolvers } from './task-type'\nimport { entities as PayloadLogEntities, resolvers as PayloadLogResolvers } from './payload-log'\nimport { entities as StateRegisterEntities, resolvers as StateRegisterResolvers } from './state-register'\nimport { resolvers as IntegrationAnalysisQuery } from './analysis'\nimport { resolvers as DomainAttributeResolvers } from './domain-attribute'\n\nexport * from './property-spec'\nexport * from './task-type/task-type-type'\nexport * from './connector/connector-type'\nexport * from './connection/connection'\nexport * from './connection/connection-type'\nexport * from './scenario/scenario'\nexport * from './scenario-instance/scenario-instance-type'\nexport * from './scenario-queue/scenario-queue-type'\nexport * from './step/step-type'\nexport * from './scenario-flow/scenario-flow'\nexport * from './payload-log/payload-log'\nexport * from './state-register/state-register'\n\nexport const entities = [\n ...TaskTypeEntities,\n ...ConnectorEntities,\n ...ConnectionEntities,\n ...ScenarioEntities,\n ...ScenarioInstanceEntities,\n ...ScenarioQueueEntities,\n ...StepEntities,\n ...PayloadLogEntities,\n ...StateRegisterEntities\n]\n\nexport const schema = {\n resolverClasses: [\n ...TaskTypeResolvers,\n ...ConnectorResolvers,\n ...ConnectionResolvers,\n ...ScenarioResolvers,\n ...ScenarioInstanceResolvers,\n ...ScenarioQueueResolvers,\n ...StepResolvers,\n ...PayloadLogResolvers,\n ...IntegrationAnalysisQuery,\n ...StateRegisterResolvers,\n ...DomainAttributeResolvers\n ]\n}\n\nexport { PayloadType } from './payload-log/payload-log'\nexport { createPayloadLog } from './payload-log/payload-log-mutation'\n"]}
|