@balena/pinejs 14.60.0-build-translated-models-85baf731130de328e2067a26c40b37309cdb1ac5-1 → 15.0.0-build-15-x-4681209f5dd8d896491fb5ed64aea47df511c14d-1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. package/.pinejs-cache.json +1 -1
  2. package/.versionbot/CHANGELOG.yml +106 -6
  3. package/CHANGELOG.md +16 -4
  4. package/VERSION +1 -1
  5. package/build/browser.ts +0 -1
  6. package/out/bin/abstract-sql-compiler.js +0 -0
  7. package/out/bin/abstract-sql-compiler.js.map +1 -1
  8. package/out/bin/odata-compiler.js +1 -4
  9. package/out/bin/odata-compiler.js.map +1 -1
  10. package/out/bin/sbvr-compiler.js +0 -0
  11. package/out/bin/sbvr-compiler.js.map +1 -1
  12. package/out/config-loader/config-loader.d.ts +2 -5
  13. package/out/config-loader/config-loader.js +19 -38
  14. package/out/config-loader/config-loader.js.map +1 -1
  15. package/out/config-loader/env.js +4 -4
  16. package/out/config-loader/env.js.map +1 -1
  17. package/out/data-server/sbvr-server.d.ts +1 -13
  18. package/out/data-server/sbvr-server.js +17 -1
  19. package/out/data-server/sbvr-server.js.map +1 -1
  20. package/out/database-layer/db.js +12 -15
  21. package/out/database-layer/db.js.map +1 -1
  22. package/out/express-emulator/express.js +4 -4
  23. package/out/express-emulator/express.js.map +1 -1
  24. package/out/http-transactions/transactions.d.ts +1 -12
  25. package/out/http-transactions/transactions.js +18 -0
  26. package/out/http-transactions/transactions.js.map +1 -1
  27. package/out/migrator/async.js +12 -16
  28. package/out/migrator/async.js.map +1 -1
  29. package/out/migrator/sync.js +6 -12
  30. package/out/migrator/sync.js.map +1 -1
  31. package/out/migrator/utils.d.ts +5 -4
  32. package/out/migrator/utils.js +38 -20
  33. package/out/migrator/utils.js.map +1 -1
  34. package/out/pinejs-session-store/pinejs-session-store.js +18 -3
  35. package/out/pinejs-session-store/pinejs-session-store.js.map +1 -1
  36. package/out/sbvr-api/abstract-sql.js +2 -4
  37. package/out/sbvr-api/abstract-sql.js.map +1 -1
  38. package/out/sbvr-api/cached-compile.js +1 -1
  39. package/out/sbvr-api/cached-compile.js.map +1 -1
  40. package/out/sbvr-api/hooks.d.ts +3 -3
  41. package/out/sbvr-api/hooks.js +32 -48
  42. package/out/sbvr-api/hooks.js.map +1 -1
  43. package/out/sbvr-api/odata-response.js +4 -5
  44. package/out/sbvr-api/odata-response.js.map +1 -1
  45. package/out/sbvr-api/permissions.js +25 -65
  46. package/out/sbvr-api/permissions.js.map +1 -1
  47. package/out/sbvr-api/sbvr-utils.d.ts +5 -11
  48. package/out/sbvr-api/sbvr-utils.js +94 -145
  49. package/out/sbvr-api/sbvr-utils.js.map +1 -1
  50. package/out/sbvr-api/uri-parser.d.ts +1 -4
  51. package/out/sbvr-api/uri-parser.js +4 -11
  52. package/out/sbvr-api/uri-parser.js.map +1 -1
  53. package/package.json +8 -8
  54. package/src/bin/abstract-sql-compiler.ts +2 -1
  55. package/src/bin/odata-compiler.ts +2 -4
  56. package/src/bin/sbvr-compiler.ts +2 -1
  57. package/src/config-loader/config-loader.ts +24 -62
  58. package/src/config-loader/env.ts +3 -7
  59. package/src/data-server/sbvr-server.js +17 -1
  60. package/src/database-layer/db.ts +1 -1
  61. package/src/express-emulator/express.js +4 -4
  62. package/src/http-transactions/transactions.js +18 -0
  63. package/src/migrator/utils.ts +40 -20
  64. package/src/pinejs-session-store/pinejs-session-store.ts +15 -0
  65. package/src/sbvr-api/abstract-sql.ts +1 -2
  66. package/src/sbvr-api/hooks.ts +33 -80
  67. package/src/sbvr-api/odata-response.ts +1 -2
  68. package/src/sbvr-api/permissions.ts +20 -68
  69. package/src/sbvr-api/sbvr-utils.ts +107 -195
  70. package/src/sbvr-api/uri-parser.ts +5 -8
  71. package/tsconfig.json +1 -1
  72. package/out/sbvr-api/translations.d.ts +0 -6
  73. package/out/sbvr-api/translations.js +0 -136
  74. package/out/sbvr-api/translations.js.map +0 -1
  75. package/src/sbvr-api/translations.ts +0 -219
@@ -3,6 +3,7 @@ import { odataNameToSqlName } from '@balena/odata-to-abstract-sql';
3
3
  // @ts-ignore
4
4
  const transactionModel = require('./transaction.sbvr');
5
5
 
6
+ /** @type {import('../config-loader/config-loader').Config} */
6
7
  export let config = {
7
8
  models: [
8
9
  {
@@ -24,6 +25,23 @@ ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT N
24
25
  ALTER TABLE "transaction"
25
26
  ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;\
26
27
  `,
28
+ '15.0.0-data-types': async (tx, sbvrUtils) => {
29
+ switch (sbvrUtils.db.engine) {
30
+ case 'mysql':
31
+ await tx.executeSql(`\
32
+ ALTER TABLE "lock"
33
+ MODIFY "is exclusive" BOOLEAN NOT NULL,
34
+ MODIFY "is under lock" BOOLEAN NOT NULL;`);
35
+ break;
36
+ case 'postgres':
37
+ await tx.executeSql(`\
38
+ ALTER TABLE "lock"
39
+ ALTER COLUMN "is exclusive" SET DATA TYPE BOOLEAN USING b::BOOLEAN,
40
+ ALTER COLUMN "is under lock" SET DATA TYPE BOOLEAN USING b::BOOLEAN;`);
41
+ break;
42
+ // No need to migrate for websql
43
+ }
44
+ },
27
45
  },
28
46
  },
29
47
  ],
@@ -12,6 +12,23 @@ import { delay } from '../sbvr-api/control-flow';
12
12
 
13
13
  // tslint:disable-next-line:no-var-requires
14
14
  export const modelText = require('./migrations.sbvr');
15
+ export const migrations: Migrations = {
16
+ '15.0.0-data-types': async (tx, { db }) => {
17
+ switch (db.engine) {
18
+ case 'mysql':
19
+ await tx.executeSql(`\
20
+ ALTER TABLE "migration"
21
+ MODIFY "executed migrations" JSON NOT NULL;`);
22
+ break;
23
+ case 'postgres':
24
+ await tx.executeSql(`\
25
+ ALTER TABLE "migration"
26
+ ALTER COLUMN "executed migrations" SET DATA TYPE JSONB USING b::JSONB;`);
27
+ break;
28
+ // No need to migrate for websql
29
+ }
30
+ },
31
+ };
15
32
 
16
33
  import * as sbvrUtils from '../sbvr-api/sbvr-utils';
17
34
  export enum MigrationCategories {
@@ -73,14 +90,14 @@ export function isSyncMigration(
73
90
  return typeof migration === 'function' || typeof migration === 'string';
74
91
  }
75
92
  export function areCategorizedMigrations(
76
- migrations: Migrations,
77
- ): migrations is CategorizedMigrations {
93
+ $migrations: Migrations,
94
+ ): $migrations is CategorizedMigrations {
78
95
  const containsCategories = Object.keys(MigrationCategories).some(
79
- (key) => key in migrations,
96
+ (key) => key in $migrations,
80
97
  );
81
98
  if (
82
99
  containsCategories &&
83
- Object.keys(migrations).some((key) => !(key in MigrationCategories))
100
+ Object.keys($migrations).some((key) => !(key in MigrationCategories))
84
101
  ) {
85
102
  throw new Error(
86
103
  'Mixing categorized and uncategorized migrations is not supported',
@@ -104,31 +121,31 @@ export type MigrationStatus = {
104
121
  };
105
122
 
106
123
  export const getRunnableAsyncMigrations = (
107
- migrations: Migrations,
124
+ $migrations: Migrations,
108
125
  ): RunnableAsyncMigrations | undefined => {
109
- if (migrations[MigrationCategories.async]) {
126
+ if ($migrations[MigrationCategories.async]) {
110
127
  if (
111
- Object.values(migrations[MigrationCategories.async]).some(
128
+ Object.values($migrations[MigrationCategories.async]).some(
112
129
  (migration) => !isAsyncMigration(migration),
113
130
  ) ||
114
- typeof migrations[MigrationCategories.async] !== 'object'
131
+ typeof $migrations[MigrationCategories.async] !== 'object'
115
132
  ) {
116
133
  throw new Error(
117
134
  `All loaded async migrations need to be of type: ${MigrationCategories.async}`,
118
135
  );
119
136
  }
120
- return migrations[MigrationCategories.async] as RunnableAsyncMigrations;
137
+ return $migrations[MigrationCategories.async] as RunnableAsyncMigrations;
121
138
  }
122
139
  };
123
140
 
124
141
  // migration loader should either get migrations from model
125
142
  // or from the filepath
126
143
  export const getRunnableSyncMigrations = (
127
- migrations: Migrations,
144
+ $migrations: Migrations,
128
145
  ): RunnableMigrations => {
129
- if (areCategorizedMigrations(migrations)) {
146
+ if (areCategorizedMigrations($migrations)) {
130
147
  const runnableMigrations: RunnableMigrations = {};
131
- for (const [category, categoryMigrations] of Object.entries(migrations)) {
148
+ for (const [category, categoryMigrations] of Object.entries($migrations)) {
132
149
  if (category in MigrationCategories) {
133
150
  for (const [key, migration] of Object.entries(
134
151
  categoryMigrations as Migrations,
@@ -145,16 +162,16 @@ export const getRunnableSyncMigrations = (
145
162
  }
146
163
  return runnableMigrations;
147
164
  }
148
- return migrations;
165
+ return $migrations;
149
166
  };
150
167
 
151
168
  // turns {"key1": migration, "key3": migration, "key2": migration}
152
169
  // into [["key1", migration], ["key2", migration], ["key3", migration]]
153
170
  export const filterAndSortPendingMigrations = (
154
- migrations: NonNullable<RunnableMigrations | RunnableAsyncMigrations>,
171
+ $migrations: NonNullable<RunnableMigrations | RunnableAsyncMigrations>,
155
172
  executedMigrations: string[],
156
173
  ): MigrationTuple[] =>
157
- (_(migrations).omit(executedMigrations) as _.Object<typeof migrations>)
174
+ (_($migrations).omit(executedMigrations) as _.Object<typeof $migrations>)
158
175
  .toPairs()
159
176
  .sortBy(([migrationKey]) => migrationKey)
160
177
  .value();
@@ -313,8 +330,10 @@ WHERE "migration"."model name" = ${1}`,
313
330
  if (data == null) {
314
331
  return [];
315
332
  }
316
-
317
- return JSON.parse(data.executed_migrations) as string[];
333
+ if (typeof data.executed_migrations === 'string') {
334
+ return JSON.parse(data.executed_migrations);
335
+ }
336
+ return data.executed_migrations;
318
337
  };
319
338
 
320
339
  export const migrationTablesExist = async (tx: Tx) => {
@@ -338,7 +357,7 @@ WHERE NOT EXISTS (SELECT 1 FROM "migration status" WHERE "migration key" = ${5})
338
357
  [
339
358
  migrationStatus['migration_key'],
340
359
  migrationStatus['start_time'],
341
- migrationStatus['is_backing_off'] ? 1 : 0,
360
+ migrationStatus['is_backing_off'],
342
361
  migrationStatus['run_count'],
343
362
  migrationStatus['migration_key'],
344
363
  ],
@@ -372,7 +391,7 @@ WHERE "migration status"."migration key" = ${7};`,
372
391
  migrationStatus['migrated_row_count'],
373
392
  migrationStatus['error_count'],
374
393
  migrationStatus['converged_time'],
375
- migrationStatus['is_backing_off'] ? 1 : 0,
394
+ migrationStatus['is_backing_off'],
376
395
  migrationStatus['migration_key'],
377
396
  ],
378
397
  );
@@ -409,7 +428,8 @@ LIMIT 1;`,
409
428
  migrated_row_count: data['migrated row count'],
410
429
  error_count: data['error count'],
411
430
  converged_time: data['converged time'],
412
- is_backing_off: data['is backing off'] === 1,
431
+ is_backing_off:
432
+ data['is backing off'] === true || data['is backing off'] === 1,
413
433
  };
414
434
  } catch (err: any) {
415
435
  // we report any error here, as no error should happen at all
@@ -173,6 +173,21 @@ export class PinejsSessionStore extends Store {
173
173
  ALTER TABLE "session"
174
174
  ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
175
175
  `,
176
+ '15.0.0-data-types': async (tx, sbvrUtils) => {
177
+ switch (sbvrUtils.db.engine) {
178
+ case 'mysql':
179
+ await tx.executeSql(`\
180
+ ALTER TABLE "session"
181
+ MODIFY "data" JSON NOT NULL;`);
182
+ break;
183
+ case 'postgres':
184
+ await tx.executeSql(`\
185
+ ALTER TABLE "session"
186
+ ALTER COLUMN "data" SET DATA TYPE JSONB USING b::JSONB;`);
187
+ break;
188
+ // No need to migrate for websql
189
+ }
190
+ },
176
191
  },
177
192
  },
178
193
  ],
@@ -95,8 +95,7 @@ export const getAndCheckBindValues = async (
95
95
 
96
96
  const sqlTableName = odataNameToSqlName(tableName);
97
97
  const sqlFieldName = odataNameToSqlName(fieldName);
98
- const table = sqlModelTables[sqlTableName];
99
- const maybeField = (table.modifyFields ?? table.fields).find(
98
+ const maybeField = sqlModelTables[sqlTableName].fields.find(
100
99
  (f) => f.fieldName === sqlFieldName,
101
100
  );
102
101
  if (maybeField == null) {
@@ -42,9 +42,7 @@ export interface Hooks {
42
42
  PREPARSE?: (options: Omit<HookArgs, 'request' | 'api'>) => HookResponse;
43
43
  POSTPARSE?: (options: HookArgs) => HookResponse;
44
44
  PRERUN?: (options: HookArgs & { tx: Tx }) => HookResponse;
45
- /** These are run in reverse translation order from newest to oldest */
46
45
  POSTRUN?: (options: HookArgs & { tx: Tx; result: any }) => HookResponse;
47
- /** These are run in reverse translation order from newest to oldest */
48
46
  PRERESPOND?: (
49
47
  options: HookArgs & {
50
48
  tx: Tx;
@@ -55,7 +53,6 @@ export interface Hooks {
55
53
  data?: any;
56
54
  },
57
55
  ) => HookResponse;
58
- /** These are run in reverse translation order from newest to oldest */
59
56
  'POSTRUN-ERROR'?: (
60
57
  options: HookArgs & { error: TypedError | any },
61
58
  ) => HookResponse;
@@ -129,13 +126,13 @@ class SideEffectHook<T extends HookFn> extends Hook<T> {
129
126
 
130
127
  // The execution order of rollback actions is unspecified
131
128
  export const rollbackRequestHooks = <T extends InstantiatedHooks>(
132
- hooksList: Array<[modelName: string, hooks: T]> | undefined,
129
+ hooks: T | undefined,
133
130
  ): void => {
134
- if (hooksList == null) {
131
+ if (hooks == null) {
135
132
  return;
136
133
  }
137
- const sideEffectHooks = hooksList
138
- .flatMap(([, v]): Array<Hook<HookFn>> => Object.values(v).flat())
134
+ const sideEffectHooks = Object.values(hooks)
135
+ .flatMap((v): Array<Hook<HookFn>> => v)
139
136
  .filter(
140
137
  (hook): hook is SideEffectHook<HookFn> => hook instanceof SideEffectHook,
141
138
  );
@@ -180,37 +177,21 @@ const getResourceHooks = (vocabHooks: VocabHooks, resourceName?: string) => {
180
177
  const getVocabHooks = (
181
178
  methodHooks: MethodHooks,
182
179
  vocabulary: string,
183
- resourceName: string | undefined,
184
- includeAllVocab: boolean,
180
+ resourceName?: string,
185
181
  ) => {
186
182
  if (methodHooks == null) {
187
183
  return {};
188
184
  }
189
- const vocabHooks = getResourceHooks(methodHooks[vocabulary], resourceName);
190
- if (!includeAllVocab) {
191
- // Do not include `vocabulary='all'` hooks, useful for translated vocabularies
192
- return vocabHooks;
193
- }
194
185
  return mergeHooks(
195
- vocabHooks,
186
+ getResourceHooks(methodHooks[vocabulary], resourceName),
196
187
  getResourceHooks(methodHooks['all'], resourceName),
197
188
  );
198
189
  };
199
190
  const getMethodHooks = memoize(
200
- (
201
- method: SupportedMethod,
202
- vocabulary: string,
203
- resourceName: string | undefined,
204
- includeAllVocab: boolean,
205
- ) =>
191
+ (method: SupportedMethod, vocabulary: string, resourceName?: string) =>
206
192
  mergeHooks(
207
- getVocabHooks(
208
- apiHooks[method],
209
- vocabulary,
210
- resourceName,
211
- includeAllVocab,
212
- ),
213
- getVocabHooks(apiHooks['all'], vocabulary, resourceName, includeAllVocab),
193
+ getVocabHooks(apiHooks[method], vocabulary, resourceName),
194
+ getVocabHooks(apiHooks['all'], vocabulary, resourceName),
214
195
  ),
215
196
  { primitive: true },
216
197
  );
@@ -219,7 +200,6 @@ export const getHooks = (
219
200
  OptionalField<ParsedODataRequest, 'resourceName'>,
220
201
  'resourceName' | 'method' | 'vocabulary'
221
202
  >,
222
- includeAllVocab: boolean,
223
203
  ): InstantiatedHooks => {
224
204
  let { resourceName } = request;
225
205
  if (resourceName != null) {
@@ -228,17 +208,10 @@ export const getHooks = (
228
208
  ParsedODataRequest,
229
209
  'resourceName' | 'method' | 'vocabulary'
230
210
  >,
231
- )
232
- // Remove version suffixes
233
- .replace(/\$.*$/, '');
211
+ );
234
212
  }
235
213
  return instantiateHooks(
236
- getMethodHooks(
237
- request.method,
238
- request.vocabulary,
239
- resourceName,
240
- includeAllVocab,
241
- ),
214
+ getMethodHooks(request.method, request.vocabulary, resourceName),
242
215
  );
243
216
  };
244
217
  getHooks.clear = () => getMethodHooks.clear();
@@ -370,11 +343,12 @@ export const addPureHook = (
370
343
  });
371
344
  };
372
345
 
373
- const defineApi = (modelName: string, args: HookArgs) => {
374
- const { req, tx } = args;
346
+ const defineApi = (args: HookArgs) => {
347
+ const { request, req, tx } = args;
348
+ const { vocabulary } = request;
375
349
  Object.defineProperty(args, 'api', {
376
350
  get: _.once(() =>
377
- api[modelName].clone({
351
+ api[vocabulary].clone({
378
352
  passthrough: { req, tx },
379
353
  }),
380
354
  ),
@@ -386,7 +360,6 @@ type RunHookArgs<T extends keyof Hooks> = Omit<
386
360
  'api'
387
361
  >;
388
362
  const getReadOnlyArgs = <T extends keyof Hooks>(
389
- modelName: string,
390
363
  args: RunHookArgs<T>,
391
364
  ): RunHookArgs<T> => {
392
365
  if (args.tx == null || args.tx.isReadOnly()) {
@@ -396,58 +369,38 @@ const getReadOnlyArgs = <T extends keyof Hooks>(
396
369
  let readOnlyArgs: typeof args;
397
370
  readOnlyArgs = { ...args, tx: args.tx.asReadOnly() };
398
371
  if ((args as HookArgs).request != null) {
399
- defineApi(modelName, readOnlyArgs as HookArgs);
372
+ defineApi(readOnlyArgs as HookArgs);
400
373
  }
401
374
  return readOnlyArgs;
402
375
  };
403
376
 
404
377
  export const runHooks = async <T extends keyof Hooks>(
405
378
  hookName: T,
406
- /**
407
- * A list of modelName/hooks to run in order, which will be reversed for hooks after the "RUN" stage,
408
- * ie POSTRUN/PRERESPOND/POSTRUN-ERROR
409
- */
410
- hooksList: Array<[modelName: string, hooks: InstantiatedHooks]> | undefined,
379
+ hooksList: InstantiatedHooks | undefined,
411
380
  args: RunHookArgs<T>,
412
381
  ) => {
413
382
  if (hooksList == null) {
414
383
  return;
415
384
  }
416
- const hooks = hooksList
417
- .map(([modelName, $hooks]): [string, InstantiatedHooks[T] | undefined] => [
418
- modelName,
419
- $hooks[hookName],
420
- ])
421
- .filter(
422
- (v): v is [string, InstantiatedHooks[T]] =>
423
- v[1] != null && v[1].length > 0,
424
- );
425
- if (hooks.length === 0) {
385
+ const hooks = hooksList[hookName];
386
+ if (hooks == null || hooks.length === 0) {
426
387
  return;
427
388
  }
428
- if (['POSTRUN', 'PRERESPOND', 'POSTRUN-ERROR'].includes(hookName)) {
429
- // Any hooks after we "run" the query are executed in reverse order from newest to oldest
430
- // as they'll be translating the query results from "latest" backwards to the model that
431
- // was actually requested
432
- hooks.reverse();
389
+
390
+ if ((args as HookArgs).request != null) {
391
+ defineApi(args as HookArgs);
433
392
  }
434
393
 
435
- for (const [modelName, modelHooks] of hooks) {
436
- const modelArgs = { ...args };
437
- let modelReadOnlyArgs: typeof modelArgs;
438
- if ((args as HookArgs).request != null) {
439
- defineApi(modelName, modelArgs as HookArgs);
440
- }
394
+ let readOnlyArgs: RunHookArgs<T>;
441
395
 
442
- await Promise.all(
443
- (modelHooks as Array<Hook<HookFn>>).map(async (hook) => {
444
- if (hook.readOnlyTx) {
445
- modelReadOnlyArgs ??= getReadOnlyArgs(modelName, args);
446
- await hook.run(modelReadOnlyArgs);
447
- } else {
448
- await hook.run(modelArgs);
449
- }
450
- }),
451
- );
452
- }
396
+ await Promise.all(
397
+ (hooks as Array<Hook<HookFn>>).map(async (hook) => {
398
+ if (hook.readOnlyTx) {
399
+ readOnlyArgs ??= getReadOnlyArgs(args);
400
+ await hook.run(readOnlyArgs);
401
+ } else {
402
+ await hook.run(args);
403
+ }
404
+ }),
405
+ );
453
406
  };
@@ -90,8 +90,7 @@ const getLocalFields = (table: AbstractSqlTable) => {
90
90
  if (table.localFields == null) {
91
91
  table.localFields = {};
92
92
  for (const { fieldName, dataType } of table.fields) {
93
- // TODO-MAJOR: This should also include ConceptType fields
94
- if (dataType !== 'ForeignKey') {
93
+ if (!['ForeignKey', 'ConceptType'].includes(dataType)) {
95
94
  const odataName = sqlNameToODataName(fieldName);
96
95
  table.localFields[odataName] = true;
97
96
  }
@@ -140,7 +140,7 @@ const $parsePermissions = env.createCache(
140
140
  },
141
141
  );
142
142
 
143
- const rewriteODataBinds = (
143
+ const rewriteBinds = (
144
144
  { tree, extraBinds }: { tree: ODataQuery; extraBinds: ODataBinds },
145
145
  odataBinds: ODataBinds,
146
146
  ): ODataQuery => {
@@ -163,7 +163,7 @@ const parsePermissions = (
163
163
  odataBinds: ODataBinds,
164
164
  ): ODataQuery => {
165
165
  const odata = $parsePermissions(filter);
166
- return rewriteODataBinds(odata, odataBinds);
166
+ return rewriteBinds(odata, odataBinds);
167
167
  };
168
168
 
169
169
  // Traverses all values in `check`, actions for the following data types:
@@ -862,9 +862,7 @@ const rewriteRelationship = memoizeWeak(
862
862
  foundCanAccessLink = true;
863
863
  }
864
864
  // return a true expression to not select the relationship, which might be virtual
865
- // this should be a boolean expression, but needs to be a subquery in case it
866
- // is wrapped in an `or` or `and`
867
- return ['Equals', ['Boolean', true], ['Boolean', true]];
865
+ return ['Boolean', true];
868
866
  };
869
867
 
870
868
  try {
@@ -1050,7 +1048,6 @@ const getBoundConstrainedMemoizer = memoizeWeak(
1050
1048
  sqlNameToODataName(permissionsTable.name),
1051
1049
  ),
1052
1050
  );
1053
-
1054
1051
  return permissionsTable;
1055
1052
  },
1056
1053
  },
@@ -1447,26 +1444,13 @@ const checkApiKey = async (
1447
1444
  apiKey: string,
1448
1445
  tx?: Tx,
1449
1446
  ): Promise<PermissionReq['apiKey']> => {
1450
- let permissions: string[];
1451
- try {
1452
- permissions = await getApiKeyPermissions(apiKey, tx);
1453
- } catch (err: any) {
1454
- console.warn('Error with API key:', err);
1455
- // Ignore errors getting the api key and just use an empty permissions object.
1456
- permissions = [];
1457
- }
1458
- let actor;
1459
- if (permissions.length > 0) {
1460
- actor = await getApiKeyActorId(apiKey, tx);
1461
- }
1462
- const resolvedApiKey: PermissionReq['apiKey'] = {
1447
+ const permissions = await getApiKeyPermissions(apiKey, tx);
1448
+ const actor = await getApiKeyActorId(apiKey, tx);
1449
+ return {
1463
1450
  key: apiKey,
1464
1451
  permissions,
1452
+ actor,
1465
1453
  };
1466
- if (actor != null) {
1467
- resolvedApiKey.actor = actor;
1468
- }
1469
- return resolvedApiKey;
1470
1454
  };
1471
1455
 
1472
1456
  export const resolveAuthHeader = async (
@@ -1475,11 +1459,6 @@ export const resolveAuthHeader = async (
1475
1459
  // TODO: Consider making tx the second argument in the next major
1476
1460
  tx?: Tx,
1477
1461
  ): Promise<PermissionReq['apiKey']> => {
1478
- // TODO-MAJOR: remove this check
1479
- if (req.apiKey != null) {
1480
- return;
1481
- }
1482
-
1483
1462
  const auth = req.header('Authorization');
1484
1463
  if (!auth) {
1485
1464
  return;
@@ -1525,11 +1504,6 @@ export const resolveApiKey = async (
1525
1504
  // TODO: Consider making tx the second argument in the next major
1526
1505
  tx?: Tx,
1527
1506
  ): Promise<PermissionReq['apiKey']> => {
1528
- // TODO-MAJOR: remove this check
1529
- if (req.apiKey != null) {
1530
- return;
1531
- }
1532
-
1533
1507
  const apiKey =
1534
1508
  req.params[paramName] ?? req.body[paramName] ?? req.query[paramName];
1535
1509
  if (apiKey == null) {
@@ -1539,9 +1513,6 @@ export const resolveApiKey = async (
1539
1513
  };
1540
1514
 
1541
1515
  export const customApiKeyMiddleware = (paramName = 'apikey') => {
1542
- if (paramName == null) {
1543
- paramName = 'apikey';
1544
- }
1545
1516
  return async (
1546
1517
  req: HookReq | Express.Request,
1547
1518
  _res?: Express.Response,
@@ -1633,32 +1604,18 @@ const getReqPermissions = async (
1633
1604
  req: PermissionReq,
1634
1605
  odataBinds: ODataBinds = [],
1635
1606
  ) => {
1636
- const [guestPermissions] = await Promise.all([
1637
- (async () => {
1638
- if (
1639
- guestPermissionsInitialized === false &&
1640
- (req.user === root.user || req.user === rootRead.user)
1641
- ) {
1642
- // In the case that guest permissions are not initialized yet and the query is being made with root permissions
1643
- // then we need to bypass `getGuestPermissions` as it will cause an infinite loop back to here.
1644
- // Therefore to break that loop we just ignore guest permissions.
1645
- return [];
1646
- }
1647
- return await getGuestPermissions();
1648
- })(),
1649
- (async () => {
1650
- // TODO: Remove this extra actor ID lookup making actor non-optional and updating open-balena-api.
1651
- if (
1652
- req.apiKey != null &&
1653
- req.apiKey.actor == null &&
1654
- req.apiKey.permissions != null &&
1655
- req.apiKey.permissions.length > 0
1656
- ) {
1657
- const actorId = await getApiKeyActorId(req.apiKey.key);
1658
- req.apiKey!.actor = actorId;
1659
- }
1660
- })(),
1661
- ]);
1607
+ const guestPermissions = await (async () => {
1608
+ if (
1609
+ guestPermissionsInitialized === false &&
1610
+ (req.user === root.user || req.user === rootRead.user)
1611
+ ) {
1612
+ // In the case that guest permissions are not initialized yet and the query is being made with root permissions
1613
+ // then we need to bypass `getGuestPermissions` as it will cause an infinite loop back to here.
1614
+ // Therefore to break that loop we just ignore guest permissions.
1615
+ return [];
1616
+ }
1617
+ return await getGuestPermissions();
1618
+ })();
1662
1619
 
1663
1620
  let actorPermissions: string[] = [];
1664
1621
  const addActorPermissions = (actorId: number, perms: string[]) => {
@@ -1679,8 +1636,7 @@ export const addPermissions = async (
1679
1636
  req: PermissionReq,
1680
1637
  request: ODataRequest & { permissionType?: PermissionCheck },
1681
1638
  ): Promise<void> => {
1682
- const { resourceName, odataQuery, odataBinds } = request;
1683
- const vocabulary = _.last(request.translateVersions)!;
1639
+ const { vocabulary, resourceName, odataQuery, odataBinds } = request;
1684
1640
  let abstractSqlModel = sbvrUtils.getAbstractSqlModel(request);
1685
1641
 
1686
1642
  let { permissionType } = request;
@@ -1803,10 +1759,6 @@ export const setup = () => {
1803
1759
  0,
1804
1760
  -'#canAccess'.length,
1805
1761
  );
1806
- request.originalResourceName = request.originalResourceName.slice(
1807
- 0,
1808
- -'#canAccess'.length,
1809
- );
1810
1762
  const resourceName = sbvrUtils.resolveSynonym(request);
1811
1763
  const resourceTable = abstractSqlModel.tables[resourceName];
1812
1764
  if (resourceTable == null) {