directus 9.14.1 → 9.14.5

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.
Files changed (50) hide show
  1. package/dist/cli/commands/security/key.d.ts +1 -0
  2. package/dist/cli/commands/security/key.js +8 -0
  3. package/dist/cli/commands/security/secret.d.ts +1 -0
  4. package/dist/cli/commands/security/secret.js +8 -0
  5. package/dist/cli/index.js +6 -0
  6. package/dist/controllers/extensions.js +9 -1
  7. package/dist/database/helpers/fn/dialects/mssql.d.ts +2 -2
  8. package/dist/database/helpers/fn/dialects/mssql.js +2 -2
  9. package/dist/database/helpers/fn/dialects/mysql.d.ts +2 -2
  10. package/dist/database/helpers/fn/dialects/mysql.js +2 -2
  11. package/dist/database/helpers/fn/dialects/oracle.d.ts +1 -1
  12. package/dist/database/helpers/fn/dialects/oracle.js +2 -2
  13. package/dist/database/helpers/fn/dialects/postgres.d.ts +2 -2
  14. package/dist/database/helpers/fn/dialects/postgres.js +2 -2
  15. package/dist/database/helpers/fn/dialects/sqlite.d.ts +1 -1
  16. package/dist/database/helpers/fn/dialects/sqlite.js +2 -2
  17. package/dist/database/helpers/fn/types.d.ts +3 -2
  18. package/dist/database/helpers/fn/types.js +11 -8
  19. package/dist/database/helpers/index.d.ts +3 -3
  20. package/dist/database/helpers/schema/dialects/sqlite.d.ts +5 -0
  21. package/dist/database/helpers/schema/dialects/sqlite.js +17 -0
  22. package/dist/database/helpers/schema/index.d.ts +1 -1
  23. package/dist/database/helpers/schema/index.js +4 -4
  24. package/dist/database/helpers/schema/types.d.ts +2 -0
  25. package/dist/database/helpers/schema/types.js +6 -0
  26. package/dist/database/run-ast.js +8 -5
  27. package/dist/logger.d.ts +0 -1
  28. package/dist/middleware/authenticate.d.ts +0 -1
  29. package/dist/middleware/cache.js +3 -3
  30. package/dist/middleware/graphql.js +9 -7
  31. package/dist/middleware/respond.js +5 -5
  32. package/dist/operations/request/index.js +1 -1
  33. package/dist/services/authorization.js +15 -5
  34. package/dist/services/fields.js +4 -0
  35. package/dist/services/graphql/index.js +44 -17
  36. package/dist/services/server.js +13 -2
  37. package/dist/types/ast.d.ts +11 -4
  38. package/dist/utils/apply-query.js +5 -13
  39. package/dist/utils/apply-snapshot.js +41 -17
  40. package/dist/utils/filter-items.js +3 -6
  41. package/dist/utils/get-ast-from-query.js +18 -0
  42. package/dist/utils/get-column-path.d.ts +5 -1
  43. package/dist/utils/get-column-path.js +3 -1
  44. package/dist/utils/get-column.d.ts +2 -2
  45. package/dist/utils/get-column.js +2 -2
  46. package/dist/utils/get-config-from-env.js +1 -1
  47. package/dist/utils/merge-permissions.d.ts +0 -1
  48. package/package.json +231 -226
  49. package/dist/utils/generate-joi.d.ts +0 -3
  50. package/dist/utils/generate-joi.js +0 -145
@@ -0,0 +1 @@
1
+ export default function generateKey(): Promise<void>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const uuid_1 = require("uuid");
4
+ async function generateKey() {
5
+ process.stdout.write((0, uuid_1.v4)());
6
+ process.exit(0);
7
+ }
8
+ exports.default = generateKey;
@@ -0,0 +1 @@
1
+ export default function generateSecret(): Promise<void>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const nanoid_1 = require("nanoid");
4
+ async function generateSecret() {
5
+ process.stdout.write((0, nanoid_1.nanoid)(32));
6
+ process.exit(0);
7
+ }
8
+ exports.default = generateSecret;
package/dist/cli/index.js CHANGED
@@ -13,6 +13,8 @@ const count_1 = __importDefault(require("./commands/count"));
13
13
  const install_1 = __importDefault(require("./commands/database/install"));
14
14
  const migrate_1 = __importDefault(require("./commands/database/migrate"));
15
15
  const init_1 = __importDefault(require("./commands/init"));
16
+ const key_1 = __importDefault(require("./commands/security/key"));
17
+ const secret_1 = __importDefault(require("./commands/security/secret"));
16
18
  const create_1 = __importDefault(require("./commands/roles/create"));
17
19
  const create_2 = __importDefault(require("./commands/users/create"));
18
20
  const passwd_1 = __importDefault(require("./commands/users/passwd"));
@@ -28,6 +30,10 @@ async function createCli() {
28
30
  program.version(pkg.version, '-v, --version');
29
31
  program.command('start').description('Start the Directus API').action(server_1.startServer);
30
32
  program.command('init').description('Create a new Directus Project').action(init_1.default);
33
+ // Security
34
+ const securityCommand = program.command('security');
35
+ securityCommand.command('key:generate').description('Generate the app key').action(key_1.default);
36
+ securityCommand.command('secret:generate').description('Generate the app secret').action(secret_1.default);
31
37
  const dbCommand = program.command('database');
32
38
  dbCommand.command('install').description('Install the database').action(install_1.default);
33
39
  dbCommand
@@ -9,6 +9,9 @@ const exceptions_1 = require("../exceptions");
9
9
  const extensions_1 = require("../extensions");
10
10
  const respond_1 = require("../middleware/respond");
11
11
  const utils_1 = require("@directus/shared/utils");
12
+ const ms_1 = __importDefault(require("ms"));
13
+ const env_1 = __importDefault(require("../env"));
14
+ const get_cache_headers_1 = require("../utils/get-cache-headers");
12
15
  const router = (0, express_1.Router)();
13
16
  router.get('/:type', (0, async_handler_1.default)(async (req, res, next) => {
14
17
  const type = (0, utils_1.depluralize)(req.params.type);
@@ -33,7 +36,12 @@ router.get('/:type/index.js', (0, async_handler_1.default)(async (req, res) => {
33
36
  throw new exceptions_1.RouteNotFoundException(req.path);
34
37
  }
35
38
  res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
36
- res.setHeader('Cache-Control', 'no-store');
39
+ if (env_1.default.EXTENSIONS_CACHE_TTL) {
40
+ res.setHeader('Cache-Control', (0, get_cache_headers_1.getCacheControlHeader)(req, (0, ms_1.default)(env_1.default.EXTENSIONS_CACHE_TTL)));
41
+ }
42
+ else {
43
+ res.setHeader('Cache-Control', 'no-store');
44
+ }
37
45
  res.setHeader('Vary', 'Origin, Cache-Control');
38
46
  res.end(extensionSource);
39
47
  }));
@@ -1,4 +1,4 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperMSSQL extends FnHelper {
4
4
  year(table: string, column: string): Knex.Raw;
@@ -9,5 +9,5 @@ export declare class FnHelperMSSQL extends FnHelper {
9
9
  hour(table: string, column: string): Knex.Raw;
10
10
  minute(table: string, column: string): Knex.Raw;
11
11
  second(table: string, column: string): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw<any>;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw<any>;
13
13
  }
@@ -27,14 +27,14 @@ class FnHelperMSSQL extends types_1.FnHelper {
27
27
  second(table, column) {
28
28
  return this.knex.raw('DATEPART(second, ??.??)', [table, column]);
29
29
  }
30
- count(table, column) {
30
+ count(table, column, options) {
31
31
  var _a, _b, _c, _d, _e;
32
32
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
33
33
  if (type === 'json') {
34
34
  return this.knex.raw(`(SELECT COUNT(*) FROM OPENJSON(??.??, '$'))`, [table, column]);
35
35
  }
36
36
  if (type === 'alias') {
37
- return this._relationalCount(table, column);
37
+ return this._relationalCount(table, column, options);
38
38
  }
39
39
  throw new Error(`Couldn't extract type from ${table}.${column}`);
40
40
  }
@@ -1,4 +1,4 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperMySQL extends FnHelper {
4
4
  year(table: string, column: string): Knex.Raw;
@@ -9,5 +9,5 @@ export declare class FnHelperMySQL extends FnHelper {
9
9
  hour(table: string, column: string): Knex.Raw;
10
10
  minute(table: string, column: string): Knex.Raw;
11
11
  second(table: string, column: string): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
13
13
  }
@@ -27,14 +27,14 @@ class FnHelperMySQL extends types_1.FnHelper {
27
27
  second(table, column) {
28
28
  return this.knex.raw('SECOND(??.??)', [table, column]);
29
29
  }
30
- count(table, column) {
30
+ count(table, column, options) {
31
31
  var _a, _b, _c, _d, _e;
32
32
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
33
33
  if (type === 'json') {
34
34
  return this.knex.raw('JSON_LENGTH(??.??)', [table, column]);
35
35
  }
36
36
  if (type === 'alias') {
37
- return this._relationalCount(table, column);
37
+ return this._relationalCount(table, column, options);
38
38
  }
39
39
  throw new Error(`Couldn't extract type from ${table}.${column}`);
40
40
  }
@@ -9,5 +9,5 @@ export declare class FnHelperOracle extends FnHelper {
9
9
  hour(table: string, column: string, options: FnHelperOptions): Knex.Raw;
10
10
  minute(table: string, column: string, options: FnHelperOptions): Knex.Raw;
11
11
  second(table: string, column: string, options: FnHelperOptions): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw<any>;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw<any>;
13
13
  }
@@ -33,14 +33,14 @@ class FnHelperOracle extends types_1.FnHelper {
33
33
  second(table, column, options) {
34
34
  return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'SS')`, [table, column]);
35
35
  }
36
- count(table, column) {
36
+ count(table, column, options) {
37
37
  var _a, _b, _c, _d, _e;
38
38
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
39
39
  if (type === 'json') {
40
40
  return this.knex.raw("json_value(??.??, '$.size()')", [table, column]);
41
41
  }
42
42
  if (type === 'alias') {
43
- return this._relationalCount(table, column);
43
+ return this._relationalCount(table, column, options);
44
44
  }
45
45
  throw new Error(`Couldn't extract type from ${table}.${column}`);
46
46
  }
@@ -1,4 +1,4 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperPostgres extends FnHelper {
4
4
  year(table: string, column: string): Knex.Raw;
@@ -9,5 +9,5 @@ export declare class FnHelperPostgres extends FnHelper {
9
9
  hour(table: string, column: string): Knex.Raw;
10
10
  minute(table: string, column: string): Knex.Raw;
11
11
  second(table: string, column: string): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
13
13
  }
@@ -27,7 +27,7 @@ class FnHelperPostgres extends types_1.FnHelper {
27
27
  second(table, column) {
28
28
  return this.knex.raw('EXTRACT(SECOND FROM ??.??)', [table, column]);
29
29
  }
30
- count(table, column) {
30
+ count(table, column, options) {
31
31
  var _a, _b, _c, _d, _e;
32
32
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
33
33
  if (type === 'json') {
@@ -38,7 +38,7 @@ class FnHelperPostgres extends types_1.FnHelper {
38
38
  ]);
39
39
  }
40
40
  if (type === 'alias') {
41
- return this._relationalCount(table, column);
41
+ return this._relationalCount(table, column, options);
42
42
  }
43
43
  throw new Error(`Couldn't extract type from ${table}.${column}`);
44
44
  }
@@ -9,5 +9,5 @@ export declare class FnHelperSQLite extends FnHelper {
9
9
  hour(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
10
10
  minute(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
11
11
  second(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw<any>;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw<any>;
13
13
  }
@@ -57,14 +57,14 @@ class FnHelperSQLite extends types_1.FnHelper {
57
57
  column,
58
58
  ]);
59
59
  }
60
- count(table, column) {
60
+ count(table, column, options) {
61
61
  var _a, _b, _c, _d, _e;
62
62
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
63
63
  if (type === 'json') {
64
64
  return this.knex.raw(`json_array_length(??.??, '$')`, [table, column]);
65
65
  }
66
66
  if (type === 'alias') {
67
- return this._relationalCount(table, column);
67
+ return this._relationalCount(table, column, options);
68
68
  }
69
69
  throw new Error(`Couldn't extract type from ${table}.${column}`);
70
70
  }
@@ -1,8 +1,9 @@
1
- import { SchemaOverview } from '@directus/shared/types';
1
+ import { Query, SchemaOverview } from '@directus/shared/types';
2
2
  import { Knex } from 'knex';
3
3
  import { DatabaseHelper } from '../types';
4
4
  export declare type FnHelperOptions = {
5
5
  type?: string;
6
+ query?: Query;
6
7
  };
7
8
  export declare abstract class FnHelper extends DatabaseHelper {
8
9
  protected knex: Knex;
@@ -17,5 +18,5 @@ export declare abstract class FnHelper extends DatabaseHelper {
17
18
  abstract minute(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
18
19
  abstract second(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
19
20
  abstract count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
20
- protected _relationalCount(table: string, column: string): Knex.Raw;
21
+ protected _relationalCount(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
21
22
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FnHelper = void 0;
4
+ const apply_query_1 = require("../../../utils/apply-query");
4
5
  const types_1 = require("../types");
5
6
  class FnHelper extends types_1.DatabaseHelper {
6
7
  constructor(knex, schema) {
@@ -9,19 +10,21 @@ class FnHelper extends types_1.DatabaseHelper {
9
10
  this.schema = schema;
10
11
  this.schema = schema;
11
12
  }
12
- _relationalCount(table, column) {
13
+ _relationalCount(table, column, options) {
14
+ var _a;
13
15
  const relation = this.schema.relations.find((relation) => { var _a; return relation.related_collection === table && ((_a = relation === null || relation === void 0 ? void 0 : relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === column; });
14
16
  const currentPrimary = this.schema.collections[table].primary;
15
17
  if (!relation) {
16
18
  throw new Error(`Field ${table}.${column} isn't a nested relational collection`);
17
19
  }
18
- return this.knex.raw('(' +
19
- this.knex
20
- .count('*')
21
- .from(relation.collection)
22
- .where(relation.field, '=', this.knex.raw(`??.??`, [table, currentPrimary]))
23
- .toQuery() +
24
- ')');
20
+ let countQuery = this.knex
21
+ .count('*')
22
+ .from(relation.collection)
23
+ .where(relation.field, '=', this.knex.raw(`??.??`, [table, currentPrimary]));
24
+ if ((_a = options === null || options === void 0 ? void 0 : options.query) === null || _a === void 0 ? void 0 : _a.filter) {
25
+ countQuery = (0, apply_query_1.applyFilter)(this.knex, this.schema, countQuery, options.query.filter, relation.collection, false);
26
+ }
27
+ return this.knex.raw('(' + countQuery.toQuery() + ')');
25
28
  }
26
29
  }
27
30
  exports.FnHelper = FnHelper;
@@ -6,8 +6,8 @@ import * as geometryHelpers from './geometry';
6
6
  import * as schemaHelpers from './schema';
7
7
  export declare function getHelpers(database: Knex): {
8
8
  date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
9
- st: geometryHelpers.postgres | geometryHelpers.redshift | geometryHelpers.oracle | geometryHelpers.sqlite | geometryHelpers.mysql | geometryHelpers.mssql;
10
- schema: schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
9
+ st: geometryHelpers.sqlite | geometryHelpers.postgres | geometryHelpers.mysql | geometryHelpers.oracle | geometryHelpers.mssql | geometryHelpers.redshift;
10
+ schema: schemaHelpers.sqlite | schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
11
11
  };
12
- export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.postgres | fnHelpers.oracle | fnHelpers.sqlite | fnHelpers.mysql | fnHelpers.mssql;
12
+ export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.sqlite | fnHelpers.postgres | fnHelpers.mysql | fnHelpers.oracle | fnHelpers.mssql;
13
13
  export declare type Helpers = ReturnType<typeof getHelpers>;
@@ -0,0 +1,5 @@
1
+ import { SchemaHelper } from '../types';
2
+ export declare class SchemaHelperSQLite extends SchemaHelper {
3
+ preColumnDelete(): Promise<boolean>;
4
+ postColumnDelete(): Promise<void>;
5
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaHelperSQLite = void 0;
4
+ const types_1 = require("../types");
5
+ class SchemaHelperSQLite extends types_1.SchemaHelper {
6
+ async preColumnDelete() {
7
+ const foreignCheckStatus = (await this.knex.raw('PRAGMA foreign_keys'))[0].foreign_keys === 1;
8
+ if (foreignCheckStatus) {
9
+ await this.knex.raw('PRAGMA foreign_keys = OFF');
10
+ }
11
+ return foreignCheckStatus;
12
+ }
13
+ async postColumnDelete() {
14
+ await this.knex.raw('PRAGMA foreign_keys = ON');
15
+ }
16
+ }
17
+ exports.SchemaHelperSQLite = SchemaHelperSQLite;
@@ -2,6 +2,6 @@ export { SchemaHelperDefault as postgres } from './dialects/default';
2
2
  export { SchemaHelperCockroachDb as cockroachdb } from './dialects/cockroachdb';
3
3
  export { SchemaHelperDefault as redshift } from './dialects/default';
4
4
  export { SchemaHelperOracle as oracle } from './dialects/oracle';
5
- export { SchemaHelperDefault as sqlite } from './dialects/default';
5
+ export { SchemaHelperSQLite as sqlite } from './dialects/sqlite';
6
6
  export { SchemaHelperDefault as mysql } from './dialects/default';
7
7
  export { SchemaHelperDefault as mssql } from './dialects/default';
@@ -9,9 +9,9 @@ var default_2 = require("./dialects/default");
9
9
  Object.defineProperty(exports, "redshift", { enumerable: true, get: function () { return default_2.SchemaHelperDefault; } });
10
10
  var oracle_1 = require("./dialects/oracle");
11
11
  Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return oracle_1.SchemaHelperOracle; } });
12
+ var sqlite_1 = require("./dialects/sqlite");
13
+ Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return sqlite_1.SchemaHelperSQLite; } });
12
14
  var default_3 = require("./dialects/default");
13
- Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return default_3.SchemaHelperDefault; } });
15
+ Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return default_3.SchemaHelperDefault; } });
14
16
  var default_4 = require("./dialects/default");
15
- Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return default_4.SchemaHelperDefault; } });
16
- var default_5 = require("./dialects/default");
17
- Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return default_5.SchemaHelperDefault; } });
17
+ Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return default_4.SchemaHelperDefault; } });
@@ -21,5 +21,7 @@ export declare abstract class SchemaHelper extends DatabaseHelper {
21
21
  nullable?: boolean;
22
22
  default?: any;
23
23
  }>(table: string, column: string, options: Options, cb: (builder: Knex.CreateTableBuilder, column: string, options: Options) => Knex.ColumnBuilder): Promise<void>;
24
+ preColumnDelete(): Promise<boolean>;
25
+ postColumnDelete(): Promise<void>;
24
26
  }
25
27
  export {};
@@ -85,5 +85,11 @@ class SchemaHelper extends types_1.DatabaseHelper {
85
85
  await this.changeNullable(table, column, options.nullable);
86
86
  }
87
87
  }
88
+ async preColumnDelete() {
89
+ return false;
90
+ }
91
+ async postColumnDelete() {
92
+ return;
93
+ }
88
94
  }
89
95
  exports.SchemaHelper = SchemaHelper;
@@ -92,7 +92,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
92
92
  const columnsToSelectInternal = [];
93
93
  const nestedCollectionNodes = [];
94
94
  for (const child of children) {
95
- if (child.type === 'field') {
95
+ if (child.type === 'field' || child.type === 'functionField') {
96
96
  const fieldName = (0, strip_function_1.stripFunction)(child.name);
97
97
  if (columnsInCollection.includes(fieldName)) {
98
98
  columnsToSelectInternal.push(child.fieldKey);
@@ -121,7 +121,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
121
121
  const columnsToSelect = [...new Set(columnsToSelectInternal)];
122
122
  const fieldNodes = columnsToSelect.map((column) => {
123
123
  var _a;
124
- return (_a = children.find((childNode) => childNode.type === 'field' && childNode.fieldKey === column)) !== null && _a !== void 0 ? _a : {
124
+ return (_a = children.find((childNode) => (childNode.type === 'field' || childNode.type === 'functionField') && childNode.fieldKey === column)) !== null && _a !== void 0 ? _a : {
125
125
  type: 'field',
126
126
  name: column,
127
127
  fieldKey: column,
@@ -138,7 +138,7 @@ function getColumnPreprocessor(knex, schema, table) {
138
138
  alias = fieldNode.fieldKey;
139
139
  }
140
140
  let field;
141
- if (fieldNode.type === 'field') {
141
+ if (fieldNode.type === 'field' || fieldNode.type === 'functionField') {
142
142
  field = schema.collections[table].fields[(0, strip_function_1.stripFunction)(fieldNode.name)];
143
143
  }
144
144
  else {
@@ -147,6 +147,9 @@ function getColumnPreprocessor(knex, schema, table) {
147
147
  if ((_a = field === null || field === void 0 ? void 0 : field.type) === null || _a === void 0 ? void 0 : _a.startsWith('geometry')) {
148
148
  return helpers.st.asText(table, field.field);
149
149
  }
150
+ if (fieldNode.type === 'functionField') {
151
+ return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema, fieldNode.query);
152
+ }
150
153
  return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema);
151
154
  };
152
155
  }
@@ -299,7 +302,7 @@ function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem
299
302
  if (!nestedCollectionNodes[relatedCollection])
300
303
  nestedCollectionNodes[relatedCollection] = [];
301
304
  for (const child of ast.children[relatedCollection]) {
302
- if (child.type === 'field') {
305
+ if (child.type === 'field' || child.type === 'functionField') {
303
306
  fields[relatedCollection].push(child.name);
304
307
  }
305
308
  else {
@@ -325,7 +328,7 @@ function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem
325
328
  const nestedCollectionNodes = [];
326
329
  for (const child of ast.children) {
327
330
  fields.push(child.fieldKey);
328
- if (child.type !== 'field') {
331
+ if (child.type !== 'field' && child.type !== 'functionField') {
329
332
  nestedCollectionNodes.push(child);
330
333
  }
331
334
  }
package/dist/logger.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="qs" />
2
1
  import { RequestHandler } from 'express';
3
2
  import pino from 'pino';
4
3
  declare const logger: pino.Logger;
@@ -1,4 +1,3 @@
1
- /// <reference types="qs" />
2
1
  import { NextFunction, Request, Response } from 'express';
3
2
  /**
4
3
  * Verify the passed JWT and assign the user ID and role to `req`
@@ -10,15 +10,15 @@ const get_cache_headers_1 = require("../utils/get-cache-headers");
10
10
  const get_cache_key_1 = require("../utils/get-cache-key");
11
11
  const logger_1 = __importDefault(require("../logger"));
12
12
  const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next) => {
13
- var _a, _b;
13
+ var _a, _b, _c;
14
14
  const { cache } = (0, cache_1.getCache)();
15
- if (req.method.toLowerCase() !== 'get')
15
+ if (req.method.toLowerCase() !== 'get' && ((_a = req.path) === null || _a === void 0 ? void 0 : _a.startsWith('/graphql')) === false)
16
16
  return next();
17
17
  if (env_1.default.CACHE_ENABLED !== true)
18
18
  return next();
19
19
  if (!cache)
20
20
  return next();
21
- if (((_a = req.headers['cache-control']) === null || _a === void 0 ? void 0 : _a.includes('no-store')) || ((_b = req.headers['Cache-Control']) === null || _b === void 0 ? void 0 : _b.includes('no-store'))) {
21
+ if (((_b = req.headers['cache-control']) === null || _b === void 0 ? void 0 : _b.includes('no-store')) || ((_c = req.headers['Cache-Control']) === null || _c === void 0 ? void 0 : _c.includes('no-store'))) {
22
22
  if (env_1.default.CACHE_STATUS_HEADER)
23
23
  res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'MISS');
24
24
  return next();
@@ -47,14 +47,16 @@ exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
47
47
  graphqlErrors: [err],
48
48
  });
49
49
  }
50
+ const operationAST = (0, graphql_1.getOperationAST)(document, operationName);
50
51
  // You can only do `query` through GET
51
- if (req.method === 'GET') {
52
- const operationAST = (0, graphql_1.getOperationAST)(document, operationName);
53
- if ((operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation) !== 'query') {
54
- throw new exceptions_1.MethodNotAllowedException(`Can only perform a ${operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation} from a POST request.`, {
55
- allow: ['POST'],
56
- });
57
- }
52
+ if (req.method === 'GET' && (operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation) !== 'query') {
53
+ throw new exceptions_1.MethodNotAllowedException(`Can only perform a ${operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation} from a POST request.`, {
54
+ allow: ['POST'],
55
+ });
56
+ }
57
+ // Prevent caching responses when mutations are made
58
+ if ((operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation) === 'mutation') {
59
+ res.locals.cache = false;
58
60
  }
59
61
  res.locals.graphqlParams = { document, query, variables, operationName, contextValue: { req, res } };
60
62
  return next();
@@ -16,7 +16,7 @@ const get_date_formatted_1 = require("../utils/get-date-formatted");
16
16
  const get_string_byte_size_1 = require("../utils/get-string-byte-size");
17
17
  const bytes_1 = require("bytes");
18
18
  exports.respond = (0, async_handler_1.default)(async (req, res) => {
19
- var _a, _b, _c;
19
+ var _a, _b, _c, _d;
20
20
  const { cache } = (0, cache_1.getCache)();
21
21
  let exceedsMaxSize = false;
22
22
  if (env_1.default.CACHE_VALUE_MAX_SIZE !== false) {
@@ -24,7 +24,7 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
24
24
  const maxSize = (0, bytes_1.parse)(env_1.default.CACHE_VALUE_MAX_SIZE);
25
25
  exceedsMaxSize = valueSize > maxSize;
26
26
  }
27
- if (req.method.toLowerCase() === 'get' &&
27
+ if ((req.method.toLowerCase() === 'get' || ((_a = req.path) === null || _a === void 0 ? void 0 : _a.startsWith('/graphql'))) &&
28
28
  env_1.default.CACHE_ENABLED === true &&
29
29
  cache &&
30
30
  !req.sanitizedQuery.export &&
@@ -59,17 +59,17 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
59
59
  if (req.sanitizedQuery.export === 'json') {
60
60
  res.attachment(`${filename}.json`);
61
61
  res.set('Content-Type', 'application/json');
62
- return res.status(200).send(exportService.transform((_a = res.locals.payload) === null || _a === void 0 ? void 0 : _a.data, 'json'));
62
+ return res.status(200).send(exportService.transform((_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data, 'json'));
63
63
  }
64
64
  if (req.sanitizedQuery.export === 'xml') {
65
65
  res.attachment(`${filename}.xml`);
66
66
  res.set('Content-Type', 'text/xml');
67
- return res.status(200).send(exportService.transform((_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data, 'xml'));
67
+ return res.status(200).send(exportService.transform((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data, 'xml'));
68
68
  }
69
69
  if (req.sanitizedQuery.export === 'csv') {
70
70
  res.attachment(`${filename}.csv`);
71
71
  res.set('Content-Type', 'text/csv');
72
- return res.status(200).send(exportService.transform((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data, 'csv'));
72
+ return res.status(200).send(exportService.transform((_d = res.locals.payload) === null || _d === void 0 ? void 0 : _d.data, 'csv'));
73
73
  }
74
74
  }
75
75
  if (Buffer.isBuffer(res.locals.payload)) {
@@ -12,7 +12,7 @@ exports.default = (0, utils_1.defineOperationApi)({
12
12
  acc[header] = value;
13
13
  return acc;
14
14
  }, {});
15
- const result = await (0, axios_1.default)({ url, method, data: body, headers: customHeaders });
15
+ const result = await (0, axios_1.default)({ url: encodeURI(url), method, data: body, headers: customHeaders });
16
16
  return { status: result.status, statusText: result.statusText, headers: result.headers, data: result.data };
17
17
  },
18
18
  });
@@ -48,7 +48,7 @@ class AuthorizationService {
48
48
  collections.push(...ast.names.map((name) => ({ collection: name, field: ast.fieldKey })));
49
49
  for (const children of Object.values(ast.children)) {
50
50
  for (const nestedNode of children) {
51
- if (nestedNode.type !== 'field') {
51
+ if (nestedNode.type !== 'field' && nestedNode.type !== 'functionField') {
52
52
  collections.push(...getCollectionsFromAST(nestedNode));
53
53
  }
54
54
  }
@@ -60,7 +60,13 @@ class AuthorizationService {
60
60
  field: ast.type === 'root' ? null : ast.fieldKey,
61
61
  });
62
62
  for (const nestedNode of ast.children) {
63
- if (nestedNode.type !== 'field') {
63
+ if (nestedNode.type === 'functionField') {
64
+ collections.push({
65
+ collection: nestedNode.relatedCollection,
66
+ field: null,
67
+ });
68
+ }
69
+ else if (nestedNode.type !== 'field') {
64
70
  collections.push(...getCollectionsFromAST(nestedNode));
65
71
  }
66
72
  }
@@ -69,7 +75,7 @@ class AuthorizationService {
69
75
  }
70
76
  function validateFields(ast) {
71
77
  var _a, _b, _c;
72
- if (ast.type !== 'field') {
78
+ if (ast.type !== 'field' && ast.type !== 'functionField') {
73
79
  if (ast.type === 'a2o') {
74
80
  for (const [collection, children] of Object.entries(ast.children)) {
75
81
  checkFields(collection, children, (_b = (_a = ast.query) === null || _a === void 0 ? void 0 : _a[collection]) === null || _b === void 0 ? void 0 : _b.aggregate);
@@ -112,7 +118,7 @@ class AuthorizationService {
112
118
  function validateFilterPermissions(ast, schema, action, accountability) {
113
119
  var _a, _b, _c, _d, _e;
114
120
  let requiredFieldPermissions = {};
115
- if (ast.type !== 'field') {
121
+ if (ast.type !== 'field' && ast.type !== 'functionField') {
116
122
  if (ast.type === 'a2o') {
117
123
  for (const collection of Object.keys(ast.children)) {
118
124
  requiredFieldPermissions = mergeRequiredFieldPermissions(requiredFieldPermissions, extractRequiredFieldPermissions(collection, (_c = (_b = (_a = ast.query) === null || _a === void 0 ? void 0 : _a[collection]) === null || _b === void 0 ? void 0 : _b.filter) !== null && _c !== void 0 ? _c : {}));
@@ -290,7 +296,11 @@ class AuthorizationService {
290
296
  }
291
297
  }
292
298
  function applyFilters(ast, accountability) {
293
- if (ast.type !== 'field') {
299
+ if (ast.type === 'functionField') {
300
+ const collection = ast.relatedCollection;
301
+ updateFilterQuery(collection, ast.query);
302
+ }
303
+ else if (ast.type !== 'field') {
294
304
  if (ast.type === 'a2o') {
295
305
  const collections = Object.keys(ast.children);
296
306
  for (const collection of collections) {
@@ -351,6 +351,7 @@ class FieldsService {
351
351
  if (this.accountability && this.accountability.admin !== true) {
352
352
  throw new exceptions_1.ForbiddenException();
353
353
  }
354
+ const runPostColumnDelete = await this.helpers.schema.preColumnDelete();
354
355
  try {
355
356
  await emitter_1.default.emitFilter('fields.delete', [field], {
356
357
  collection: collection,
@@ -439,6 +440,9 @@ class FieldsService {
439
440
  });
440
441
  }
441
442
  finally {
443
+ if (runPostColumnDelete) {
444
+ await this.helpers.schema.postColumnDelete();
445
+ }
442
446
  if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
443
447
  await this.cache.clear();
444
448
  }