agentlang 0.0.2
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/LICENSE +83 -0
- package/README.md +120 -0
- package/bin/cli.js +4 -0
- package/out/api/http.d.ts +3 -0
- package/out/api/http.d.ts.map +1 -0
- package/out/api/http.js +290 -0
- package/out/api/http.js.map +1 -0
- package/out/cli/cli-util.d.ts +7 -0
- package/out/cli/cli-util.d.ts.map +1 -0
- package/out/cli/cli-util.js +9 -0
- package/out/cli/cli-util.js.map +1 -0
- package/out/cli/docs.d.ts +2 -0
- package/out/cli/docs.d.ts.map +1 -0
- package/out/cli/docs.js +236 -0
- package/out/cli/docs.js.map +1 -0
- package/out/cli/main.d.ts +288 -0
- package/out/cli/main.d.ts.map +1 -0
- package/out/cli/main.js +119 -0
- package/out/cli/main.js.map +1 -0
- package/out/cli/openapi-docs.yml +695 -0
- package/out/extension/main.cjs +18093 -0
- package/out/extension/main.cjs.map +7 -0
- package/out/extension/main.d.ts +4 -0
- package/out/extension/main.d.ts.map +1 -0
- package/out/extension/main.js +42 -0
- package/out/extension/main.js.map +1 -0
- package/out/language/agentlang-module.d.ts +42 -0
- package/out/language/agentlang-module.d.ts.map +1 -0
- package/out/language/agentlang-module.js +42 -0
- package/out/language/agentlang-module.js.map +1 -0
- package/out/language/agentlang-validator.d.ts +15 -0
- package/out/language/agentlang-validator.d.ts.map +1 -0
- package/out/language/agentlang-validator.js +50 -0
- package/out/language/agentlang-validator.js.map +1 -0
- package/out/language/generated/ast.d.ts +491 -0
- package/out/language/generated/ast.d.ts.map +1 -0
- package/out/language/generated/ast.js +934 -0
- package/out/language/generated/ast.js.map +1 -0
- package/out/language/generated/grammar.d.ts +7 -0
- package/out/language/generated/grammar.d.ts.map +1 -0
- package/out/language/generated/grammar.js +4475 -0
- package/out/language/generated/grammar.js.map +1 -0
- package/out/language/generated/module.d.ts +14 -0
- package/out/language/generated/module.d.ts.map +1 -0
- package/out/language/generated/module.js +21 -0
- package/out/language/generated/module.js.map +1 -0
- package/out/language/main-browser.d.ts +2 -0
- package/out/language/main-browser.d.ts.map +1 -0
- package/out/language/main-browser.js +10 -0
- package/out/language/main-browser.js.map +1 -0
- package/out/language/main.cjs +36229 -0
- package/out/language/main.cjs.map +7 -0
- package/out/language/main.d.ts +2 -0
- package/out/language/main.d.ts.map +1 -0
- package/out/language/main.js +11 -0
- package/out/language/main.js.map +1 -0
- package/out/language/parser.d.ts +9 -0
- package/out/language/parser.d.ts.map +1 -0
- package/out/language/parser.js +273 -0
- package/out/language/parser.js.map +1 -0
- package/out/language/syntax.d.ts +155 -0
- package/out/language/syntax.d.ts.map +1 -0
- package/out/language/syntax.js +527 -0
- package/out/language/syntax.js.map +1 -0
- package/out/runtime/agents/common.d.ts +2 -0
- package/out/runtime/agents/common.d.ts.map +1 -0
- package/out/runtime/agents/common.js +178 -0
- package/out/runtime/agents/common.js.map +1 -0
- package/out/runtime/agents/impl/openai.d.ts +8 -0
- package/out/runtime/agents/impl/openai.d.ts.map +1 -0
- package/out/runtime/agents/impl/openai.js +15 -0
- package/out/runtime/agents/impl/openai.js.map +1 -0
- package/out/runtime/agents/provider.d.ts +21 -0
- package/out/runtime/agents/provider.d.ts.map +1 -0
- package/out/runtime/agents/provider.js +32 -0
- package/out/runtime/agents/provider.js.map +1 -0
- package/out/runtime/agents/registry.d.ts +2 -0
- package/out/runtime/agents/registry.d.ts.map +1 -0
- package/out/runtime/agents/registry.js +10 -0
- package/out/runtime/agents/registry.js.map +1 -0
- package/out/runtime/auth/cognito.d.ts +16 -0
- package/out/runtime/auth/cognito.d.ts.map +1 -0
- package/out/runtime/auth/cognito.js +186 -0
- package/out/runtime/auth/cognito.js.map +1 -0
- package/out/runtime/auth/defs.d.ts +11 -0
- package/out/runtime/auth/defs.d.ts.map +1 -0
- package/out/runtime/auth/defs.js +24 -0
- package/out/runtime/auth/defs.js.map +1 -0
- package/out/runtime/auth/interface.d.ts +22 -0
- package/out/runtime/auth/interface.d.ts.map +1 -0
- package/out/runtime/auth/interface.js +2 -0
- package/out/runtime/auth/interface.js.map +1 -0
- package/out/runtime/defs.js +24 -0
- package/out/runtime/defs.js.map +1 -0
- package/out/runtime/interpreter.d.ts +69 -0
- package/out/runtime/interpreter.d.ts.map +1 -0
- package/out/runtime/interpreter.js +1163 -0
- package/out/runtime/interpreter.js.map +1 -0
- package/out/runtime/loader.d.ts +25 -0
- package/out/runtime/loader.d.ts.map +1 -0
- package/out/runtime/loader.js +346 -0
- package/out/runtime/loader.js.map +1 -0
- package/out/runtime/logger.d.ts +2 -0
- package/out/runtime/logger.d.ts.map +1 -0
- package/out/runtime/logger.js +44 -0
- package/out/runtime/logger.js.map +1 -0
- package/out/runtime/module.d.ts +273 -0
- package/out/runtime/module.d.ts.map +1 -0
- package/out/runtime/module.js +1786 -0
- package/out/runtime/module.js.map +1 -0
- package/out/runtime/modules/ai.d.ts +26 -0
- package/out/runtime/modules/ai.d.ts.map +1 -0
- package/out/runtime/modules/ai.js +211 -0
- package/out/runtime/modules/ai.js.map +1 -0
- package/out/runtime/modules/auth.d.ts +39 -0
- package/out/runtime/modules/auth.d.ts.map +1 -0
- package/out/runtime/modules/auth.js +359 -0
- package/out/runtime/modules/auth.js.map +1 -0
- package/out/runtime/modules/core.d.ts +2 -0
- package/out/runtime/modules/core.d.ts.map +1 -0
- package/out/runtime/modules/core.js +67 -0
- package/out/runtime/modules/core.js.map +1 -0
- package/out/runtime/relgraph.d.ts +21 -0
- package/out/runtime/relgraph.d.ts.map +1 -0
- package/out/runtime/relgraph.js +156 -0
- package/out/runtime/relgraph.js.map +1 -0
- package/out/runtime/resolvers/interface.d.ts +59 -0
- package/out/runtime/resolvers/interface.d.ts.map +1 -0
- package/out/runtime/resolvers/interface.js +111 -0
- package/out/runtime/resolvers/interface.js.map +1 -0
- package/out/runtime/resolvers/registry.d.ts +8 -0
- package/out/runtime/resolvers/registry.d.ts.map +1 -0
- package/out/runtime/resolvers/registry.js +26 -0
- package/out/runtime/resolvers/registry.js.map +1 -0
- package/out/runtime/resolvers/sqldb/database.d.ts +50 -0
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -0
- package/out/runtime/resolvers/sqldb/database.js +618 -0
- package/out/runtime/resolvers/sqldb/database.js.map +1 -0
- package/out/runtime/resolvers/sqldb/dbutil.d.ts +18 -0
- package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -0
- package/out/runtime/resolvers/sqldb/dbutil.js +221 -0
- package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -0
- package/out/runtime/resolvers/sqldb/impl.d.ts +26 -0
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -0
- package/out/runtime/resolvers/sqldb/impl.js +300 -0
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -0
- package/out/runtime/state.js +83 -0
- package/out/runtime/state.js.map +1 -0
- package/out/runtime/util.d.ts +43 -0
- package/out/runtime/util.d.ts.map +1 -0
- package/out/runtime/util.js +447 -0
- package/out/runtime/util.js.map +1 -0
- package/out/setupClassic.d.ts +98 -0
- package/out/setupClassic.d.ts.map +1 -0
- package/out/setupClassic.js +38 -0
- package/out/setupClassic.js.map +1 -0
- package/out/setupCommon.d.ts +2 -0
- package/out/setupCommon.d.ts.map +1 -0
- package/out/setupCommon.js +33 -0
- package/out/setupCommon.js.map +1 -0
- package/out/setupExtended.d.ts +40 -0
- package/out/setupExtended.d.ts.map +1 -0
- package/out/setupExtended.js +67 -0
- package/out/setupExtended.js.map +1 -0
- package/out/syntaxes/agentlang.monarch.d.ts +77 -0
- package/out/syntaxes/agentlang.monarch.d.ts.map +1 -0
- package/out/syntaxes/agentlang.monarch.js +31 -0
- package/out/syntaxes/agentlang.monarch.js.map +1 -0
- package/out/utils/fs/index.d.ts +14 -0
- package/out/utils/fs/index.d.ts.map +1 -0
- package/out/utils/fs/index.js +26 -0
- package/out/utils/fs/index.js.map +1 -0
- package/out/utils/fs/interfaces.d.ts +105 -0
- package/out/utils/fs/interfaces.d.ts.map +1 -0
- package/out/utils/fs/interfaces.js +5 -0
- package/out/utils/fs/interfaces.js.map +1 -0
- package/out/utils/fs/lightning-fs.d.ts +116 -0
- package/out/utils/fs/lightning-fs.d.ts.map +1 -0
- package/out/utils/fs/lightning-fs.js +243 -0
- package/out/utils/fs/lightning-fs.js.map +1 -0
- package/out/utils/fs/node-fs.d.ts +93 -0
- package/out/utils/fs/node-fs.d.ts.map +1 -0
- package/out/utils/fs/node-fs.js +169 -0
- package/out/utils/fs/node-fs.js.map +1 -0
- package/out/utils/fs-utils.d.ts +153 -0
- package/out/utils/fs-utils.d.ts.map +1 -0
- package/out/utils/fs-utils.js +271 -0
- package/out/utils/fs-utils.js.map +1 -0
- package/out/utils/runtime.d.ts +36 -0
- package/out/utils/runtime.d.ts.map +1 -0
- package/out/utils/runtime.js +39 -0
- package/out/utils/runtime.js.map +1 -0
- package/package.json +155 -0
- package/src/api/http.ts +361 -0
- package/src/cli/cli-util.ts +18 -0
- package/src/cli/main.ts +146 -0
- package/src/extension/main.ts +51 -0
- package/src/language/agentlang-module.ts +75 -0
- package/src/language/agentlang-validator.ts +60 -0
- package/src/language/agentlang.langium +178 -0
- package/src/language/generated/ast.ts +1698 -0
- package/src/language/generated/grammar.ts +4477 -0
- package/src/language/generated/module.ts +25 -0
- package/src/language/main-browser.ts +19 -0
- package/src/language/main.ts +13 -0
- package/src/language/parser.ts +329 -0
- package/src/language/syntax.ts +646 -0
- package/src/runtime/agents/common.ts +177 -0
- package/src/runtime/agents/impl/openai.ts +19 -0
- package/src/runtime/agents/provider.ts +58 -0
- package/src/runtime/agents/registry.ts +9 -0
- package/src/runtime/auth/cognito.ts +225 -0
- package/src/runtime/auth/defs.ts +33 -0
- package/src/runtime/auth/interface.ts +31 -0
- package/src/runtime/defs.ts +33 -0
- package/src/runtime/interpreter.ts +1352 -0
- package/src/runtime/loader.ts +450 -0
- package/src/runtime/logger.ts +51 -0
- package/src/runtime/module.ts +2188 -0
- package/src/runtime/modules/ai.ts +257 -0
- package/src/runtime/modules/auth.ts +489 -0
- package/src/runtime/modules/core.ts +95 -0
- package/src/runtime/relgraph.ts +195 -0
- package/src/runtime/resolvers/interface.ts +160 -0
- package/src/runtime/resolvers/registry.ts +30 -0
- package/src/runtime/resolvers/sqldb/database.ts +823 -0
- package/src/runtime/resolvers/sqldb/dbutil.ts +257 -0
- package/src/runtime/resolvers/sqldb/impl.ts +471 -0
- package/src/runtime/state.ts +87 -0
- package/src/runtime/util.ts +513 -0
- package/src/setupClassic.ts +43 -0
- package/src/setupCommon.ts +33 -0
- package/src/setupExtended.ts +79 -0
- package/src/syntaxes/agentlang.monarch.ts +31 -0
- package/src/utils/fs/index.ts +28 -0
- package/src/utils/fs/interfaces.ts +118 -0
- package/src/utils/fs/lightning-fs.ts +284 -0
- package/src/utils/fs/node-fs.ts +185 -0
- package/src/utils/fs-utils.ts +304 -0
- package/src/utils/runtime.ts +43 -0
|
@@ -0,0 +1,823 @@
|
|
|
1
|
+
import { DataSource, EntityManager, EntitySchema, QueryRunner, SelectQueryBuilder } from 'typeorm';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
import {
|
|
4
|
+
DefaultVectorDimension,
|
|
5
|
+
modulesAsOrmSchema,
|
|
6
|
+
OwnersSuffix,
|
|
7
|
+
VectorSuffix,
|
|
8
|
+
} from './dbutil.js';
|
|
9
|
+
import { DefaultAuthInfo, ResolverAuthInfo } from '../interface.js';
|
|
10
|
+
import { canUserCreate, canUserDelete, canUserRead, canUserUpdate } from '../../modules/auth.js';
|
|
11
|
+
import { Environment, GlobalEnvironment } from '../../interpreter.js';
|
|
12
|
+
import {
|
|
13
|
+
Instance,
|
|
14
|
+
InstanceAttributes,
|
|
15
|
+
newInstanceAttributes,
|
|
16
|
+
RbacPermissionFlag,
|
|
17
|
+
Relationship,
|
|
18
|
+
} from '../../module.js';
|
|
19
|
+
import pgvector from 'pgvector';
|
|
20
|
+
import { isString } from '../../util.js';
|
|
21
|
+
import { DeletedFlagAttributeName, PathAttributeName, UnauthorisedError } from '../../defs.js';
|
|
22
|
+
|
|
23
|
+
export let defaultDataSource: DataSource | undefined;
|
|
24
|
+
|
|
25
|
+
export class DbContext {
|
|
26
|
+
txnId: string | undefined;
|
|
27
|
+
authInfo: ResolverAuthInfo;
|
|
28
|
+
private inKernelMode: boolean = false;
|
|
29
|
+
resourceFqName: string;
|
|
30
|
+
activeEnv: Environment;
|
|
31
|
+
private needAuthCheckFlag: boolean = true;
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
resourceFqName: string,
|
|
35
|
+
authInfo: ResolverAuthInfo,
|
|
36
|
+
activeEnv: Environment,
|
|
37
|
+
txnId?: string,
|
|
38
|
+
inKernelMode?: boolean
|
|
39
|
+
) {
|
|
40
|
+
this.resourceFqName = resourceFqName;
|
|
41
|
+
this.authInfo = authInfo;
|
|
42
|
+
this.activeEnv = activeEnv;
|
|
43
|
+
this.txnId = txnId;
|
|
44
|
+
if (inKernelMode != undefined) {
|
|
45
|
+
this.inKernelMode = inKernelMode;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
private static GlobalDbContext: DbContext | undefined;
|
|
49
|
+
|
|
50
|
+
static getGlobalContext(): DbContext {
|
|
51
|
+
if (DbContext.GlobalDbContext == undefined) {
|
|
52
|
+
DbContext.GlobalDbContext = new DbContext(
|
|
53
|
+
'',
|
|
54
|
+
DefaultAuthInfo,
|
|
55
|
+
GlobalEnvironment,
|
|
56
|
+
undefined,
|
|
57
|
+
true
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return DbContext.GlobalDbContext;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Shallow clone
|
|
64
|
+
clone(): DbContext {
|
|
65
|
+
return new DbContext(
|
|
66
|
+
this.resourceFqName,
|
|
67
|
+
this.authInfo,
|
|
68
|
+
this.activeEnv,
|
|
69
|
+
this.txnId,
|
|
70
|
+
this.inKernelMode
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getUserId(): string {
|
|
75
|
+
return this.authInfo.userId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isForDelete(): boolean {
|
|
79
|
+
return this.authInfo.readForDelete;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
isForUpdate(): boolean {
|
|
83
|
+
return this.authInfo.readForUpdate;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setResourceFqNameFrom(inst: Instance): DbContext {
|
|
87
|
+
this.resourceFqName = inst.getFqName();
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setNeedAuthCheck(flag: boolean): DbContext {
|
|
92
|
+
this.needAuthCheckFlag = flag;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
isPermitted(): boolean {
|
|
97
|
+
return this.inKernelMode || !this.needAuthCheckFlag;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
isInKernelMode(): boolean {
|
|
101
|
+
return this.inKernelMode;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type JoinOn = {
|
|
106
|
+
attributeName: string;
|
|
107
|
+
operator: string;
|
|
108
|
+
attributeValue: any;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export function makeJoinOn(attrName: string, attrValue: any, opr: string = '='): JoinOn {
|
|
112
|
+
return {
|
|
113
|
+
attributeName: attrName,
|
|
114
|
+
attributeValue: attrValue,
|
|
115
|
+
operator: opr,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type JoinClause = {
|
|
120
|
+
tableName: string;
|
|
121
|
+
queryObject?: object;
|
|
122
|
+
queryValues?: object;
|
|
123
|
+
joinOn: JoinOn | JoinOn[];
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type DatabaseConfig = {
|
|
127
|
+
type: string;
|
|
128
|
+
host?: string;
|
|
129
|
+
username?: string;
|
|
130
|
+
password?: string;
|
|
131
|
+
dbname?: string;
|
|
132
|
+
port?: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
function mkDbName(): string {
|
|
136
|
+
return process.env.AGENTLANG_DB_NAME || `db-${Date.now()}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function makePostgresDataSource(
|
|
140
|
+
entities: EntitySchema[],
|
|
141
|
+
config: DatabaseConfig | undefined,
|
|
142
|
+
synchronize: boolean = true
|
|
143
|
+
): DataSource {
|
|
144
|
+
return new DataSource({
|
|
145
|
+
type: 'postgres',
|
|
146
|
+
host: process.env.POSTGRES_HOST || config?.host || 'localhost',
|
|
147
|
+
port: getPostgressEnvPort() || config?.port || 5432,
|
|
148
|
+
username: process.env.POSTGRES_USER || config?.username || 'postgres',
|
|
149
|
+
password: process.env.POSTGRES_PASSWORD || config?.password || 'postgres',
|
|
150
|
+
database: process.env.POSTGRES_DB || config?.dbname || 'postgres',
|
|
151
|
+
synchronize: synchronize,
|
|
152
|
+
entities: entities,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getPostgressEnvPort(): number | undefined {
|
|
157
|
+
const s: string | undefined = process.env.POSTGRES_PORT;
|
|
158
|
+
if (s) {
|
|
159
|
+
return Number(s);
|
|
160
|
+
} else {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function makeSqliteDataSource(
|
|
166
|
+
entities: EntitySchema[],
|
|
167
|
+
config: DatabaseConfig | undefined,
|
|
168
|
+
synchronize: boolean = true
|
|
169
|
+
): DataSource {
|
|
170
|
+
return new DataSource({
|
|
171
|
+
type: 'sqlite',
|
|
172
|
+
database: config?.dbname || mkDbName(),
|
|
173
|
+
synchronize: synchronize,
|
|
174
|
+
entities: entities,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const DbType = 'sqlite';
|
|
179
|
+
|
|
180
|
+
function getDsFunction(
|
|
181
|
+
config: DatabaseConfig | undefined
|
|
182
|
+
): (
|
|
183
|
+
entities: EntitySchema<any>[],
|
|
184
|
+
config: DatabaseConfig | undefined,
|
|
185
|
+
synchronize?: boolean | undefined
|
|
186
|
+
) => DataSource {
|
|
187
|
+
switch (process.env.AL_DB_TYPE || config?.type || DbType) {
|
|
188
|
+
case 'sqlite':
|
|
189
|
+
return makeSqliteDataSource;
|
|
190
|
+
case 'postgres':
|
|
191
|
+
return makePostgresDataSource;
|
|
192
|
+
default:
|
|
193
|
+
throw new Error(`Unsupported database type - ${config?.type}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function initDatabase(config: DatabaseConfig | undefined) {
|
|
198
|
+
if (defaultDataSource == undefined) {
|
|
199
|
+
const mkds = getDsFunction(config);
|
|
200
|
+
if (mkds) {
|
|
201
|
+
const ormScm = modulesAsOrmSchema();
|
|
202
|
+
defaultDataSource = mkds(ormScm.entities, config) as DataSource;
|
|
203
|
+
await defaultDataSource.initialize();
|
|
204
|
+
const vectEnts = ormScm.vectorEntities.map((es: EntitySchema) => {
|
|
205
|
+
return es.options.name;
|
|
206
|
+
});
|
|
207
|
+
if (vectEnts.length > 0) {
|
|
208
|
+
await initVectorStore(vectEnts, DbContext.getGlobalContext());
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
throw new Error(`Unsupported database type - ${DbType}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function resetDefaultDatabase() {
|
|
217
|
+
if (defaultDataSource) {
|
|
218
|
+
await defaultDataSource.destroy();
|
|
219
|
+
defaultDataSource = undefined;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function ownersTable(tableName: string): string {
|
|
224
|
+
return (tableName + OwnersSuffix).toLowerCase();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function insertRowsHelper(
|
|
228
|
+
tableName: string,
|
|
229
|
+
rows: object[],
|
|
230
|
+
ctx: DbContext,
|
|
231
|
+
doUpsert: boolean
|
|
232
|
+
): Promise<void> {
|
|
233
|
+
const repo = getDatasourceForTransaction(ctx.txnId).getRepository(tableName);
|
|
234
|
+
if (doUpsert) await repo.save(rows);
|
|
235
|
+
else await repo.insert(rows);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function addRowForFullTextSearch(
|
|
239
|
+
tableName: string,
|
|
240
|
+
id: string,
|
|
241
|
+
vect: number[],
|
|
242
|
+
ctx: DbContext
|
|
243
|
+
) {
|
|
244
|
+
try {
|
|
245
|
+
const vecTableName = tableName + VectorSuffix;
|
|
246
|
+
const qb = getDatasourceForTransaction(ctx.txnId).createQueryBuilder();
|
|
247
|
+
await qb
|
|
248
|
+
.insert()
|
|
249
|
+
.into(vecTableName)
|
|
250
|
+
.values([{ id: id, embedding: pgvector.toSql(vect) }])
|
|
251
|
+
.execute();
|
|
252
|
+
} catch (err: any) {
|
|
253
|
+
logger.error(`Failed to add row to vector store - ${err}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function initVectorStore(tableNames: string[], ctx: DbContext) {
|
|
258
|
+
let notInited = true;
|
|
259
|
+
tableNames.forEach(async (vecTableName: string) => {
|
|
260
|
+
const vecRepo = getDatasourceForTransaction(ctx.txnId).getRepository(vecTableName);
|
|
261
|
+
if (notInited) {
|
|
262
|
+
let failure = false;
|
|
263
|
+
try {
|
|
264
|
+
await vecRepo.query('CREATE EXTENSION IF NOT EXISTS vector');
|
|
265
|
+
} catch (err: any) {
|
|
266
|
+
logger.error(`Failed to initialize vector store - ${err}`);
|
|
267
|
+
failure = true;
|
|
268
|
+
}
|
|
269
|
+
if (failure) return;
|
|
270
|
+
notInited = false;
|
|
271
|
+
}
|
|
272
|
+
await vecRepo.query(
|
|
273
|
+
`CREATE TABLE IF NOT EXISTS ${vecTableName} (
|
|
274
|
+
id varchar PRIMARY KEY,
|
|
275
|
+
embedding vector(${DefaultVectorDimension}),
|
|
276
|
+
__is_deleted__ boolean default false
|
|
277
|
+
)`
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export async function vectorStoreSearch(
|
|
283
|
+
tableName: string,
|
|
284
|
+
searchVec: number[],
|
|
285
|
+
limit: number,
|
|
286
|
+
ctx: DbContext
|
|
287
|
+
): Promise<any> {
|
|
288
|
+
try {
|
|
289
|
+
const vecTableName = tableName + VectorSuffix;
|
|
290
|
+
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
291
|
+
return await qb.query(
|
|
292
|
+
`select id from ${vecTableName} order by embedding <-> $1 LIMIT ${limit}`,
|
|
293
|
+
[pgvector.toSql(searchVec)]
|
|
294
|
+
);
|
|
295
|
+
} catch (err: any) {
|
|
296
|
+
logger.error(`Vector store search failed - ${err}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export async function vectorStoreSearchEntryExists(
|
|
301
|
+
tableName: string,
|
|
302
|
+
id: string,
|
|
303
|
+
ctx: DbContext
|
|
304
|
+
): Promise<boolean> {
|
|
305
|
+
try {
|
|
306
|
+
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
307
|
+
const vecTableName = tableName + VectorSuffix;
|
|
308
|
+
const result: any[] = await qb.query(`select id from ${vecTableName} where id = $1`, [id]);
|
|
309
|
+
return result != null && result.length > 0;
|
|
310
|
+
} catch (err: any) {
|
|
311
|
+
logger.error(`Vector store search failed - ${err}`);
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export async function deleteFullTextSearchEntry(tableName: string, id: string, ctx: DbContext) {
|
|
317
|
+
try {
|
|
318
|
+
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
319
|
+
const vecTableName = tableName + VectorSuffix;
|
|
320
|
+
await qb.query(`delete from ${vecTableName} where id = $1`, [id]);
|
|
321
|
+
} catch (err: any) {
|
|
322
|
+
logger.error(`Vector store delete failed - ${err}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function checkUserPerm(
|
|
327
|
+
opr: RbacPermissionFlag,
|
|
328
|
+
ctx: DbContext,
|
|
329
|
+
instRows: object
|
|
330
|
+
): Promise<boolean> {
|
|
331
|
+
let hasPerm = ctx.isPermitted();
|
|
332
|
+
if (!hasPerm) {
|
|
333
|
+
const userId = ctx.getUserId();
|
|
334
|
+
let f: Function | undefined;
|
|
335
|
+
switch (opr) {
|
|
336
|
+
case RbacPermissionFlag.CREATE:
|
|
337
|
+
f = canUserCreate;
|
|
338
|
+
break;
|
|
339
|
+
case RbacPermissionFlag.READ:
|
|
340
|
+
f = canUserRead;
|
|
341
|
+
break;
|
|
342
|
+
case RbacPermissionFlag.UPDATE:
|
|
343
|
+
f = canUserUpdate;
|
|
344
|
+
break;
|
|
345
|
+
case RbacPermissionFlag.DELETE:
|
|
346
|
+
f = canUserDelete;
|
|
347
|
+
break;
|
|
348
|
+
default:
|
|
349
|
+
f = undefined;
|
|
350
|
+
}
|
|
351
|
+
if (f != undefined) {
|
|
352
|
+
hasPerm = await f(userId, ctx.resourceFqName, ctx.activeEnv);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (!hasPerm) {
|
|
356
|
+
hasPerm = await isOwnerOfParent(instRows[PathKey], ctx);
|
|
357
|
+
}
|
|
358
|
+
return hasPerm;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function checkCreatePermission(ctx: DbContext, inst: Instance): Promise<boolean> {
|
|
362
|
+
const tmpCtx = ctx.clone().setResourceFqNameFrom(inst);
|
|
363
|
+
return await checkUserPerm(
|
|
364
|
+
RbacPermissionFlag.CREATE,
|
|
365
|
+
tmpCtx,
|
|
366
|
+
Object.fromEntries(inst.attributes)
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export async function insertRows(
|
|
371
|
+
tableName: string,
|
|
372
|
+
rows: object[],
|
|
373
|
+
ctx: DbContext,
|
|
374
|
+
doUpsert: boolean = false
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
let hasPerm = ctx.isPermitted();
|
|
377
|
+
if (!hasPerm) {
|
|
378
|
+
hasPerm = await checkUserPerm(RbacPermissionFlag.CREATE, ctx, rows[0]);
|
|
379
|
+
}
|
|
380
|
+
if (hasPerm) {
|
|
381
|
+
await insertRowsHelper(tableName, rows, ctx, doUpsert);
|
|
382
|
+
if (!ctx.isInKernelMode() && !doUpsert) {
|
|
383
|
+
await createOwnership(tableName, rows, ctx);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
throw new UnauthorisedError({ opr: 'insert', entity: tableName });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export async function insertRow(
|
|
391
|
+
tableName: string,
|
|
392
|
+
row: object,
|
|
393
|
+
ctx: DbContext,
|
|
394
|
+
doUpsert: boolean
|
|
395
|
+
): Promise<void> {
|
|
396
|
+
const rows: Array<object> = new Array<object>();
|
|
397
|
+
rows.push(row);
|
|
398
|
+
await insertRows(tableName, rows, ctx, doUpsert);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export async function insertBetweenRow(
|
|
402
|
+
n: string,
|
|
403
|
+
a1: string,
|
|
404
|
+
a2: string,
|
|
405
|
+
node1: Instance,
|
|
406
|
+
node2: Instance,
|
|
407
|
+
relEntry: Relationship,
|
|
408
|
+
ctx: DbContext
|
|
409
|
+
): Promise<void> {
|
|
410
|
+
let hasPerm = await checkCreatePermission(ctx, node1);
|
|
411
|
+
if (hasPerm) {
|
|
412
|
+
hasPerm = await checkCreatePermission(ctx, node2);
|
|
413
|
+
}
|
|
414
|
+
if (hasPerm) {
|
|
415
|
+
const attrs: InstanceAttributes = newInstanceAttributes();
|
|
416
|
+
const p1 = node1.attributes.get(PathAttributeName);
|
|
417
|
+
const p2 = node2.attributes.get(PathAttributeName);
|
|
418
|
+
attrs.set(a1, p1);
|
|
419
|
+
attrs.set(a2, p2);
|
|
420
|
+
attrs.set(PathAttributeName, crypto.randomUUID());
|
|
421
|
+
if (relEntry.isOneToMany()) {
|
|
422
|
+
attrs.set(relEntry.joinNodesAttributeName(), `${p1}_${p2}`);
|
|
423
|
+
}
|
|
424
|
+
const row = Object.fromEntries(attrs);
|
|
425
|
+
await insertRow(n, row, ctx.clone().setNeedAuthCheck(false), false);
|
|
426
|
+
} else {
|
|
427
|
+
throw new UnauthorisedError({ opr: 'insert', entity: n });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const PathKey = PathAttributeName as keyof object;
|
|
432
|
+
|
|
433
|
+
async function createOwnership(tableName: string, rows: object[], ctx: DbContext): Promise<void> {
|
|
434
|
+
const ownerRows: object[] = [];
|
|
435
|
+
rows.forEach((r: object) => {
|
|
436
|
+
ownerRows.push({
|
|
437
|
+
id: crypto.randomUUID(),
|
|
438
|
+
path: r[PathKey],
|
|
439
|
+
user_id: ctx.authInfo.userId,
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
const tname = ownersTable(tableName);
|
|
443
|
+
await insertRowsHelper(tname, ownerRows, ctx, false);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function isOwnerOfParent(path: string, ctx: DbContext): Promise<boolean> {
|
|
447
|
+
const parts = path.split('/');
|
|
448
|
+
if (parts.length <= 2) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
const parentPaths = new Array<[string, string]>();
|
|
452
|
+
let i = 0;
|
|
453
|
+
let lastPath: string | undefined;
|
|
454
|
+
while (i < parts.length - 2) {
|
|
455
|
+
const parentName = parts[i].replace('$', '_');
|
|
456
|
+
const parentPath = `${lastPath ? lastPath + '/' : ''}${parts[i]}/${parts[i + 1]}`;
|
|
457
|
+
lastPath = `${parentPath}/${parts[i + 2]}`;
|
|
458
|
+
parentPaths.push([parentName, parentPath]);
|
|
459
|
+
i += 3;
|
|
460
|
+
}
|
|
461
|
+
if (parentPaths.length == 0) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
for (let i = 0; i < parentPaths.length; ++i) {
|
|
465
|
+
const [parentName, parentPath] = parentPaths[i];
|
|
466
|
+
const result = await isOwner(parentName, parentPath, ctx);
|
|
467
|
+
if (result) return result;
|
|
468
|
+
}
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function isOwner(parentName: string, instPath: string, ctx: DbContext): Promise<boolean> {
|
|
473
|
+
const userId = ctx.getUserId();
|
|
474
|
+
const tabName = ownersTable(parentName);
|
|
475
|
+
const alias = tabName;
|
|
476
|
+
const query = [
|
|
477
|
+
`${alias}.path = '${instPath}'`,
|
|
478
|
+
`${alias}.user_id = '${userId}'`,
|
|
479
|
+
`${alias}.type = 'o'`,
|
|
480
|
+
];
|
|
481
|
+
let result: any = undefined;
|
|
482
|
+
const sq: SelectQueryBuilder<any> = getDatasourceForTransaction(ctx.txnId)
|
|
483
|
+
.createQueryBuilder()
|
|
484
|
+
.select()
|
|
485
|
+
.from(tabName, alias)
|
|
486
|
+
.where(query.join(' AND '));
|
|
487
|
+
try {
|
|
488
|
+
await sq.getRawMany().then((r: any) => (result = r));
|
|
489
|
+
} catch (reason: any) {
|
|
490
|
+
logger.error(`Failed to check ownership on parent ${parentName} - ${reason}`);
|
|
491
|
+
}
|
|
492
|
+
if (result == undefined || result.length == 0) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export async function upsertRows(tableName: string, rows: object[], ctx: DbContext): Promise<void> {
|
|
499
|
+
await insertRows(tableName, rows, ctx, true);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export async function upsertRow(tableName: string, row: object, ctx: DbContext): Promise<void> {
|
|
503
|
+
const rows: Array<object> = new Array<object>();
|
|
504
|
+
rows.push(row);
|
|
505
|
+
await upsertRows(tableName, rows, ctx);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export async function updateRow(
|
|
509
|
+
tableName: string,
|
|
510
|
+
queryObj: object,
|
|
511
|
+
queryVals: object,
|
|
512
|
+
updateObj: object,
|
|
513
|
+
ctx: DbContext
|
|
514
|
+
): Promise<boolean> {
|
|
515
|
+
await getDatasourceForTransaction(ctx.txnId)
|
|
516
|
+
.createQueryBuilder()
|
|
517
|
+
.update(tableName)
|
|
518
|
+
.set(updateObj)
|
|
519
|
+
.where(objectToWhereClause(queryObj, queryVals), queryVals)
|
|
520
|
+
.execute();
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
type QueryObjectEntry = [string, any];
|
|
525
|
+
export type QueryObject = Array<QueryObjectEntry>;
|
|
526
|
+
|
|
527
|
+
function queryObjectAsWhereClause(qobj: QueryObject): string {
|
|
528
|
+
const ss: Array<string> = [];
|
|
529
|
+
qobj.forEach((kv: QueryObjectEntry) => {
|
|
530
|
+
const k = kv[0];
|
|
531
|
+
ss.push(`${k} = :${k}`);
|
|
532
|
+
});
|
|
533
|
+
return ss.join(' AND ');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export async function hardDeleteRow(tableName: string, queryObject: QueryObject, ctx: DbContext) {
|
|
537
|
+
const clause = queryObjectAsWhereClause(queryObject);
|
|
538
|
+
await getDatasourceForTransaction(ctx.txnId)
|
|
539
|
+
.createQueryBuilder()
|
|
540
|
+
.delete()
|
|
541
|
+
.from(tableName)
|
|
542
|
+
.where(clause, Object.fromEntries(queryObject))
|
|
543
|
+
.execute();
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function mkBetweenClause(tableName: string | undefined, k: string, queryVals: any): string {
|
|
548
|
+
const ov = queryVals[k];
|
|
549
|
+
if (ov instanceof Array) {
|
|
550
|
+
const isstr = isString(ov[0]);
|
|
551
|
+
const v1 = isstr ? `'${ov[0]}'` : ov[0];
|
|
552
|
+
const v2 = isstr ? `'${ov[1]}'` : ov[1];
|
|
553
|
+
const s = tableName
|
|
554
|
+
? `${tableName}.${k} BETWEEN ${v1} AND ${v2}`
|
|
555
|
+
: `${k} BETWEEN ${v1} AND ${v2}`;
|
|
556
|
+
delete queryVals[k];
|
|
557
|
+
return s;
|
|
558
|
+
} else {
|
|
559
|
+
throw new Error(`between requires an array argument, not ${ov}`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function objectToWhereClause(queryObj: object, queryVals: any, tableName?: string): string {
|
|
564
|
+
const clauses: Array<string> = new Array<string>();
|
|
565
|
+
Object.entries(queryObj).forEach((value: [string, any]) => {
|
|
566
|
+
const op: string = value[1] as string;
|
|
567
|
+
const clause =
|
|
568
|
+
op == 'between'
|
|
569
|
+
? mkBetweenClause(tableName, value[0], queryVals)
|
|
570
|
+
: tableName
|
|
571
|
+
? `${tableName}.${value[0]} ${op} :${value[0]}`
|
|
572
|
+
: `${value[0]} ${op} :${value[0]}`;
|
|
573
|
+
clauses.push(clause);
|
|
574
|
+
});
|
|
575
|
+
return clauses.join(' AND ');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function objectToRawWhereClause(queryObj: object, queryVals: any, tableName?: string): string {
|
|
579
|
+
const clauses: Array<string> = new Array<string>();
|
|
580
|
+
Object.entries(queryObj).forEach((value: [string, any]) => {
|
|
581
|
+
const op: string = value[1] as string;
|
|
582
|
+
const k: string = value[0];
|
|
583
|
+
let clause = '';
|
|
584
|
+
if (op == 'between') {
|
|
585
|
+
clause = mkBetweenClause(tableName, k, queryVals);
|
|
586
|
+
} else {
|
|
587
|
+
const ov: any = queryVals[k];
|
|
588
|
+
const v = isString(ov) ? `'${ov}'` : ov;
|
|
589
|
+
clause = tableName ? `${tableName}.${k} ${op} ${v}` : `${k} ${op} ${v}`;
|
|
590
|
+
}
|
|
591
|
+
clauses.push(clause);
|
|
592
|
+
});
|
|
593
|
+
if (clauses.length > 0) {
|
|
594
|
+
return clauses.join(' AND ');
|
|
595
|
+
} else {
|
|
596
|
+
return '';
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export async function getMany(
|
|
601
|
+
tableName: string,
|
|
602
|
+
queryObj: object | undefined,
|
|
603
|
+
queryVals: object | undefined,
|
|
604
|
+
ctx: DbContext
|
|
605
|
+
): Promise<any> {
|
|
606
|
+
const alias: string = tableName.toLowerCase();
|
|
607
|
+
const queryStr: string = withNotDeletedClause(
|
|
608
|
+
alias,
|
|
609
|
+
queryObj != undefined ? objectToWhereClause(queryObj, queryVals, alias) : ''
|
|
610
|
+
);
|
|
611
|
+
let ownersJoinCond: string[] | undefined;
|
|
612
|
+
let ot: string = '';
|
|
613
|
+
let otAlias: string = '';
|
|
614
|
+
if (!ctx.isPermitted()) {
|
|
615
|
+
const userId = ctx.getUserId();
|
|
616
|
+
const fqName = ctx.resourceFqName;
|
|
617
|
+
const env: Environment = ctx.activeEnv;
|
|
618
|
+
let hasGlobalPerms = await canUserRead(userId, fqName, env);
|
|
619
|
+
if (hasGlobalPerms) {
|
|
620
|
+
if (ctx.isForUpdate()) {
|
|
621
|
+
hasGlobalPerms = await canUserUpdate(userId, fqName, env);
|
|
622
|
+
} else if (ctx.isForDelete()) {
|
|
623
|
+
hasGlobalPerms = await canUserDelete(userId, fqName, env);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (!hasGlobalPerms) {
|
|
627
|
+
ot = ownersTable(tableName);
|
|
628
|
+
otAlias = ot.toLowerCase();
|
|
629
|
+
ownersJoinCond = [
|
|
630
|
+
`${otAlias}.path = ${alias}.${PathAttributeName}`,
|
|
631
|
+
`${otAlias}.user_id = '${ctx.authInfo.userId}'`,
|
|
632
|
+
`${otAlias}.r = true`,
|
|
633
|
+
];
|
|
634
|
+
if (ctx.isForUpdate()) {
|
|
635
|
+
ownersJoinCond.push(`${otAlias}.u = true`);
|
|
636
|
+
}
|
|
637
|
+
if (ctx.isForDelete()) {
|
|
638
|
+
ownersJoinCond.push(`${otAlias}.d = true`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const qb: SelectQueryBuilder<any> = getDatasourceForTransaction(ctx.txnId)
|
|
644
|
+
.getRepository(tableName)
|
|
645
|
+
.createQueryBuilder();
|
|
646
|
+
if (ownersJoinCond) {
|
|
647
|
+
qb.innerJoin(ot, otAlias, ownersJoinCond.join(' AND '));
|
|
648
|
+
}
|
|
649
|
+
qb.where(queryStr, queryVals);
|
|
650
|
+
return await qb.getMany();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
export async function getManyByJoin(
|
|
654
|
+
tableName: string,
|
|
655
|
+
queryObj: object | undefined,
|
|
656
|
+
queryVals: object | undefined,
|
|
657
|
+
joinClauses: JoinClause[],
|
|
658
|
+
intoSpec: Map<string, string>,
|
|
659
|
+
ctx: DbContext
|
|
660
|
+
): Promise<any> {
|
|
661
|
+
const alias: string = tableName.toLowerCase();
|
|
662
|
+
const queryStr: string = withNotDeletedClause(
|
|
663
|
+
alias,
|
|
664
|
+
queryObj != undefined ? objectToRawWhereClause(queryObj, queryVals, alias) : ''
|
|
665
|
+
);
|
|
666
|
+
let ot: string = '';
|
|
667
|
+
let otAlias: string = '';
|
|
668
|
+
if (!ctx.isPermitted()) {
|
|
669
|
+
const userId = ctx.getUserId();
|
|
670
|
+
const fqName = ctx.resourceFqName;
|
|
671
|
+
const env: Environment = ctx.activeEnv;
|
|
672
|
+
const hasGlobalPerms = await canUserRead(userId, fqName, env);
|
|
673
|
+
if (!hasGlobalPerms) {
|
|
674
|
+
ot = ownersTable(tableName);
|
|
675
|
+
otAlias = ot.toLowerCase();
|
|
676
|
+
joinClauses.push({
|
|
677
|
+
tableName: otAlias,
|
|
678
|
+
joinOn: [
|
|
679
|
+
makeJoinOn(`${otAlias}.path`, `${alias}.${PathAttributeName}`),
|
|
680
|
+
makeJoinOn(`${otAlias}.user_id`, `'${ctx.authInfo.userId}'`),
|
|
681
|
+
makeJoinOn(`${otAlias}.r`, true),
|
|
682
|
+
],
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const joinSql = new Array<string>();
|
|
687
|
+
joinClauses.forEach((jc: JoinClause) => {
|
|
688
|
+
joinSql.push(
|
|
689
|
+
`inner join ${jc.tableName} as ${jc.tableName} on ${joinOnAsSql(jc.joinOn)} AND ${jc.tableName}.${DeletedFlagAttributeName} = false`
|
|
690
|
+
);
|
|
691
|
+
if (jc.queryObject) {
|
|
692
|
+
const q = objectToRawWhereClause(jc.queryObject, jc.queryValues, jc.tableName);
|
|
693
|
+
if (q.length > 0) {
|
|
694
|
+
joinSql.push(` AND ${q}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
const sql = `SELECT ${intoSpecToSql(intoSpec)} FROM ${tableName} ${joinSql.join('\n')} WHERE ${queryStr}`;
|
|
699
|
+
logger.debug(`Join Query: ${sql}`);
|
|
700
|
+
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
701
|
+
return await qb.query(sql);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function intoSpecToSql(intoSpec: Map<string, string>): string {
|
|
705
|
+
const cols = new Array<string>();
|
|
706
|
+
intoSpec.forEach((v: string, k: string) => {
|
|
707
|
+
cols.push(`${v} AS ${k}`);
|
|
708
|
+
});
|
|
709
|
+
return cols.join(', ');
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function joinOnAsSql(joinOn: JoinOn | JoinOn[]): string {
|
|
713
|
+
if (joinOn instanceof Array) {
|
|
714
|
+
return joinOn.map(joinOnAsSql).join(' AND ');
|
|
715
|
+
} else {
|
|
716
|
+
return `${joinOn.attributeName} ${joinOn.operator} ${joinOn.attributeValue}`;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function notDeletedClause(alias: string): string {
|
|
721
|
+
return `${alias}.${DeletedFlagAttributeName} = false`;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function withNotDeletedClause(alias: string, sql: string): string {
|
|
725
|
+
if (sql == '') {
|
|
726
|
+
return notDeletedClause(alias);
|
|
727
|
+
} else {
|
|
728
|
+
return `${sql} AND ${notDeletedClause(alias)}`;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export type BetweenConnectionInfo = {
|
|
733
|
+
connectionTable: string;
|
|
734
|
+
fromColumn: string;
|
|
735
|
+
fromValue: string;
|
|
736
|
+
toColumn: string;
|
|
737
|
+
toRef: string;
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
function buildQueryFromConnnectionInfo(
|
|
741
|
+
connAlias: string,
|
|
742
|
+
mainAlias: string,
|
|
743
|
+
connInfo: BetweenConnectionInfo
|
|
744
|
+
): string {
|
|
745
|
+
return `${connAlias}.${connInfo.fromColumn} = ${connInfo.fromValue} AND ${connAlias}.${connInfo.toColumn} = ${mainAlias}.${connInfo.toRef}`;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export async function getAllConnected(
|
|
749
|
+
tableName: string,
|
|
750
|
+
queryObj: object,
|
|
751
|
+
queryVals: object,
|
|
752
|
+
connInfo: BetweenConnectionInfo,
|
|
753
|
+
ctx: DbContext
|
|
754
|
+
) {
|
|
755
|
+
const alias: string = tableName.toLowerCase();
|
|
756
|
+
const connAlias: string = connInfo.connectionTable.toLowerCase();
|
|
757
|
+
const qb = getDatasourceForTransaction(ctx.txnId)
|
|
758
|
+
.createQueryBuilder()
|
|
759
|
+
.select()
|
|
760
|
+
.from(tableName, alias)
|
|
761
|
+
.where(objectToWhereClause(queryObj, alias), queryVals)
|
|
762
|
+
.innerJoin(
|
|
763
|
+
connInfo.connectionTable,
|
|
764
|
+
connAlias,
|
|
765
|
+
buildQueryFromConnnectionInfo(connAlias, alias, connInfo)
|
|
766
|
+
);
|
|
767
|
+
return await qb.getRawMany();
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const transactionsDb: Map<string, QueryRunner> = new Map<string, QueryRunner>();
|
|
771
|
+
|
|
772
|
+
export async function startDbTransaction(): Promise<string> {
|
|
773
|
+
if (defaultDataSource != undefined) {
|
|
774
|
+
const queryRunner = defaultDataSource.createQueryRunner();
|
|
775
|
+
await queryRunner.startTransaction();
|
|
776
|
+
const txnId: string = crypto.randomUUID();
|
|
777
|
+
transactionsDb.set(txnId, queryRunner);
|
|
778
|
+
return txnId;
|
|
779
|
+
} else {
|
|
780
|
+
throw new Error('Database not initialized');
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function getDatasourceForTransaction(txnId: string | undefined): DataSource | EntityManager {
|
|
785
|
+
if (txnId) {
|
|
786
|
+
const qr: QueryRunner | undefined = transactionsDb.get(txnId);
|
|
787
|
+
if (qr == undefined) {
|
|
788
|
+
throw new Error(`Transaction not found - ${txnId}`);
|
|
789
|
+
} else {
|
|
790
|
+
return qr.manager;
|
|
791
|
+
}
|
|
792
|
+
} else {
|
|
793
|
+
if (defaultDataSource != undefined) return defaultDataSource;
|
|
794
|
+
else throw new Error('No default datasource is initialized');
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export async function commitDbTransaction(txnId: string): Promise<void> {
|
|
799
|
+
await endTransaction(txnId, true);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
export async function rollbackDbTransaction(txnId: string): Promise<void> {
|
|
803
|
+
await endTransaction(txnId, false);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
async function endTransaction(txnId: string, commit: boolean): Promise<void> {
|
|
807
|
+
const qr: QueryRunner | undefined = transactionsDb.get(txnId);
|
|
808
|
+
if (qr && qr.isTransactionActive) {
|
|
809
|
+
try {
|
|
810
|
+
if (commit)
|
|
811
|
+
await qr.commitTransaction().catch((reason: any) => {
|
|
812
|
+
logger.error(`failed to commit transaction ${txnId} - ${reason}`);
|
|
813
|
+
});
|
|
814
|
+
else
|
|
815
|
+
await qr.rollbackTransaction().catch((reason: any) => {
|
|
816
|
+
logger.error(`failed to rollback transaction ${txnId} - ${reason}`);
|
|
817
|
+
});
|
|
818
|
+
} finally {
|
|
819
|
+
await qr.release();
|
|
820
|
+
transactionsDb.delete(txnId);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|