@balena/odata-to-abstract-sql 8.0.2 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/select.ts DELETED
@@ -1,418 +0,0 @@
1
- import { expect } from 'chai';
2
- import {
3
- operandToAbstractSQLFactory,
4
- aliasFields,
5
- pilotFields,
6
- } from './chai-sql';
7
- import test, { itExpectsError } from './test';
8
- import _ from 'lodash';
9
-
10
- const operandToAbstractSQL = operandToAbstractSQLFactory();
11
-
12
- const pilotName = pilotFields.filter((field) => field[2] === 'name')[0];
13
- const pilotAge = pilotFields.filter((field) => field[2] === 'age')[0];
14
- test('/pilot?$select=name', (result) => {
15
- it('should select name from pilot', () => {
16
- expect(result).to.be.a.query.that.selects([pilotName]).from('pilot');
17
- });
18
- });
19
-
20
- test('/pilot?$select=p/name', (result) => {
21
- // TODO: This should fail
22
- it('should select name from pilot using a non-existing alias', () => {
23
- expect(result).to.be.a.query.that.selects([pilotName]).from('pilot');
24
- });
25
- });
26
-
27
- test('/pilot?$select=favourite_colour', (result) => {
28
- it('should select favourite_colour from pilot', () => {
29
- expect(result)
30
- .to.be.a.query.that.selects(
31
- _.filter(pilotFields, { 2: 'favourite_colour' }),
32
- )
33
- .from('pilot');
34
- });
35
- });
36
-
37
- test('/pilot(1)?$select=favourite_colour', (result) => {
38
- it('should select from pilot with id', () => {
39
- expect(result)
40
- .to.be.a.query.that.selects(
41
- _.filter(pilotFields, { 2: 'favourite_colour' }),
42
- )
43
- .from('pilot')
44
- .where([
45
- 'IsNotDistinctFrom',
46
- ['ReferencedField', 'pilot', 'id'],
47
- ['Bind', 0],
48
- ]);
49
- });
50
- });
51
-
52
- test("/pilot('TextKey')?$select=favourite_colour", (result) => {
53
- it('should select from pilot with id', () => {
54
- expect(result)
55
- .to.be.a.query.that.selects(
56
- _.filter(pilotFields, { 2: 'favourite_colour' }),
57
- )
58
- .from('pilot')
59
- .where([
60
- 'IsNotDistinctFrom',
61
- ['ReferencedField', 'pilot', 'id'],
62
- ['Bind', 0],
63
- ]);
64
- });
65
- });
66
-
67
- test('/pilot?$select=was_trained_by__pilot/name', (result) => {
68
- it('should select name from pilot', () => {
69
- expect(result)
70
- .to.be.a.query.that.selects(
71
- aliasFields('pilot', [pilotName], 'was trained by'),
72
- )
73
- .from('pilot')
74
- .leftJoin([
75
- ['pilot', 'pilot.was trained by-pilot'],
76
- [
77
- 'Equals',
78
- ['ReferencedField', 'pilot', 'was trained by-pilot'],
79
- ['ReferencedField', 'pilot.was trained by-pilot', 'id'],
80
- ],
81
- ])
82
- .where();
83
- });
84
- });
85
-
86
- test('/pilot?$select=p/was_trained_by__pilot/name', (result) => {
87
- // TODO: This should fail
88
- it('generates invalid select name from pilot query when using a non-existing alias', () => {
89
- expect(result)
90
- .to.be.a.query.that.selects(
91
- aliasFields('pilot', [pilotName], 'was trained by'),
92
- )
93
- .from('pilot')
94
- .where();
95
- });
96
- });
97
-
98
- test('/pilot?$select=was_trained_by__pilot/p/name', (result) => {
99
- // TODO: This should fail
100
- it('should select name from pilot when using an invalid path', () => {
101
- expect(result)
102
- .to.be.a.query.that.selects(
103
- aliasFields('pilot', [pilotName], 'was trained by'),
104
- )
105
- .from('pilot')
106
- .leftJoin([
107
- ['pilot', 'pilot.was trained by-pilot'],
108
- [
109
- 'Equals',
110
- ['ReferencedField', 'pilot', 'was trained by-pilot'],
111
- ['ReferencedField', 'pilot.was trained by-pilot', 'id'],
112
- ],
113
- ])
114
- .where();
115
- });
116
- });
117
-
118
- test('/pilot?$select=was_trained_by__pilot/p', (result) => {
119
- it('should fail to select from pilot when using an invalid path', () => {
120
- expect(result)
121
- .to.be.instanceOf(SyntaxError)
122
- .and.to.have.property(
123
- 'message',
124
- `Could not resolve relationship mapping from 'pilot' to 'p'`,
125
- );
126
- });
127
- });
128
-
129
- test('/pilot?$select=trained__pilot/name', (result) => {
130
- it('should select name from pilot', () => {
131
- expect(result)
132
- .to.be.a.query.that.selects(aliasFields('pilot', [pilotName], 'trained'))
133
- .from('pilot')
134
- .leftJoin([
135
- ['pilot', 'pilot.trained-pilot'],
136
- [
137
- 'Equals',
138
- ['ReferencedField', 'pilot', 'id'],
139
- ['ReferencedField', 'pilot.trained-pilot', 'was trained by-pilot'],
140
- ],
141
- ])
142
- .where();
143
- });
144
- });
145
-
146
- test('/pilot?$select=was_trained_by__pilot/name,trained__pilot/name', (result) => {
147
- it('should select name from pilot', () => {
148
- expect(result)
149
- .to.be.a.query.that.selects(
150
- aliasFields('pilot', [pilotName], 'was trained by').concat(
151
- aliasFields('pilot', [pilotName], 'trained'),
152
- ),
153
- )
154
- .from('pilot')
155
- .leftJoin(
156
- [
157
- ['pilot', 'pilot.was trained by-pilot'],
158
- [
159
- 'Equals',
160
- ['ReferencedField', 'pilot', 'was trained by-pilot'],
161
- ['ReferencedField', 'pilot.was trained by-pilot', 'id'],
162
- ],
163
- ],
164
- [
165
- ['pilot', 'pilot.trained-pilot'],
166
- [
167
- 'Equals',
168
- ['ReferencedField', 'pilot', 'id'],
169
- ['ReferencedField', 'pilot.trained-pilot', 'was trained by-pilot'],
170
- ],
171
- ],
172
- )
173
- .where();
174
- });
175
- });
176
-
177
- test('/pilot?$select=trained__pilot/name,age', (result) => {
178
- it('should select name, age from pilot', () => {
179
- expect(result)
180
- .to.be.a.query.that.selects(
181
- aliasFields('pilot', [pilotName], 'trained').concat([pilotAge]),
182
- )
183
- .from('pilot')
184
- .leftJoin([
185
- ['pilot', 'pilot.trained-pilot'],
186
- [
187
- 'Equals',
188
- ['ReferencedField', 'pilot', 'id'],
189
- ['ReferencedField', 'pilot.trained-pilot', 'was trained by-pilot'],
190
- ],
191
- ])
192
- .where();
193
- });
194
- });
195
-
196
- test('/pilot?$select=*', (result) => {
197
- it('should select * from pilot', () => {
198
- expect(result).to.be.a.query.that.selects(pilotFields).from('pilot');
199
- });
200
- });
201
-
202
- test('/pilot?$select=licence/id', (result) => {
203
- it('should select licence/id for pilots', () => {
204
- expect(result)
205
- .to.be.a.query.that.selects([operandToAbstractSQL('licence/id')])
206
- .from('pilot')
207
- .leftJoin([
208
- ['licence', 'pilot.licence'],
209
- [
210
- 'Equals',
211
- ['ReferencedField', 'pilot', 'licence'],
212
- ['ReferencedField', 'pilot.licence', 'id'],
213
- ],
214
- ])
215
- .where();
216
- });
217
- });
218
-
219
- test('/pilot?$select=can_fly__plane/plane/id', (result) => {
220
- it('should select can_fly__plane/plane/id for pilots', () => {
221
- expect(result)
222
- .to.be.a.query.that.selects([
223
- operandToAbstractSQL('can_fly__plane/plane/id'),
224
- ])
225
- .from('pilot')
226
- .leftJoin(
227
- [
228
- ['pilot-can fly-plane', 'pilot.pilot-can fly-plane'],
229
- [
230
- 'Equals',
231
- ['ReferencedField', 'pilot', 'id'],
232
- ['ReferencedField', 'pilot.pilot-can fly-plane', 'pilot'],
233
- ],
234
- ],
235
- [
236
- ['plane', 'pilot.pilot-can fly-plane.plane'],
237
- [
238
- 'Equals',
239
- ['ReferencedField', 'pilot.pilot-can fly-plane', 'can fly-plane'],
240
- ['ReferencedField', 'pilot.pilot-can fly-plane.plane', 'id'],
241
- ],
242
- ],
243
- )
244
- .where();
245
- });
246
- });
247
-
248
- // Skipping because the optimization for already computed fields had to be removed due to dangerous interactions with translations
249
- test.skip('/copilot?$select=*', (result) => {
250
- it('should select * from copilot', () => {
251
- expect(result).to.be.a.query.to.deep.equal([
252
- 'SelectQuery',
253
- [
254
- 'Select',
255
- [
256
- ['Alias', ['ReferencedField', 'copilot', 'created at'], 'created_at'],
257
- [
258
- 'Alias',
259
- ['ReferencedField', 'copilot', 'modified at'],
260
- 'modified_at',
261
- ],
262
- ['ReferencedField', 'copilot', 'id'],
263
- ['ReferencedField', 'copilot', 'pilot'],
264
- [
265
- 'Alias',
266
- ['ReferencedField', 'copilot', 'assists-pilot'],
267
- 'assists__pilot',
268
- ],
269
- ['Alias', ['ReferencedField', 'copilot', 'is blocked'], 'is_blocked'],
270
- ['ReferencedField', 'copilot', 'rank'],
271
- ],
272
- ],
273
- [
274
- 'From',
275
- [
276
- 'Alias',
277
- [
278
- 'SelectQuery',
279
- [
280
- 'Select',
281
- [
282
- ['Field', '*'],
283
- ['Alias', ['Boolean', false], 'is blocked'],
284
- ['Alias', ['Text', 'Junior'], 'rank'],
285
- ],
286
- ],
287
- ['From', ['Table', 'copilot']],
288
- ],
289
- 'copilot',
290
- ],
291
- ],
292
- ]);
293
- });
294
- });
295
-
296
- // Skipping because the optimization for already computed fields had to be removed due to dangerous interactions with translations
297
- test.skip('/copilot?$select=id,is_blocked,rank', (result) => {
298
- it('should select * from copilot', () => {
299
- expect(result).to.be.a.query.to.deep.equal([
300
- 'SelectQuery',
301
- [
302
- 'Select',
303
- [
304
- ['ReferencedField', 'copilot', 'id'],
305
- ['Alias', ['ReferencedField', 'copilot', 'is blocked'], 'is_blocked'],
306
- ['ReferencedField', 'copilot', 'rank'],
307
- ],
308
- ],
309
- [
310
- 'From',
311
- [
312
- 'Alias',
313
- [
314
- 'SelectQuery',
315
- [
316
- 'Select',
317
- [
318
- ['Field', '*'],
319
- ['Alias', ['Boolean', false], 'is blocked'],
320
- ['Alias', ['Text', 'Junior'], 'rank'],
321
- ],
322
- ],
323
- ['From', ['Table', 'copilot']],
324
- ],
325
- 'copilot',
326
- ],
327
- ],
328
- ]);
329
- });
330
- });
331
-
332
- test(`/pilot?$select=name,identification_method(identification_type='passport')/identification_number`, (result) => {
333
- it(`should select the pilot's name & passport number when using part of the alternate key and infering the rest from the navigation`, () => {
334
- expect(result)
335
- .to.be.a.query.that.selects([
336
- operandToAbstractSQL('name'),
337
- [
338
- 'Alias',
339
- [
340
- 'ReferencedField',
341
- 'pilot.identification method',
342
- 'identification number',
343
- ],
344
- 'identification_number',
345
- ],
346
- ])
347
- .from('pilot')
348
- .leftJoin([
349
- ['identification method', 'pilot.identification method'],
350
- [
351
- 'And',
352
- [
353
- 'Equals',
354
- ['ReferencedField', 'pilot', 'id'],
355
- ['ReferencedField', 'pilot.identification method', 'pilot'],
356
- ],
357
- [
358
- 'IsNotDistinctFrom',
359
- [
360
- 'ReferencedField',
361
- 'pilot.identification method',
362
- 'identification type',
363
- ],
364
- ['Bind', 0],
365
- ],
366
- ],
367
- ]);
368
- });
369
- });
370
-
371
- test(`/pilot?$select=name,identification_method(identification_type='passport')/identification_number,identification_method(identification_type='passport')/created_at`, (result) => {
372
- // TODO: This atm doens't work b/c the two JOINs are generated with differnet Bind numbers
373
- itExpectsError(
374
- `should select the pilot's name, passport number & passport creation date when using part of the alternate key and infering the rest from the navigation`,
375
- () => {
376
- expect(result)
377
- .to.be.a.query.that.selects([
378
- operandToAbstractSQL('name'),
379
- [
380
- 'Alias',
381
- [
382
- 'ReferencedField',
383
- 'pilot.identification method',
384
- 'identification number',
385
- ],
386
- 'identification_number',
387
- ],
388
- [
389
- 'Alias',
390
- ['ReferencedField', 'pilot.identification method', 'created at'],
391
- 'created_at',
392
- ],
393
- ])
394
- .from('pilot')
395
- .leftJoin([
396
- ['identification method', 'pilot.identification method'],
397
- [
398
- 'And',
399
- [
400
- 'Equals',
401
- ['ReferencedField', 'pilot', 'id'],
402
- ['ReferencedField', 'pilot.identification method', 'pilot'],
403
- ],
404
- [
405
- 'IsNotDistinctFrom',
406
- [
407
- 'ReferencedField',
408
- 'pilot.identification method',
409
- 'identification type',
410
- ],
411
- ['Bind', 0],
412
- ],
413
- ],
414
- ]);
415
- },
416
- 'expected SyntaxError: Adding JOINs on the same res… to be an instance of Array',
417
- );
418
- });
package/test/stress.ts DELETED
@@ -1,14 +0,0 @@
1
- import { expect } from 'chai';
2
- import { pilotFields } from './chai-sql';
3
- import test from './test';
4
- import _ from 'lodash';
5
-
6
- const filterString = _.range(1, 2000)
7
- .map((i) => 'id eq ' + i)
8
- .join(' or ');
9
- test('/pilot?$filter=' + filterString, (result) => {
10
- it('should select from pilot with a very long where', () => {
11
- expect(result).to.be.a.query.that.selects(pilotFields).from('pilot');
12
- // with a very long where.
13
- });
14
- });
package/test/test.ts DELETED
@@ -1,106 +0,0 @@
1
- import { clientModel } from './chai-sql';
2
- import * as ODataParser from '@balena/odata-parser';
3
- import { OData2AbstractSQL } from '../out/odata-to-abstract-sql';
4
- const translator = new OData2AbstractSQL(clientModel);
5
-
6
- const { skip } = describe;
7
- const runExpectation = function (
8
- describe: Mocha.SuiteFunction,
9
- input: any,
10
- method: any,
11
- body?: any,
12
- expectation?: any,
13
- ) {
14
- if (expectation == null) {
15
- if (body == null) {
16
- expectation = method;
17
- method = 'GET';
18
- } else {
19
- expectation = body;
20
- }
21
- body = {};
22
- }
23
-
24
- return describe(
25
- 'Parsing ' + method + ' ' + input + ' ' + JSON.stringify(body),
26
- function () {
27
- let tree;
28
- if (describe === skip) {
29
- return expectation();
30
- }
31
- try {
32
- let extraBodyVars;
33
- input = ODataParser.parse(input);
34
- ({ tree, extraBodyVars } = translator.match(
35
- input.tree,
36
- method,
37
- Object.keys(body),
38
- 0,
39
- ));
40
- Object.assign(body, extraBodyVars);
41
- } catch (e) {
42
- expectation(e);
43
- return;
44
- }
45
- return expectation(tree);
46
- },
47
- );
48
- };
49
-
50
- type TailParameters<T extends (...args: any) => any> = T extends (
51
- arg,
52
- ...args: infer P
53
- ) => any
54
- ? P
55
- : never;
56
- type TestFn = (
57
- ...args: TailParameters<typeof runExpectation>
58
- ) => ReturnType<typeof runExpectation>;
59
- interface Test extends TestFn {
60
- skip: TestFn;
61
- only: TestFn;
62
- }
63
- const test = runExpectation.bind(null, describe) as Test;
64
- test.skip = runExpectation.bind(null, describe.skip);
65
- // eslint-disable-next-line no-only-tests/no-only-tests
66
- test.only = runExpectation.bind(null, describe.only);
67
-
68
- export default test;
69
-
70
- export const itExpectsError = (
71
- title: string,
72
- fn: (this: Mocha.Context) => void,
73
- expectedError: string | RegExp | ((err: Error) => boolean),
74
- ) => {
75
- it(`[Expect test case to fail] ${title}`, function () {
76
- try {
77
- fn?.call(this);
78
- throw new Error(`
79
- (Maybe a good one) Test case:
80
- > ${title}
81
-
82
- that was expected to fail, now completed without issues!
83
- Confirm whether the test was properly fixed and change its 'itExpectsError()' to an 'it()'.
84
- Thanks for fixing it!
85
- `);
86
- } catch (err) {
87
- if (!(err instanceof Error)) {
88
- throw err;
89
- }
90
- const isExpectedError =
91
- typeof expectedError === 'function'
92
- ? expectedError
93
- : (e: Error) => {
94
- if (typeof expectedError === 'string') {
95
- return expectedError === e.message;
96
- }
97
- if (expectedError instanceof RegExp) {
98
- return expectedError.test(e.message);
99
- }
100
- };
101
- if (!isExpectedError(err)) {
102
- throw err;
103
- }
104
- }
105
- });
106
- };
package/tsconfig.js.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noImplicitAny": false,
5
- "noImplicitThis": false,
6
- "strictFunctionTypes": false
7
- },
8
- "include": [
9
- "*",
10
- "src/**/*",
11
- "test/**/*"
12
- ],
13
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "Node16",
4
- "target": "es2023",
5
- "strict": true,
6
- "noImplicitAny": true,
7
- "noUnusedParameters": true,
8
- "noUnusedLocals": true,
9
- "preserveConstEnums": true,
10
- "removeComments": true,
11
- "sourceMap": true,
12
- "declaration": true,
13
- "skipLibCheck": true,
14
- "outDir": "out"
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules"]
18
- }