directus 9.15.1 → 9.17.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.
Files changed (107) hide show
  1. package/dist/__utils__/items-utils.d.ts +2 -0
  2. package/dist/__utils__/items-utils.js +36 -0
  3. package/dist/__utils__/schemas.d.ts +13 -0
  4. package/dist/__utils__/schemas.js +304 -0
  5. package/dist/__utils__/snapshots.d.ts +5 -0
  6. package/dist/__utils__/snapshots.js +897 -0
  7. package/dist/app.js +8 -7
  8. package/dist/auth/drivers/ldap.js +1 -0
  9. package/dist/auth/drivers/local.js +6 -0
  10. package/dist/auth/drivers/oauth2.js +1 -0
  11. package/dist/auth/drivers/openid.js +1 -0
  12. package/dist/cli/index.test.d.ts +1 -0
  13. package/dist/cli/index.test.js +58 -0
  14. package/dist/cli/utils/create-env/env-stub.liquid +6 -2
  15. package/dist/controllers/activity.js +1 -0
  16. package/dist/controllers/assets.js +20 -16
  17. package/dist/controllers/auth.js +6 -9
  18. package/dist/controllers/files.test.d.ts +1 -0
  19. package/dist/controllers/files.test.js +49 -0
  20. package/dist/controllers/server.js +0 -1
  21. package/dist/database/migrations/20220826A-add-origin-to-accountability.d.ts +3 -0
  22. package/dist/database/migrations/20220826A-add-origin-to-accountability.js +21 -0
  23. package/dist/database/migrations/run.test.d.ts +1 -0
  24. package/dist/database/migrations/run.test.js +92 -0
  25. package/dist/database/system-data/fields/activity.yaml +6 -0
  26. package/dist/database/system-data/fields/sessions.yaml +2 -0
  27. package/dist/env.js +15 -0
  28. package/dist/env.test.d.ts +8 -0
  29. package/dist/env.test.js +39 -0
  30. package/dist/extensions.d.ts +1 -0
  31. package/dist/extensions.js +16 -3
  32. package/dist/flows.js +28 -17
  33. package/dist/mailer.js +1 -0
  34. package/dist/middleware/authenticate.d.ts +1 -1
  35. package/dist/middleware/authenticate.js +1 -0
  36. package/dist/middleware/authenticate.test.d.ts +1 -0
  37. package/dist/middleware/authenticate.test.js +214 -0
  38. package/dist/middleware/extract-token.test.d.ts +1 -0
  39. package/dist/middleware/extract-token.test.js +60 -0
  40. package/dist/middleware/validate-batch.d.ts +1 -2
  41. package/dist/middleware/validate-batch.js +10 -13
  42. package/dist/middleware/validate-batch.test.d.ts +1 -0
  43. package/dist/middleware/validate-batch.test.js +82 -0
  44. package/dist/operations/exec/index.d.ts +5 -0
  45. package/dist/operations/exec/index.js +26 -0
  46. package/dist/operations/exec/index.test.d.ts +1 -0
  47. package/dist/operations/exec/index.test.js +95 -0
  48. package/dist/operations/notification/index.js +9 -6
  49. package/dist/operations/request/index.js +22 -3
  50. package/dist/operations/transform/index.d.ts +1 -1
  51. package/dist/operations/transform/index.js +1 -1
  52. package/dist/services/authentication.js +13 -3
  53. package/dist/services/files.js +3 -2
  54. package/dist/services/files.test.d.ts +1 -0
  55. package/dist/services/files.test.js +53 -0
  56. package/dist/services/flows.js +4 -0
  57. package/dist/services/graphql/index.d.ts +2 -2
  58. package/dist/services/graphql/index.js +78 -75
  59. package/dist/services/items.js +98 -42
  60. package/dist/services/items.test.d.ts +1 -0
  61. package/dist/services/items.test.js +765 -0
  62. package/dist/services/payload.d.ts +7 -4
  63. package/dist/services/payload.js +63 -12
  64. package/dist/services/payload.test.d.ts +1 -0
  65. package/dist/services/payload.test.js +94 -0
  66. package/dist/services/server.js +10 -7
  67. package/dist/services/shares.js +2 -1
  68. package/dist/services/specifications.test.d.ts +1 -0
  69. package/dist/services/specifications.test.js +96 -0
  70. package/dist/types/items.d.ts +11 -0
  71. package/dist/utils/apply-query.js +7 -3
  72. package/dist/utils/apply-snapshot.js +15 -0
  73. package/dist/utils/apply-snapshot.test.d.ts +1 -0
  74. package/dist/utils/apply-snapshot.test.js +305 -0
  75. package/dist/utils/async-handler.d.ts +2 -6
  76. package/dist/utils/async-handler.js +1 -13
  77. package/dist/utils/async-handler.test.d.ts +1 -0
  78. package/dist/utils/async-handler.test.js +18 -0
  79. package/dist/utils/calculate-field-depth.test.d.ts +1 -0
  80. package/dist/utils/calculate-field-depth.test.js +76 -0
  81. package/dist/utils/filter-items.test.d.ts +1 -0
  82. package/dist/utils/filter-items.test.js +60 -0
  83. package/dist/utils/get-cache-key.test.d.ts +1 -0
  84. package/dist/utils/get-cache-key.test.js +53 -0
  85. package/dist/utils/get-column-path.test.d.ts +1 -0
  86. package/dist/utils/get-column-path.test.js +136 -0
  87. package/dist/utils/get-config-from-env.test.d.ts +1 -0
  88. package/dist/utils/get-config-from-env.test.js +19 -0
  89. package/dist/utils/get-graphql-type.d.ts +1 -1
  90. package/dist/utils/get-graphql-type.js +4 -1
  91. package/dist/utils/get-os-info.d.ts +9 -0
  92. package/dist/utils/get-os-info.js +47 -0
  93. package/dist/utils/get-relation-info.test.d.ts +1 -0
  94. package/dist/utils/get-relation-info.test.js +88 -0
  95. package/dist/utils/get-relation-type.test.d.ts +1 -0
  96. package/dist/utils/get-relation-type.test.js +69 -0
  97. package/dist/utils/get-string-byte-size.test.d.ts +1 -0
  98. package/dist/utils/get-string-byte-size.test.js +8 -0
  99. package/dist/utils/is-directus-jwt.test.d.ts +1 -0
  100. package/dist/utils/is-directus-jwt.test.js +26 -0
  101. package/dist/utils/jwt.test.d.ts +1 -0
  102. package/dist/utils/jwt.test.js +36 -0
  103. package/dist/utils/merge-permissions.test.d.ts +1 -0
  104. package/dist/utils/merge-permissions.test.js +80 -0
  105. package/dist/utils/validate-keys.test.d.ts +1 -0
  106. package/dist/utils/validate-keys.test.js +97 -0
  107. package/package.json +14 -12
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vm2_1 = require("vm2");
7
+ const index_1 = __importDefault(require("./index"));
8
+ test('Rejects when modules are used without modules being allowed', async () => {
9
+ const testCode = `
10
+ const test = require('test');
11
+ `;
12
+ await expect(index_1.default.handler({ code: testCode }, {
13
+ data: {},
14
+ env: {
15
+ FLOWS_EXEC_ALLOWED_MODULES: '',
16
+ },
17
+ })).rejects.toEqual(new vm2_1.VMError("Cannot find module 'test'"));
18
+ });
19
+ test('Rejects when code contains syntax errors', async () => {
20
+ const testCode = `
21
+ ~~
22
+ `;
23
+ await expect(index_1.default.handler({ code: testCode }, {
24
+ data: {},
25
+ env: {
26
+ FLOWS_EXEC_ALLOWED_MODULES: '',
27
+ },
28
+ })).rejects.toEqual(new Error("Couldn't compile code: Unexpected end of input"));
29
+ });
30
+ test('Rejects when returned function does something illegal', async () => {
31
+ const testCode = `
32
+ module.exports = function() {
33
+ return a + b;
34
+ };
35
+ `;
36
+ await expect(index_1.default.handler({ code: testCode }, {
37
+ data: {},
38
+ env: {
39
+ FLOWS_EXEC_ALLOWED_MODULES: '',
40
+ },
41
+ })).rejects.toEqual(new ReferenceError('a is not defined'));
42
+ });
43
+ test("Rejects when code doesn't return valid function", async () => {
44
+ const testCode = `
45
+ module.exports = false;
46
+ `;
47
+ await expect(index_1.default.handler({ code: testCode }, {
48
+ data: {},
49
+ env: {
50
+ FLOWS_EXEC_ALLOWED_MODULES: '',
51
+ },
52
+ })).rejects.toEqual(new TypeError('fn is not a function'));
53
+ });
54
+ test('Rejects returned function throws errors', async () => {
55
+ const testCode = `
56
+ module.exports = function () {
57
+ throw new Error('test');
58
+ };
59
+ `;
60
+ await expect(index_1.default.handler({ code: testCode }, {
61
+ data: {},
62
+ env: {
63
+ FLOWS_EXEC_ALLOWED_MODULES: '',
64
+ },
65
+ })).rejects.toEqual(new Error('test'));
66
+ });
67
+ test('Executes function when valid', () => {
68
+ const testCode = `
69
+ module.exports = function (data) {
70
+ return { result: data.input + ' test' };
71
+ };
72
+ `;
73
+ expect(index_1.default.handler({ code: testCode }, {
74
+ data: {
75
+ input: 'start',
76
+ },
77
+ env: {
78
+ FLOWS_EXEC_ALLOWED_MODULES: '',
79
+ },
80
+ })).resolves.toEqual({ result: 'start test' });
81
+ });
82
+ test('Allows modules that are whitelisted', () => {
83
+ const testCode = `
84
+ const bytes = require('bytes');
85
+
86
+ module.exports = function (data) {
87
+ return { result: bytes(1000) };
88
+ };
89
+ `;
90
+ expect(index_1.default.handler({ code: testCode }, {
91
+ env: {
92
+ FLOWS_EXEC_ALLOWED_MODULES: 'bytes',
93
+ },
94
+ })).resolves.toEqual({ result: '1000B' });
95
+ });
@@ -6,7 +6,6 @@ const get_accountability_for_role_1 = require("../../utils/get-accountability-fo
6
6
  exports.default = (0, utils_1.defineOperationApi)({
7
7
  id: 'notification',
8
8
  handler: async ({ recipient, subject, message, permissions }, { accountability, database, getSchema }) => {
9
- var _a;
10
9
  const schema = await getSchema({ database });
11
10
  let customAccountability;
12
11
  if (!permissions || permissions === '$trigger') {
@@ -27,12 +26,16 @@ exports.default = (0, utils_1.defineOperationApi)({
27
26
  knex: database,
28
27
  });
29
28
  const messageString = message ? (0, utils_1.optionToString)(message) : null;
30
- const result = await notificationsService.createOne({
31
- recipient,
32
- sender: (_a = customAccountability === null || customAccountability === void 0 ? void 0 : customAccountability.user) !== null && _a !== void 0 ? _a : null,
33
- subject,
34
- message: messageString,
29
+ const payload = (0, utils_1.toArray)(recipient).map((userId) => {
30
+ var _a;
31
+ return {
32
+ recipient: userId,
33
+ sender: (_a = customAccountability === null || customAccountability === void 0 ? void 0 : customAccountability.user) !== null && _a !== void 0 ? _a : null,
34
+ subject,
35
+ message: messageString,
36
+ };
35
37
  });
38
+ const result = await notificationsService.createMany(payload);
36
39
  return result;
37
40
  },
38
41
  });
@@ -5,14 +5,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const utils_1 = require("@directus/shared/utils");
7
7
  const axios_1 = __importDefault(require("axios"));
8
+ const encodeurl_1 = __importDefault(require("encodeurl"));
8
9
  exports.default = (0, utils_1.defineOperationApi)({
9
10
  id: 'request',
10
11
  handler: async ({ url, method, body, headers }) => {
11
- const customHeaders = headers === null || headers === void 0 ? void 0 : headers.reduce((acc, { header, value }) => {
12
+ var _a;
13
+ const customHeaders = (_a = headers === null || headers === void 0 ? void 0 : headers.reduce((acc, { header, value }) => {
12
14
  acc[header] = value;
13
15
  return acc;
14
- }, {});
15
- const result = await (0, axios_1.default)({ url: encodeURI(url), method, data: body, headers: customHeaders });
16
+ }, {})) !== null && _a !== void 0 ? _a : {};
17
+ if (!customHeaders['Content-Type'] && isValidJSON(body)) {
18
+ customHeaders['Content-Type'] = 'application/json';
19
+ }
20
+ const result = await (0, axios_1.default)({
21
+ url: (0, encodeurl_1.default)(url),
22
+ method,
23
+ data: body,
24
+ headers: customHeaders,
25
+ });
16
26
  return { status: result.status, statusText: result.statusText, headers: result.headers, data: result.data };
27
+ function isValidJSON(value) {
28
+ try {
29
+ (0, utils_1.parseJSON)(value);
30
+ return true;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
17
36
  },
18
37
  });
@@ -1,5 +1,5 @@
1
1
  declare type Options = {
2
- json: string;
2
+ json: string | Record<string, any>;
3
3
  };
4
4
  declare const _default: import("@directus/shared/types").OperationApiConfig<Options>;
5
5
  export default _default;
@@ -4,6 +4,6 @@ const utils_1 = require("@directus/shared/utils");
4
4
  exports.default = (0, utils_1.defineOperationApi)({
5
5
  id: 'transform',
6
6
  handler: ({ json }) => {
7
- return (0, utils_1.parseJSON)(json);
7
+ return (0, utils_1.optionToObject)(json);
8
8
  },
9
9
  });
@@ -36,15 +36,23 @@ class AuthenticationService {
36
36
  * to handle password existence checks elsewhere
37
37
  */
38
38
  async login(providerName = constants_1.DEFAULT_AUTH_PROVIDER, payload, otp) {
39
- var _a, _b;
40
- const STALL_TIME = 100;
39
+ var _a, _b, _c;
40
+ const STALL_TIME = env_1.default.LOGIN_STALL_TIME;
41
41
  const timeStart = perf_hooks_1.performance.now();
42
42
  const provider = (0, auth_1.getAuthProvider)(providerName);
43
+ let userId;
44
+ try {
45
+ userId = await provider.getUserID((0, lodash_1.cloneDeep)(payload));
46
+ }
47
+ catch (err) {
48
+ await (0, stall_1.stall)(STALL_TIME, timeStart);
49
+ throw err;
50
+ }
43
51
  const user = await this.knex
44
52
  .select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'r.admin_access', 'r.app_access', 'u.tfa_secret', 'u.provider', 'u.external_identifier', 'u.auth_data')
45
53
  .from('directus_users as u')
46
54
  .leftJoin('directus_roles as r', 'u.role', 'r.id')
47
- .where('u.id', await provider.getUserID((0, lodash_1.cloneDeep)(payload)))
55
+ .where('u.id', userId)
48
56
  .first();
49
57
  const updatedPayload = await emitter_1.default.emitFilter('auth.login', payload, {
50
58
  status: 'pending',
@@ -151,6 +159,7 @@ class AuthenticationService {
151
159
  expires: refreshTokenExpiration,
152
160
  ip: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.ip,
153
161
  user_agent: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.userAgent,
162
+ origin: (_c = this.accountability) === null || _c === void 0 ? void 0 : _c.origin,
154
163
  });
155
164
  await this.knex('directus_sessions').delete().where('expires', '<', new Date());
156
165
  if (this.accountability) {
@@ -159,6 +168,7 @@ class AuthenticationService {
159
168
  user: user.id,
160
169
  ip: this.accountability.ip,
161
170
  user_agent: this.accountability.userAgent,
171
+ origin: this.accountability.origin,
162
172
  collection: 'directus_users',
163
173
  item: user.id,
164
174
  });
@@ -46,6 +46,7 @@ const utils_1 = require("@directus/shared/utils");
46
46
  const items_1 = require("./items");
47
47
  const net_1 = __importDefault(require("net"));
48
48
  const os_1 = __importDefault(require("os"));
49
+ const encodeurl_1 = __importDefault(require("encodeurl"));
49
50
  const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
50
51
  class FilesService extends items_1.ItemsService {
51
52
  constructor(options) {
@@ -229,7 +230,7 @@ class FilesService extends items_1.ItemsService {
229
230
  }
230
231
  let fileResponse;
231
232
  try {
232
- fileResponse = await axios_1.default.get(importURL, {
233
+ fileResponse = await axios_1.default.get((0, encodeurl_1.default)(importURL), {
233
234
  responseType: 'stream',
234
235
  });
235
236
  }
@@ -240,7 +241,7 @@ class FilesService extends items_1.ItemsService {
240
241
  });
241
242
  }
242
243
  const parsedURL = url_1.default.parse(fileResponse.request.res.responseUrl);
243
- const filename = path_1.default.basename(parsedURL.pathname);
244
+ const filename = decodeURI(path_1.default.basename(parsedURL.pathname));
244
245
  const payload = {
245
246
  filename_download: filename,
246
247
  storage: (0, utils_1.toArray)(env_1.default.STORAGE_LOCATIONS)[0],
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const exifr_1 = __importDefault(require("exifr"));
7
+ const knex_1 = __importDefault(require("knex"));
8
+ const knex_mock_client_1 = require("knex-mock-client");
9
+ const _1 = require(".");
10
+ jest.mock('exifr');
11
+ jest.mock('../../src/database/index', () => {
12
+ return { getDatabaseClient: jest.fn().mockReturnValue('postgres') };
13
+ });
14
+ jest.requireMock('../../src/database/index');
15
+ describe('Integration Tests', () => {
16
+ let db;
17
+ let tracker;
18
+ beforeAll(async () => {
19
+ db = (0, knex_1.default)({ client: knex_mock_client_1.MockClient });
20
+ tracker = (0, knex_mock_client_1.getTracker)();
21
+ });
22
+ afterEach(() => {
23
+ tracker.reset();
24
+ });
25
+ describe('Services / Files', () => {
26
+ describe('getMetadata', () => {
27
+ let service;
28
+ let exifrParseSpy;
29
+ const sampleMetadata = {
30
+ CustomTagA: 'value a',
31
+ CustomTagB: 'value b',
32
+ CustomTagC: 'value c',
33
+ };
34
+ beforeEach(() => {
35
+ exifrParseSpy = jest.spyOn(exifr_1.default, 'parse');
36
+ service = new _1.FilesService({
37
+ knex: db,
38
+ schema: { collections: {}, relations: [] },
39
+ });
40
+ });
41
+ it('accepts allowlist metadata tags', async () => {
42
+ exifrParseSpy.mockReturnValue(Promise.resolve({ ...sampleMetadata }));
43
+ const bufferContent = 'file buffer content';
44
+ const allowList = ['CustomTagB', 'CustomTagA'];
45
+ const metadata = await service.getMetadata(bufferContent, allowList);
46
+ expect(exifrParseSpy).toHaveBeenCalled();
47
+ expect(metadata.metadata.CustomTagA).toStrictEqual(sampleMetadata.CustomTagA);
48
+ expect(metadata.metadata.CustomTagB).toStrictEqual(sampleMetadata.CustomTagB);
49
+ expect(metadata.metadata.CustomTagC).toBeUndefined();
50
+ });
51
+ });
52
+ });
53
+ });
@@ -39,12 +39,16 @@ class FlowsService extends items_1.ItemsService {
39
39
  }
40
40
  async deleteOne(key, opts) {
41
41
  const flowManager = (0, flows_1.getFlowManager)();
42
+ // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
43
+ await this.knex('directus_operations').update({ resolve: null, reject: null }).where('flow', key);
42
44
  const result = await super.deleteOne(key, opts);
43
45
  await flowManager.reload();
44
46
  return result;
45
47
  }
46
48
  async deleteMany(keys, opts) {
47
49
  const flowManager = (0, flows_1.getFlowManager)();
50
+ // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
51
+ await this.knex('directus_operations').update({ resolve: null, reject: null }).whereIn('flow', keys);
48
52
  const result = await super.deleteMany(keys, opts);
49
53
  await flowManager.reload();
50
54
  return result;
@@ -1,6 +1,6 @@
1
1
  import { BaseException } from '@directus/shared/exceptions';
2
2
  import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
3
- import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, ObjectFieldNode, SelectionNode } from 'graphql';
3
+ import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, SelectionNode } from 'graphql';
4
4
  import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
5
5
  import { Knex } from 'knex';
6
6
  import { AbstractServiceOptions, GraphQLParams, Item } from '../../types';
@@ -44,7 +44,7 @@ export declare class GraphQLService {
44
44
  * In order to do that, we'll parse over all ArgumentNodes and ObjectFieldNodes to manually recreate an object structure
45
45
  * of arguments
46
46
  */
47
- parseArgs(args: readonly ArgumentNode[] | readonly ObjectFieldNode[], variableValues: GraphQLResolveInfo['variableValues']): Record<string, any>;
47
+ parseArgs(args: readonly ArgumentNode[], variableValues: GraphQLResolveInfo['variableValues']): Record<string, any>;
48
48
  /**
49
49
  * Get a Directus Query object from the parsed arguments (rawQuery) and GraphQL AST selectionSet. Converts SelectionSet into
50
50
  * Directus' `fields` query for use in the resolver. Also applies variables where appropriate.
@@ -274,7 +274,7 @@ class GraphQLService {
274
274
  CollectionTypes[collection.collection] = schemaComposer.createObjectTC({
275
275
  name: action === 'read' ? collection.collection : `${action}_${collection.collection}`,
276
276
  fields: Object.values(collection.fields).reduce((acc, field) => {
277
- let type = (0, get_graphql_type_1.getGraphQLType)(field.type);
277
+ let type = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
278
278
  // GraphQL doesn't differentiate between not-null and has-to-be-submitted. We
279
279
  // can't non-null in update, as that would require every not-nullable field to be
280
280
  // submitted on updates
@@ -293,41 +293,43 @@ class GraphQLService {
293
293
  return obj[field.field];
294
294
  },
295
295
  };
296
- if (field.type === 'date') {
297
- acc[`${field.field}_func`] = {
298
- type: DateFunctions,
299
- resolve: (obj) => {
300
- const funcFields = Object.keys(DateFunctions.getFields()).map((key) => `${field.field}_${key}`);
301
- return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
302
- },
303
- };
304
- }
305
- if (field.type === 'time') {
306
- acc[`${field.field}_func`] = {
307
- type: TimeFunctions,
308
- resolve: (obj) => {
309
- const funcFields = Object.keys(TimeFunctions.getFields()).map((key) => `${field.field}_${key}`);
310
- return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
311
- },
312
- };
313
- }
314
- if (field.type === 'dateTime' || field.type === 'timestamp') {
315
- acc[`${field.field}_func`] = {
316
- type: DateTimeFunctions,
317
- resolve: (obj) => {
318
- const funcFields = Object.keys(DateTimeFunctions.getFields()).map((key) => `${field.field}_${key}`);
319
- return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
320
- },
321
- };
322
- }
323
- if (field.type === 'json' || field.type === 'alias') {
324
- acc[`${field.field}_func`] = {
325
- type: CountFunctions,
326
- resolve: (obj) => {
327
- const funcFields = Object.keys(CountFunctions.getFields()).map((key) => `${field.field}_${key}`);
328
- return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
329
- },
330
- };
296
+ if (action === 'read') {
297
+ if (field.type === 'date') {
298
+ acc[`${field.field}_func`] = {
299
+ type: DateFunctions,
300
+ resolve: (obj) => {
301
+ const funcFields = Object.keys(DateFunctions.getFields()).map((key) => `${field.field}_${key}`);
302
+ return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
303
+ },
304
+ };
305
+ }
306
+ if (field.type === 'time') {
307
+ acc[`${field.field}_func`] = {
308
+ type: TimeFunctions,
309
+ resolve: (obj) => {
310
+ const funcFields = Object.keys(TimeFunctions.getFields()).map((key) => `${field.field}_${key}`);
311
+ return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
312
+ },
313
+ };
314
+ }
315
+ if (field.type === 'dateTime' || field.type === 'timestamp') {
316
+ acc[`${field.field}_func`] = {
317
+ type: DateTimeFunctions,
318
+ resolve: (obj) => {
319
+ const funcFields = Object.keys(DateTimeFunctions.getFields()).map((key) => `${field.field}_${key}`);
320
+ return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
321
+ },
322
+ };
323
+ }
324
+ if (field.type === 'json' || field.type === 'alias') {
325
+ acc[`${field.field}_func`] = {
326
+ type: CountFunctions,
327
+ resolve: (obj) => {
328
+ const funcFields = Object.keys(CountFunctions.getFields()).map((key) => `${field.field}_${key}`);
329
+ return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
330
+ },
331
+ };
332
+ }
331
333
  }
332
334
  return acc;
333
335
  }, {}),
@@ -639,7 +641,7 @@ class GraphQLService {
639
641
  ReadableCollectionFilterTypes[collection.collection] = schemaComposer.createInputTC({
640
642
  name: `${collection.collection}_filter`,
641
643
  fields: Object.values(collection.fields).reduce((acc, field) => {
642
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
644
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
643
645
  let filterOperatorType;
644
646
  switch (graphqlType) {
645
647
  case graphql_1.GraphQLBoolean:
@@ -692,7 +694,7 @@ class GraphQLService {
692
694
  AggregatedFields[collection.collection] = schemaComposer.createObjectTC({
693
695
  name: `${collection.collection}_aggregated_fields`,
694
696
  fields: Object.values(collection.fields).reduce((acc, field) => {
695
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
697
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
696
698
  switch (graphqlType) {
697
699
  case graphql_1.GraphQLInt:
698
700
  case graphql_1.GraphQLFloat:
@@ -736,7 +738,7 @@ class GraphQLService {
736
738
  },
737
739
  };
738
740
  const hasNumericAggregates = Object.values(collection.fields).some((field) => {
739
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
741
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
740
742
  if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
741
743
  return true;
742
744
  }
@@ -1207,38 +1209,29 @@ class GraphQLService {
1207
1209
  parseArgs(args, variableValues) {
1208
1210
  if (!args || args.length === 0)
1209
1211
  return {};
1210
- const parseObjectValue = (arg) => {
1211
- return this.parseArgs(arg.fields, variableValues);
1212
- };
1213
- const argsObject = {};
1214
- for (const argument of args) {
1215
- if (argument.value.kind === 'ObjectValue') {
1216
- argsObject[argument.name.value] = parseObjectValue(argument.value);
1217
- }
1218
- else if (argument.value.kind === 'Variable') {
1219
- argsObject[argument.name.value] = variableValues[argument.value.name.value];
1220
- }
1221
- else if (argument.value.kind === 'ListValue') {
1222
- const values = [];
1223
- for (const valueNode of argument.value.values) {
1224
- if (valueNode.kind === 'ObjectValue') {
1225
- values.push(this.parseArgs(valueNode.fields, variableValues));
1226
- }
1227
- else {
1228
- if (valueNode.kind === 'Variable') {
1229
- values.push(variableValues[valueNode.name.value]);
1230
- }
1231
- else {
1232
- values.push(valueNode.value);
1233
- }
1234
- }
1235
- }
1236
- argsObject[argument.name.value] = values;
1237
- }
1238
- else {
1239
- argsObject[argument.name.value] = argument.value.value;
1212
+ const parse = (node) => {
1213
+ switch (node.kind) {
1214
+ case 'Variable':
1215
+ return variableValues[node.name.value];
1216
+ case 'ListValue':
1217
+ return node.values.map(parse);
1218
+ case 'ObjectValue':
1219
+ return Object.fromEntries(node.fields.map((node) => [node.name.value, parse(node.value)]));
1220
+ case 'NullValue':
1221
+ return null;
1222
+ case 'StringValue':
1223
+ return String(node.value);
1224
+ case 'IntValue':
1225
+ case 'FloatValue':
1226
+ return Number(node.value);
1227
+ case 'BooleanValue':
1228
+ return Boolean(node.value);
1229
+ case 'EnumValue':
1230
+ default:
1231
+ return node.value;
1240
1232
  }
1241
- }
1233
+ };
1234
+ const argsObject = Object.fromEntries(args.map((arg) => [arg.name.value, parse(arg.value)]));
1242
1235
  return argsObject;
1243
1236
  }
1244
1237
  /**
@@ -1624,6 +1617,7 @@ class GraphQLService {
1624
1617
  const accountability = {
1625
1618
  ip: req === null || req === void 0 ? void 0 : req.ip,
1626
1619
  userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
1620
+ origin: req === null || req === void 0 ? void 0 : req.get('origin'),
1627
1621
  role: null,
1628
1622
  };
1629
1623
  const authenticationService = new authentication_1.AuthenticationService({
@@ -1658,6 +1652,7 @@ class GraphQLService {
1658
1652
  const accountability = {
1659
1653
  ip: req === null || req === void 0 ? void 0 : req.ip,
1660
1654
  userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
1655
+ origin: req === null || req === void 0 ? void 0 : req.get('origin'),
1661
1656
  role: null,
1662
1657
  };
1663
1658
  const authenticationService = new authentication_1.AuthenticationService({
@@ -1694,6 +1689,7 @@ class GraphQLService {
1694
1689
  const accountability = {
1695
1690
  ip: req === null || req === void 0 ? void 0 : req.ip,
1696
1691
  userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
1692
+ origin: req === null || req === void 0 ? void 0 : req.get('origin'),
1697
1693
  role: null,
1698
1694
  };
1699
1695
  const authenticationService = new authentication_1.AuthenticationService({
@@ -1718,6 +1714,7 @@ class GraphQLService {
1718
1714
  const accountability = {
1719
1715
  ip: req === null || req === void 0 ? void 0 : req.ip,
1720
1716
  userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
1717
+ origin: req === null || req === void 0 ? void 0 : req.get('origin'),
1721
1718
  role: null,
1722
1719
  };
1723
1720
  const service = new users_1.UsersService({ accountability, schema: this.schema });
@@ -1742,6 +1739,7 @@ class GraphQLService {
1742
1739
  const accountability = {
1743
1740
  ip: req === null || req === void 0 ? void 0 : req.ip,
1744
1741
  userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
1742
+ origin: req === null || req === void 0 ? void 0 : req.get('origin'),
1745
1743
  role: null,
1746
1744
  };
1747
1745
  const service = new users_1.UsersService({ accountability, schema: this.schema });
@@ -1902,7 +1900,9 @@ class GraphQLService {
1902
1900
  name: 'directus_collections_meta',
1903
1901
  fields: Object.values(schema.read.collections['directus_collections'].fields).reduce((acc, field) => {
1904
1902
  acc[field.field] = {
1905
- type: field.nullable ? (0, get_graphql_type_1.getGraphQLType)(field.type) : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type)),
1903
+ type: field.nullable
1904
+ ? (0, get_graphql_type_1.getGraphQLType)(field.type, field.special)
1905
+ : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type, field.special)),
1906
1906
  description: field.note,
1907
1907
  };
1908
1908
  return acc;
@@ -1951,7 +1951,9 @@ class GraphQLService {
1951
1951
  name: 'directus_fields_meta',
1952
1952
  fields: Object.values(schema.read.collections['directus_fields'].fields).reduce((acc, field) => {
1953
1953
  acc[field.field] = {
1954
- type: field.nullable ? (0, get_graphql_type_1.getGraphQLType)(field.type) : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type)),
1954
+ type: field.nullable
1955
+ ? (0, get_graphql_type_1.getGraphQLType)(field.type, field.special)
1956
+ : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type, field.special)),
1955
1957
  description: field.note,
1956
1958
  };
1957
1959
  return acc;
@@ -2038,7 +2040,7 @@ class GraphQLService {
2038
2040
  name: 'directus_relations_meta',
2039
2041
  fields: Object.values(schema.read.collections['directus_relations'].fields).reduce((acc, field) => {
2040
2042
  acc[field.field] = {
2041
- type: (0, get_graphql_type_1.getGraphQLType)(field.type),
2043
+ type: (0, get_graphql_type_1.getGraphQLType)(field.type, field.special),
2042
2044
  description: field.note,
2043
2045
  };
2044
2046
  return acc;
@@ -2308,7 +2310,7 @@ class GraphQLService {
2308
2310
  comment: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLString),
2309
2311
  },
2310
2312
  resolve: async (_, args, __, info) => {
2311
- var _a, _b, _c, _d, _e;
2313
+ var _a, _b, _c, _d, _e, _f;
2312
2314
  const service = new activity_1.ActivityService({
2313
2315
  accountability: this.accountability,
2314
2316
  schema: this.schema,
@@ -2319,9 +2321,10 @@ class GraphQLService {
2319
2321
  user: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.user,
2320
2322
  ip: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.ip,
2321
2323
  user_agent: (_c = this.accountability) === null || _c === void 0 ? void 0 : _c.userAgent,
2324
+ origin: (_d = this.accountability) === null || _d === void 0 ? void 0 : _d.origin,
2322
2325
  });
2323
2326
  if ('directus_activity' in ReadCollectionTypes) {
2324
- const selections = this.replaceFragmentsInSelections((_e = (_d = info.fieldNodes[0]) === null || _d === void 0 ? void 0 : _d.selectionSet) === null || _e === void 0 ? void 0 : _e.selections, info.fragments);
2327
+ const selections = this.replaceFragmentsInSelections((_f = (_e = info.fieldNodes[0]) === null || _e === void 0 ? void 0 : _e.selectionSet) === null || _f === void 0 ? void 0 : _f.selections, info.fragments);
2325
2328
  const query = this.getQuery(args, selections || [], info.variableValues);
2326
2329
  return await service.readOne(primaryKey, query);
2327
2330
  }