@entity-access/entity-access 1.0.1
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/.eslintrc.cjs +234 -0
- package/.github/workflows/node.yml +44 -0
- package/.github/workflows/npm-publish.yml +33 -0
- package/.vscode/launch.json +20 -0
- package/.vscode/settings.json +39 -0
- package/LICENSE +201 -0
- package/README.md +247 -0
- package/package.json +39 -0
- package/src/TestRunner.ts +2 -0
- package/src/common/cache/InstanceCache.ts +14 -0
- package/src/common/cache/TimedCache.ts +74 -0
- package/src/common/symbols/symbols.ts +2 -0
- package/src/common/usingAsync.ts +24 -0
- package/src/compiler/ISqlHelpers.ts +30 -0
- package/src/compiler/QueryCompiler.ts +88 -0
- package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +83 -0
- package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +83 -0
- package/src/decorators/Column.ts +46 -0
- package/src/decorators/ForeignKey.ts +42 -0
- package/src/decorators/IClassOf.ts +1 -0
- package/src/decorators/IColumn.ts +70 -0
- package/src/decorators/ISqlType.ts +70 -0
- package/src/decorators/SchemaRegistry.ts +19 -0
- package/src/decorators/Table.ts +18 -0
- package/src/decorators/parser/MemberParser.ts +8 -0
- package/src/drivers/base/BaseDriver.ts +134 -0
- package/src/drivers/postgres/PostgreSqlDriver.ts +178 -0
- package/src/drivers/sql-server/ExpressionToSqlServer.ts +78 -0
- package/src/drivers/sql-server/SqlServerDriver.ts +215 -0
- package/src/drivers/sql-server/SqlServerLiteral.ts +12 -0
- package/src/drivers/sql-server/SqlServerQueryCompiler.ts +25 -0
- package/src/entity-query/EntityType.ts +116 -0
- package/src/migrations/Migrations.ts +17 -0
- package/src/migrations/postgres/PostgresAutomaticMigrations.ts +97 -0
- package/src/migrations/postgres/PostgresMigrations.ts +56 -0
- package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +102 -0
- package/src/migrations/sql-server/SqlServerMigrations.ts +60 -0
- package/src/model/ChangeEntry.ts +154 -0
- package/src/model/ChangeSet.ts +88 -0
- package/src/model/EntityContext.ts +48 -0
- package/src/model/EntityModel.ts +27 -0
- package/src/model/EntityQuery.ts +152 -0
- package/src/model/EntitySchema.ts +21 -0
- package/src/model/EntitySource.ts +98 -0
- package/src/model/IFilterWithParameter.ts +38 -0
- package/src/model/IdentityService.ts +23 -0
- package/src/model/SourceExpression.ts +137 -0
- package/src/query/Query.ts +158 -0
- package/src/query/ast/ExpressionToSql.ts +348 -0
- package/src/query/ast/Expressions.ts +294 -0
- package/src/query/ast/IStringTransformer.ts +74 -0
- package/src/query/ast/SqlLiteral.ts +25 -0
- package/src/query/ast/Visitor.ts +159 -0
- package/src/query/parser/ArrowToExpression.ts +160 -0
- package/src/query/parser/BabelVisitor.ts +86 -0
- package/src/sql/ISql.ts +31 -0
- package/src/sql/Sql.ts +4 -0
- package/src/tests/TestConfig.ts +6 -0
- package/src/tests/db-tests/tests/select-items.ts +37 -0
- package/src/tests/db-tests/tests/update-items.ts +17 -0
- package/src/tests/drivers/postgres/connection-test.ts +29 -0
- package/src/tests/drivers/sql-server/sql-server-test.ts +9 -0
- package/src/tests/expressions/left-joins/child-joins.ts +71 -0
- package/src/tests/expressions/simple/parse-arrow.ts +46 -0
- package/src/tests/expressions/trimInternal.ts +23 -0
- package/src/tests/model/ShoppingContext.ts +203 -0
- package/src/tests/model/createContext.ts +294 -0
- package/src/tests/query/combine.ts +28 -0
- package/test.js +107 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { IClassOf } from "../../decorators/IClassOf.js";
|
|
2
|
+
import { BaseDriver } from "../../drivers/base/BaseDriver.js";
|
|
3
|
+
import { ShoppingContext } from "./ShoppingContext.js";
|
|
4
|
+
|
|
5
|
+
export async function createContext(driver: BaseDriver) {
|
|
6
|
+
|
|
7
|
+
const rn = "d" + Date.now();
|
|
8
|
+
const copy = { ... driver } as BaseDriver;
|
|
9
|
+
(copy as any).connectionString = { ... driver.connectionString };
|
|
10
|
+
copy.connectionString.database = rn;
|
|
11
|
+
Object.setPrototypeOf(copy, Object.getPrototypeOf(driver));
|
|
12
|
+
const context = new ShoppingContext(copy);
|
|
13
|
+
|
|
14
|
+
await context.driver.ensureDatabase();
|
|
15
|
+
|
|
16
|
+
await context.driver.automaticMigrations().migrate(context);
|
|
17
|
+
|
|
18
|
+
await seed(context);
|
|
19
|
+
|
|
20
|
+
return context;
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// export default function () {
|
|
25
|
+
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
async function seed(context: ShoppingContext) {
|
|
29
|
+
const now = new Date();
|
|
30
|
+
|
|
31
|
+
addHeadPhones(context, now);
|
|
32
|
+
|
|
33
|
+
const maleClothes = addMaleClothes(context);
|
|
34
|
+
|
|
35
|
+
const femaleClothes = addFemaleClothes(context);
|
|
36
|
+
|
|
37
|
+
await context.saveChanges();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function addFemaleClothes(context: ShoppingContext) {
|
|
41
|
+
const category = context.categories.add({
|
|
42
|
+
name: "Female Clothes",
|
|
43
|
+
categoryID: "clothes/female"
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const startDate = new Date();
|
|
47
|
+
const active = true;
|
|
48
|
+
|
|
49
|
+
context.products.add({
|
|
50
|
+
name: "White T-Shirt",
|
|
51
|
+
categories: [
|
|
52
|
+
context.productCategories.add({
|
|
53
|
+
category
|
|
54
|
+
})
|
|
55
|
+
],
|
|
56
|
+
prices: [
|
|
57
|
+
context.productPrices.add({
|
|
58
|
+
amount: 20,
|
|
59
|
+
active,
|
|
60
|
+
startDate
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
context.products.add({
|
|
66
|
+
name: "Red T-Shirt",
|
|
67
|
+
categories: [
|
|
68
|
+
context.productCategories.add({
|
|
69
|
+
category
|
|
70
|
+
})
|
|
71
|
+
],
|
|
72
|
+
prices: [
|
|
73
|
+
context.productPrices.add({
|
|
74
|
+
amount: 20,
|
|
75
|
+
active,
|
|
76
|
+
startDate
|
|
77
|
+
})
|
|
78
|
+
]
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
context.products.add({
|
|
82
|
+
name: "Blue T-Shirt",
|
|
83
|
+
categories: [
|
|
84
|
+
context.productCategories.add({
|
|
85
|
+
category
|
|
86
|
+
})
|
|
87
|
+
],
|
|
88
|
+
prices: [
|
|
89
|
+
context.productPrices.add({
|
|
90
|
+
amount: 20,
|
|
91
|
+
active,
|
|
92
|
+
startDate
|
|
93
|
+
})
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
context.products.add({
|
|
98
|
+
name: "Pink T-Shirt",
|
|
99
|
+
categories: [
|
|
100
|
+
context.productCategories.add({
|
|
101
|
+
category
|
|
102
|
+
})
|
|
103
|
+
],
|
|
104
|
+
prices: [
|
|
105
|
+
context.productPrices.add({
|
|
106
|
+
amount: 20,
|
|
107
|
+
active,
|
|
108
|
+
startDate
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function addMaleClothes(context: ShoppingContext) {
|
|
115
|
+
const category = context.categories.add({
|
|
116
|
+
name: "Male Clothes",
|
|
117
|
+
categoryID: "clothes/male"
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const startDate = new Date();
|
|
121
|
+
const active = true;
|
|
122
|
+
|
|
123
|
+
context.products.add({
|
|
124
|
+
name: "White T-Shirt",
|
|
125
|
+
categories: [
|
|
126
|
+
context.productCategories.add({
|
|
127
|
+
category
|
|
128
|
+
})
|
|
129
|
+
],
|
|
130
|
+
prices: [
|
|
131
|
+
context.productPrices.add({
|
|
132
|
+
amount: 20,
|
|
133
|
+
active,
|
|
134
|
+
startDate
|
|
135
|
+
})
|
|
136
|
+
]
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
context.products.add({
|
|
140
|
+
name: "Red T-Shirt",
|
|
141
|
+
categories: [
|
|
142
|
+
context.productCategories.add({
|
|
143
|
+
category
|
|
144
|
+
})
|
|
145
|
+
],
|
|
146
|
+
prices: [
|
|
147
|
+
context.productPrices.add({
|
|
148
|
+
amount: 20,
|
|
149
|
+
active,
|
|
150
|
+
startDate
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
context.products.add({
|
|
156
|
+
name: "Blue T-Shirt",
|
|
157
|
+
categories: [
|
|
158
|
+
context.productCategories.add({
|
|
159
|
+
category
|
|
160
|
+
})
|
|
161
|
+
],
|
|
162
|
+
prices: [
|
|
163
|
+
context.productPrices.add({
|
|
164
|
+
amount: 20,
|
|
165
|
+
active,
|
|
166
|
+
startDate
|
|
167
|
+
})
|
|
168
|
+
]
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
context.products.add({
|
|
172
|
+
name: "Pink T-Shirt",
|
|
173
|
+
categories: [
|
|
174
|
+
context.productCategories.add({
|
|
175
|
+
category
|
|
176
|
+
})
|
|
177
|
+
],
|
|
178
|
+
prices: [
|
|
179
|
+
context.productPrices.add({
|
|
180
|
+
amount: 20,
|
|
181
|
+
active,
|
|
182
|
+
startDate
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const headPhoneCategory = "head-phones";
|
|
189
|
+
function addHeadPhones(context: ShoppingContext, now: Date) {
|
|
190
|
+
const category = context.categories.add({
|
|
191
|
+
name: "Headphones",
|
|
192
|
+
categoryID: headPhoneCategory
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const startDate = new Date();
|
|
196
|
+
const active = true;
|
|
197
|
+
|
|
198
|
+
context.products.add({
|
|
199
|
+
name: "Jabber Head Phones",
|
|
200
|
+
categories: [
|
|
201
|
+
context.productCategories.add({
|
|
202
|
+
category
|
|
203
|
+
})
|
|
204
|
+
],
|
|
205
|
+
prices: [
|
|
206
|
+
context.productPrices.add({
|
|
207
|
+
active,
|
|
208
|
+
startDate,
|
|
209
|
+
amount: 100
|
|
210
|
+
})
|
|
211
|
+
]
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
context.products.add({
|
|
215
|
+
name: "Sony Head Phones",
|
|
216
|
+
categories: [
|
|
217
|
+
context.productCategories.add({
|
|
218
|
+
category
|
|
219
|
+
})
|
|
220
|
+
],
|
|
221
|
+
prices: [
|
|
222
|
+
context.productPrices.add({
|
|
223
|
+
active,
|
|
224
|
+
startDate,
|
|
225
|
+
amount: 120
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
context.products.add({
|
|
231
|
+
name: "Sony Head Phones Black",
|
|
232
|
+
categories: [
|
|
233
|
+
context.productCategories.add({
|
|
234
|
+
category
|
|
235
|
+
})
|
|
236
|
+
],
|
|
237
|
+
prices: [
|
|
238
|
+
context.productPrices.add({
|
|
239
|
+
active,
|
|
240
|
+
startDate,
|
|
241
|
+
amount: 140
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
context.products.add({
|
|
247
|
+
name: "Sony Head Phones Blue",
|
|
248
|
+
categories: [
|
|
249
|
+
context.productCategories.add({
|
|
250
|
+
category
|
|
251
|
+
})
|
|
252
|
+
],
|
|
253
|
+
prices: [
|
|
254
|
+
context.productPrices.add({
|
|
255
|
+
active,
|
|
256
|
+
startDate,
|
|
257
|
+
amount: 140
|
|
258
|
+
})
|
|
259
|
+
]
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
context.products.add({
|
|
263
|
+
name: "Jabber Head Phones Black",
|
|
264
|
+
categories: [
|
|
265
|
+
context.productCategories.add({
|
|
266
|
+
category
|
|
267
|
+
})
|
|
268
|
+
],
|
|
269
|
+
prices: [
|
|
270
|
+
context.productPrices.add({
|
|
271
|
+
active,
|
|
272
|
+
startDate,
|
|
273
|
+
amount: 140
|
|
274
|
+
})
|
|
275
|
+
]
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
context.products.add({
|
|
279
|
+
name: "Jabber Head Phones Blue",
|
|
280
|
+
categories: [
|
|
281
|
+
context.productCategories.add({
|
|
282
|
+
category
|
|
283
|
+
})
|
|
284
|
+
],
|
|
285
|
+
prices: [
|
|
286
|
+
context.productPrices.add({
|
|
287
|
+
active,
|
|
288
|
+
startDate,
|
|
289
|
+
amount: 140
|
|
290
|
+
})
|
|
291
|
+
]
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as assert from "assert";
|
|
2
|
+
import { IQuery, Query, QueryPart } from "../../query/Query.js";
|
|
3
|
+
|
|
4
|
+
export default function () {
|
|
5
|
+
const id = 1;
|
|
6
|
+
const q = Query.create `SELECT * FROM Accounts WHERE ${id}`;
|
|
7
|
+
assert.equal("SELECT * FROM Accounts WHERE @p0", q.toString());
|
|
8
|
+
|
|
9
|
+
const p = q.parts[1] as QueryPart;
|
|
10
|
+
assert.equal(id, p.value);
|
|
11
|
+
|
|
12
|
+
// lets combine multiple queries...
|
|
13
|
+
|
|
14
|
+
const id2 = 2;
|
|
15
|
+
|
|
16
|
+
const orConstraints = [];
|
|
17
|
+
orConstraints.push(Query.create `ID == ${id}`);
|
|
18
|
+
orConstraints.push(Query.create `ID == ${id2}`);
|
|
19
|
+
|
|
20
|
+
const final = Query.create `SELECT * FROM Accounts WHERE ${Query.join(orConstraints, " OR ")}`;
|
|
21
|
+
assert.equal("SELECT * FROM Accounts WHERE ID == @p0 OR ID == @p1", final.toString());
|
|
22
|
+
|
|
23
|
+
const rn = "D" + Date.now();
|
|
24
|
+
const create = Query.create `CREATE Database ${Query.literal(rn)}`;
|
|
25
|
+
assert.equal(`CREATE Database ${rn}`, create.toString());
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { readdir } from "fs/promises";
|
|
2
|
+
import PostgreSqlDriver from "./dist/drivers/postgres/PostgreSqlDriver.js";
|
|
3
|
+
import SqlServerDriver from "./dist/drivers/sql-server/SqlServerDriver.js";
|
|
4
|
+
import * as ports from "tcp-port-used";
|
|
5
|
+
|
|
6
|
+
const host = process.env.POSTGRES_HOST ?? "localhost";
|
|
7
|
+
const postGresPort = Number(process.env.POSTGRES_PORT ?? 5432);
|
|
8
|
+
|
|
9
|
+
// if (process.argv.includes("test-db")) {
|
|
10
|
+
// // wait for ports to open...
|
|
11
|
+
// console.log("Waiting for port to be open");
|
|
12
|
+
// await ports.waitUntilUsedOnHost(port, host, void 0, 15000);
|
|
13
|
+
// }
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @type Array<{ name: string, error: string }>
|
|
17
|
+
*/
|
|
18
|
+
const results = [];
|
|
19
|
+
|
|
20
|
+
let start = Date.now();
|
|
21
|
+
|
|
22
|
+
export default class TestRunner {
|
|
23
|
+
|
|
24
|
+
static get drivers() {
|
|
25
|
+
const database = "D" + start++;
|
|
26
|
+
return [
|
|
27
|
+
new PostgreSqlDriver({
|
|
28
|
+
database,
|
|
29
|
+
host,
|
|
30
|
+
user: "postgres",
|
|
31
|
+
password: "abcd123",
|
|
32
|
+
port: postGresPort
|
|
33
|
+
}),
|
|
34
|
+
new SqlServerDriver({
|
|
35
|
+
database,
|
|
36
|
+
host,
|
|
37
|
+
user: "sa",
|
|
38
|
+
password: "$EntityAccess2023",
|
|
39
|
+
port: 1433,
|
|
40
|
+
options: {
|
|
41
|
+
encrypt: true, // for azure
|
|
42
|
+
trustServerCertificate: true // change to true for local dev / self-signed certs
|
|
43
|
+
},
|
|
44
|
+
debug: true
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* @param {string} name
|
|
53
|
+
*/
|
|
54
|
+
static async runTest(name, thisParam) {
|
|
55
|
+
const moduleExports = await import(name);
|
|
56
|
+
const { default: d } = moduleExports;
|
|
57
|
+
if (!d) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
|
|
62
|
+
const r = d.call(thisParam);
|
|
63
|
+
if (r?.then) {
|
|
64
|
+
await r;
|
|
65
|
+
}
|
|
66
|
+
results.push({ name });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
results.unshift({ name, error });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static async runAll(dir, db) {
|
|
73
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
74
|
+
const tasks = [];
|
|
75
|
+
for (const iterator of items) {
|
|
76
|
+
const next = dir + "/" + iterator.name;
|
|
77
|
+
if (iterator.isDirectory()) {
|
|
78
|
+
tasks.push(this.runAll(next, db));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (iterator.name.endsWith(".js")) {
|
|
82
|
+
for (const driver of this.drivers) {
|
|
83
|
+
tasks.push(this.runTest(next, { driver, db }));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await Promise.all(tasks);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
await TestRunner.runAll("./dist/tests", true);
|
|
94
|
+
|
|
95
|
+
let exitCode = 0;
|
|
96
|
+
|
|
97
|
+
for (const { error, name } of results) {
|
|
98
|
+
if (error) {
|
|
99
|
+
exitCode = 1;
|
|
100
|
+
console.error(`${name} failed`);
|
|
101
|
+
console.error(error?.stack ?? error);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
console.log(`${name} executed.`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
process.exit(exitCode);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module":"NodeNext",
|
|
5
|
+
"sourceMap": true,
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"outDir": "dist",
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"emitDecoratorMetadata": true,
|
|
11
|
+
"lib": [
|
|
12
|
+
"ES2018"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*"
|
|
17
|
+
],
|
|
18
|
+
"exclude": [
|
|
19
|
+
"node_modules",
|
|
20
|
+
"tests"
|
|
21
|
+
]
|
|
22
|
+
}
|