@directus/api 25.0.0 → 26.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/app.js +3 -3
  2. package/dist/auth/drivers/oauth2.d.ts +2 -0
  3. package/dist/auth/drivers/oauth2.js +40 -2
  4. package/dist/auth/drivers/openid.js +8 -1
  5. package/dist/controllers/access.js +2 -2
  6. package/dist/controllers/comments.js +2 -2
  7. package/dist/controllers/dashboards.js +2 -2
  8. package/dist/controllers/files.js +2 -2
  9. package/dist/controllers/flows.js +2 -2
  10. package/dist/controllers/folders.js +2 -2
  11. package/dist/controllers/items.js +2 -2
  12. package/dist/controllers/notifications.js +2 -2
  13. package/dist/controllers/operations.js +2 -2
  14. package/dist/controllers/panels.js +2 -2
  15. package/dist/controllers/permissions.js +2 -2
  16. package/dist/controllers/policies.js +2 -2
  17. package/dist/controllers/presets.js +2 -2
  18. package/dist/controllers/roles.js +2 -2
  19. package/dist/controllers/shares.js +2 -2
  20. package/dist/controllers/translations.js +2 -2
  21. package/dist/controllers/users.js +2 -2
  22. package/dist/controllers/utils.js +8 -3
  23. package/dist/controllers/versions.js +2 -2
  24. package/dist/controllers/webhooks.js +1 -1
  25. package/dist/database/helpers/capabilities/dialects/default.d.ts +3 -0
  26. package/dist/database/helpers/capabilities/dialects/default.js +3 -0
  27. package/dist/database/helpers/capabilities/dialects/mysql.d.ts +4 -0
  28. package/dist/database/helpers/capabilities/dialects/mysql.js +9 -0
  29. package/dist/database/helpers/capabilities/dialects/postgres.d.ts +5 -0
  30. package/dist/database/helpers/capabilities/dialects/postgres.js +14 -0
  31. package/dist/database/helpers/capabilities/index.d.ts +7 -0
  32. package/dist/database/helpers/capabilities/index.js +7 -0
  33. package/dist/database/helpers/capabilities/types.d.ts +11 -0
  34. package/dist/database/helpers/capabilities/types.js +15 -0
  35. package/dist/database/helpers/index.d.ts +2 -0
  36. package/dist/database/helpers/index.js +2 -0
  37. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -2
  38. package/dist/database/helpers/schema/dialects/cockroachdb.js +0 -4
  39. package/dist/database/helpers/schema/dialects/postgres.d.ts +1 -2
  40. package/dist/database/helpers/schema/dialects/postgres.js +0 -4
  41. package/dist/database/index.js +1 -1
  42. package/dist/database/migrations/20250224A-visual-editor.d.ts +3 -0
  43. package/dist/database/migrations/20250224A-visual-editor.js +35 -0
  44. package/dist/database/run-ast/lib/get-db-query.js +16 -4
  45. package/dist/logger/index.js +3 -3
  46. package/dist/middleware/sanitize-query.js +17 -7
  47. package/dist/middleware/validate-batch.js +1 -1
  48. package/dist/operations/item-delete/index.js +1 -1
  49. package/dist/operations/item-read/index.js +1 -1
  50. package/dist/operations/item-update/index.js +1 -1
  51. package/dist/permissions/lib/fetch-permissions.js +6 -4
  52. package/dist/permissions/modules/process-ast/utils/context-has-dynamic-variables.d.ts +2 -0
  53. package/dist/permissions/modules/process-ast/utils/context-has-dynamic-variables.js +3 -0
  54. package/dist/permissions/modules/process-payload/process-payload.d.ts +1 -0
  55. package/dist/permissions/modules/process-payload/process-payload.js +13 -4
  56. package/dist/permissions/types.d.ts +2 -1
  57. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +3 -2
  58. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +24 -5
  59. package/dist/permissions/utils/fetch-dynamic-variable-data.d.ts +9 -0
  60. package/dist/permissions/utils/{fetch-dynamic-variable-context.js → fetch-dynamic-variable-data.js} +11 -12
  61. package/dist/rate-limiter.js +1 -1
  62. package/dist/services/assets.js +12 -2
  63. package/dist/services/authentication.js +2 -2
  64. package/dist/services/collections.js +39 -3
  65. package/dist/services/fields/build-collection-and-field-relations.d.ts +21 -0
  66. package/dist/services/fields/build-collection-and-field-relations.js +55 -0
  67. package/dist/services/fields/get-collection-meta-updates.d.ts +11 -0
  68. package/dist/services/fields/get-collection-meta-updates.js +72 -0
  69. package/dist/services/fields/get-collection-relation-list.d.ts +5 -0
  70. package/dist/services/fields/get-collection-relation-list.js +28 -0
  71. package/dist/services/fields.js +17 -12
  72. package/dist/services/graphql/resolvers/get-collection-type.d.ts +3 -0
  73. package/dist/services/graphql/resolvers/get-collection-type.js +34 -0
  74. package/dist/services/graphql/resolvers/get-field-type.d.ts +3 -0
  75. package/dist/services/graphql/resolvers/get-field-type.js +51 -0
  76. package/dist/services/graphql/resolvers/get-relation-type.d.ts +3 -0
  77. package/dist/services/graphql/resolvers/get-relation-type.js +39 -0
  78. package/dist/services/graphql/resolvers/mutation.js +1 -1
  79. package/dist/services/graphql/resolvers/query.js +4 -4
  80. package/dist/services/graphql/resolvers/system-admin.d.ts +2 -2
  81. package/dist/services/graphql/resolvers/system-admin.js +207 -199
  82. package/dist/services/graphql/resolvers/system.d.ts +1 -7
  83. package/dist/services/graphql/resolvers/system.js +12 -113
  84. package/dist/services/graphql/schema/index.js +1 -1
  85. package/dist/services/graphql/schema/parse-query.d.ts +2 -2
  86. package/dist/services/graphql/schema/parse-query.js +6 -6
  87. package/dist/services/graphql/schema/read.d.ts +2 -2
  88. package/dist/services/graphql/schema/read.js +86 -2
  89. package/dist/services/graphql/schema-cache.d.ts +2 -2
  90. package/dist/services/graphql/schema-cache.js +1 -3
  91. package/dist/services/graphql/subscription.d.ts +3 -3
  92. package/dist/services/graphql/subscription.js +3 -3
  93. package/dist/services/graphql/utils/{aggrgate-query.d.ts → aggregate-query.d.ts} +2 -2
  94. package/dist/services/graphql/utils/{aggrgate-query.js → aggregate-query.js} +3 -3
  95. package/dist/services/items.d.ts +1 -0
  96. package/dist/services/items.js +30 -16
  97. package/dist/services/meta.js +4 -2
  98. package/dist/services/payload.d.ts +1 -0
  99. package/dist/services/payload.js +32 -17
  100. package/dist/services/shares.js +1 -1
  101. package/dist/services/specifications.js +10 -5
  102. package/dist/services/tus/lockers.d.ts +1 -1
  103. package/dist/services/tus/lockers.js +6 -5
  104. package/dist/services/tus/server.js +24 -0
  105. package/dist/services/users.js +1 -0
  106. package/dist/types/services.d.ts +2 -0
  107. package/dist/utils/apply-query.d.ts +1 -0
  108. package/dist/utils/apply-query.js +42 -31
  109. package/dist/utils/generate-hash.js +1 -1
  110. package/dist/utils/get-config-from-env.d.ts +6 -1
  111. package/dist/utils/get-config-from-env.js +16 -11
  112. package/dist/utils/get-graphql-type.js +3 -1
  113. package/dist/utils/is-login-redirect-allowed.js +2 -0
  114. package/dist/utils/redact-object.js +5 -1
  115. package/dist/utils/sanitize-query.d.ts +5 -2
  116. package/dist/utils/sanitize-query.js +34 -9
  117. package/dist/websocket/controllers/base.d.ts +2 -2
  118. package/dist/websocket/handlers/items.js +4 -4
  119. package/dist/websocket/handlers/subscribe.js +2 -2
  120. package/dist/websocket/messages.d.ts +7 -7
  121. package/dist/websocket/messages.js +1 -1
  122. package/package.json +58 -58
  123. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +0 -8
@@ -0,0 +1,21 @@
1
+ import type { Relation } from '@directus/types';
2
+ /**
3
+ * Builds two maps where collectionRelationTree is a map of collection to related collections
4
+ * and fieldToCollectionList is a map of field:collection to related collection.
5
+ *
6
+ * @example
7
+ * returns {
8
+ * collectionRelationTree: new Map([
9
+ * ['B', new Set(['A'])],
10
+ * ['A', new Set(['B'])],
11
+ * ]),
12
+ * fieldToCollectionList: new Map([
13
+ * ['B:b', 'A'],
14
+ * ['A:a', 'B'],
15
+ * ]),
16
+ * }
17
+ */
18
+ export declare function buildCollectionAndFieldRelations(relations: Relation[]): {
19
+ collectionRelationTree: Map<string, Set<string>>;
20
+ fieldToCollectionList: Map<string, string>;
21
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Builds two maps where collectionRelationTree is a map of collection to related collections
3
+ * and fieldToCollectionList is a map of field:collection to related collection.
4
+ *
5
+ * @example
6
+ * returns {
7
+ * collectionRelationTree: new Map([
8
+ * ['B', new Set(['A'])],
9
+ * ['A', new Set(['B'])],
10
+ * ]),
11
+ * fieldToCollectionList: new Map([
12
+ * ['B:b', 'A'],
13
+ * ['A:a', 'B'],
14
+ * ]),
15
+ * }
16
+ */
17
+ export function buildCollectionAndFieldRelations(relations) {
18
+ // Map<Collection, Set<RelatedCollection>>
19
+ const collectionRelationTree = new Map();
20
+ // Map<Field:Collection, RelatedCollection>
21
+ const fieldToCollectionList = new Map();
22
+ for (const relation of relations) {
23
+ let relatedCollections = [];
24
+ if (relation.related_collection) {
25
+ relatedCollections.push(relation.related_collection);
26
+ }
27
+ else if (relation.meta?.one_collection_field && relation.meta?.one_allowed_collections) {
28
+ relatedCollections = relation.meta?.one_allowed_collections;
29
+ }
30
+ else {
31
+ // safe guard
32
+ continue;
33
+ }
34
+ for (const relatedCollection of relatedCollections) {
35
+ let fieldToCollectionListKey = relation.collection + ':' + relation.field;
36
+ const collectionList = collectionRelationTree.get(relatedCollection) ?? new Set();
37
+ collectionList.add(relation.collection);
38
+ // O2M can have outward duplication path
39
+ if (relation.meta?.one_field) {
40
+ const relatedfieldToCollectionListKey = relatedCollection + ':' + relation.meta.one_field;
41
+ const realatedCollectionList = collectionRelationTree.get(relation.collection) ?? new Set();
42
+ realatedCollectionList.add(relatedCollection);
43
+ fieldToCollectionList.set(relatedfieldToCollectionListKey, relation.collection);
44
+ collectionRelationTree.set(relation.collection, realatedCollectionList);
45
+ }
46
+ // m2a fields show as field:collection in duplication path
47
+ if (relation.meta?.one_allowed_collections) {
48
+ fieldToCollectionListKey += ':' + relatedCollection;
49
+ }
50
+ fieldToCollectionList.set(fieldToCollectionListKey, relatedCollection);
51
+ collectionRelationTree.set(relatedCollection, collectionList);
52
+ }
53
+ }
54
+ return { collectionRelationTree, fieldToCollectionList };
55
+ }
@@ -0,0 +1,11 @@
1
+ import type { CollectionsOverview } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ export declare function getCollectionMetaUpdates(collection: string, field: string, collectionMetas: {
4
+ archive_field?: null | string;
5
+ sort_field?: null | string;
6
+ item_duplication_fields?: null | string | string[];
7
+ collection: string;
8
+ }[], collections: CollectionsOverview, fieldToCollectionList: Map<string, string>): {
9
+ collection: string;
10
+ updates: Record<string, Knex.Value>;
11
+ }[];
@@ -0,0 +1,72 @@
1
+ import { parseJSON } from '@directus/utils';
2
+ export function getCollectionMetaUpdates(collection, field, collectionMetas, collections, fieldToCollectionList) {
3
+ const collectionMetaUpdates = [];
4
+ for (const collectionMeta of collectionMetas) {
5
+ let hasUpdates = false;
6
+ const meta = {
7
+ collection: collectionMeta.collection,
8
+ updates: {},
9
+ };
10
+ if (collectionMeta.collection === collection) {
11
+ if (collectionMeta?.archive_field === field) {
12
+ meta.updates['archive_field'] = null;
13
+ hasUpdates = true;
14
+ }
15
+ if (collectionMeta?.sort_field === field) {
16
+ meta.updates['sort_field'] = null;
17
+ hasUpdates = true;
18
+ }
19
+ }
20
+ if (collectionMeta?.item_duplication_fields) {
21
+ const itemDuplicationPaths = typeof collectionMeta.item_duplication_fields === 'string'
22
+ ? parseJSON(collectionMeta.item_duplication_fields)
23
+ : collectionMeta.item_duplication_fields;
24
+ const updatedPaths = [];
25
+ for (const path of itemDuplicationPaths) {
26
+ const updatedPath = updateItemDuplicationPath(path, collectionMeta.collection, field, collection, collections, fieldToCollectionList);
27
+ if (updatedPath && updatedPath.length !== 0) {
28
+ updatedPaths.push(updatedPath.join('.'));
29
+ }
30
+ }
31
+ // only add updates on change
32
+ if (updatedPaths.length !== itemDuplicationPaths.length) {
33
+ meta.updates['item_duplication_fields'] = updatedPaths.length !== 0 ? JSON.stringify(updatedPaths) : null;
34
+ hasUpdates = true;
35
+ }
36
+ }
37
+ if (hasUpdates) {
38
+ collectionMetaUpdates.push(meta);
39
+ }
40
+ }
41
+ return collectionMetaUpdates;
42
+ }
43
+ function updateItemDuplicationPath(path, root, field, collection, collections, fieldToCollectionList) {
44
+ let currentCollection = root;
45
+ const parts = path.split('.');
46
+ // if the field name is not present in the path we can skip processing as no possible match
47
+ if ([field, `.${field}`, `.${field}.`, `${field}.`].some((fieldPart) => path.includes(fieldPart)) === false) {
48
+ return parts;
49
+ }
50
+ const updatedParts = [];
51
+ for (let index = 0; index < parts.length; index++) {
52
+ const part = parts[index];
53
+ // Invalid path for the field that is currently being removed
54
+ if (currentCollection === collection && part === field)
55
+ return;
56
+ const isLastPart = index === parts.length - 1;
57
+ const isLocalField = typeof collections[currentCollection]?.['fields'][part] !== 'undefined';
58
+ const nextCollectionNode = fieldToCollectionList.get(`${currentCollection}:${part}`);
59
+ // Invalid path for old deleted collections
60
+ if (!nextCollectionNode && !isLastPart)
61
+ return;
62
+ // Invalid path for old deleted fields
63
+ if (!nextCollectionNode && isLastPart && !isLocalField)
64
+ return;
65
+ // next/last path is relational
66
+ if (nextCollectionNode) {
67
+ currentCollection = nextCollectionNode;
68
+ }
69
+ updatedParts.push(part);
70
+ }
71
+ return updatedParts;
72
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Returns a list of all related collections for a given collection.
3
+ * Or in math terms, returns the [strongly connected component](https://en.wikipedia.org/wiki/Strongly_connected_component) that a given node belongs to.
4
+ */
5
+ export declare function getCollectionRelationList(collection: string, collectionRelationTree: Map<string, Set<string>>): Set<string>;
@@ -0,0 +1,28 @@
1
+ import { isSystemCollection } from '@directus/system-data';
2
+ /**
3
+ * Returns a list of all related collections for a given collection.
4
+ * Or in math terms, returns the [strongly connected component](https://en.wikipedia.org/wiki/Strongly_connected_component) that a given node belongs to.
5
+ */
6
+ export function getCollectionRelationList(collection, collectionRelationTree) {
7
+ const collectionRelationList = new Set();
8
+ traverseCollectionRelationTree(collection);
9
+ return collectionRelationList;
10
+ function traverseCollectionRelationTree(root) {
11
+ const relationTree = collectionRelationTree.get(root);
12
+ if (!relationTree)
13
+ return;
14
+ for (const relationNode of relationTree) {
15
+ addRelationNode(relationNode);
16
+ }
17
+ }
18
+ function addRelationNode(node) {
19
+ // system collections cannot have duplication fields and therfore can be skipped
20
+ if (isSystemCollection(node))
21
+ return;
22
+ // skip circular reference and existing linked nodes
23
+ if (node === collection || collectionRelationList.has(node))
24
+ return;
25
+ collectionRelationList.add(node);
26
+ traverseCollectionRelationTree(node);
27
+ }
28
+ }
@@ -20,6 +20,9 @@ import { getSchema } from '../utils/get-schema.js';
20
20
  import { sanitizeColumn } from '../utils/sanitize-schema.js';
21
21
  import { shouldClearCache } from '../utils/should-clear-cache.js';
22
22
  import { transaction } from '../utils/transaction.js';
23
+ import { buildCollectionAndFieldRelations } from './fields/build-collection-and-field-relations.js';
24
+ import { getCollectionMetaUpdates } from './fields/get-collection-meta-updates.js';
25
+ import { getCollectionRelationList } from './fields/get-collection-relation-list.js';
23
26
  import { ItemsService } from './items.js';
24
27
  import { PayloadService } from './payload.js';
25
28
  import { RelationsService } from './relations.js';
@@ -556,20 +559,22 @@ export class FieldsService {
556
559
  table.dropColumn(field);
557
560
  });
558
561
  }
559
- const collectionMeta = await trx
560
- .select('archive_field', 'sort_field')
562
+ const { collectionRelationTree, fieldToCollectionList } = await buildCollectionAndFieldRelations(this.schema.relations);
563
+ const collectionRelationList = getCollectionRelationList(collection, collectionRelationTree);
564
+ const collectionMetaQuery = trx
565
+ .queryBuilder()
566
+ .select('collection', 'archive_field', 'sort_field', 'item_duplication_fields')
561
567
  .from('directus_collections')
562
- .where({ collection })
563
- .first();
564
- const collectionMetaUpdates = {};
565
- if (collectionMeta?.archive_field === field) {
566
- collectionMetaUpdates['archive_field'] = null;
567
- }
568
- if (collectionMeta?.sort_field === field) {
569
- collectionMetaUpdates['sort_field'] = null;
568
+ .where({ collection });
569
+ if (collectionRelationList.size !== 0) {
570
+ collectionMetaQuery.orWhere(function () {
571
+ this.whereIn('collection', Array.from(collectionRelationList)).whereNotNull('item_duplication_fields');
572
+ });
570
573
  }
571
- if (Object.keys(collectionMetaUpdates).length > 0) {
572
- await trx('directus_collections').update(collectionMetaUpdates).where({ collection });
574
+ const collectionMetas = await collectionMetaQuery;
575
+ const collectionMetaUpdates = getCollectionMetaUpdates(collection, field, collectionMetas, this.schema.collections, fieldToCollectionList);
576
+ for (const meta of collectionMetaUpdates) {
577
+ await trx('directus_collections').update(meta.updates).where({ collection: meta.collection });
573
578
  }
574
579
  // Cleanup directus_fields
575
580
  const metaRow = await trx
@@ -0,0 +1,3 @@
1
+ import type { SchemaComposer } from 'graphql-compose';
2
+ import type { Schema } from '../schema/index.js';
3
+ export declare function getCollectionType(schemaComposer: SchemaComposer, schema: Schema, action: 'read' | 'write'): import("graphql-compose").ObjectTypeComposer<any, any>;
@@ -0,0 +1,34 @@
1
+ import { GraphQLNonNull, GraphQLString } from 'graphql';
2
+ import { getGraphQLType } from '../../../utils/get-graphql-type.js';
3
+ export function getCollectionType(schemaComposer, schema, action) {
4
+ const prefix = action === 'read' ? '' : 'write_';
5
+ const Collection = schemaComposer.createObjectTC({
6
+ name: `${prefix}directus_collections`,
7
+ });
8
+ if ('directus_collections' in schema.read.collections === false) {
9
+ return Collection;
10
+ }
11
+ Collection.addFields({
12
+ collection: GraphQLString,
13
+ meta: schemaComposer.createObjectTC({
14
+ name: `${prefix}directus_collections_meta`,
15
+ fields: Object.values(schema.read.collections['directus_collections'].fields).reduce((acc, field) => {
16
+ acc[field.field] = {
17
+ type: field.nullable || action === 'write'
18
+ ? getGraphQLType(field.type, field.special)
19
+ : new GraphQLNonNull(getGraphQLType(field.type, field.special)),
20
+ description: field.note,
21
+ };
22
+ return acc;
23
+ }, {}),
24
+ }),
25
+ schema: schemaComposer.createObjectTC({
26
+ name: `${prefix}directus_collections_schema`,
27
+ fields: {
28
+ name: GraphQLString,
29
+ comment: GraphQLString,
30
+ },
31
+ }),
32
+ });
33
+ return Collection;
34
+ }
@@ -0,0 +1,3 @@
1
+ import type { SchemaComposer } from 'graphql-compose';
2
+ import type { Schema } from '../schema/index.js';
3
+ export declare function getFieldType(schemaComposer: SchemaComposer, schema: Schema, action: 'read' | 'write'): import("graphql-compose").ObjectTypeComposer<any, any>;
@@ -0,0 +1,51 @@
1
+ import { GraphQLBoolean, GraphQLInt, GraphQLNonNull, GraphQLString } from 'graphql';
2
+ import { getGraphQLType } from '../../../utils/get-graphql-type.js';
3
+ export function getFieldType(schemaComposer, schema, action) {
4
+ const prefix = action === 'read' ? '' : 'write_';
5
+ const Field = schemaComposer.createObjectTC({
6
+ name: `${prefix}directus_fields`,
7
+ });
8
+ if ('directus_fields' in schema.read.collections === false) {
9
+ return Field;
10
+ }
11
+ Field.addFields({
12
+ collection: GraphQLString,
13
+ field: GraphQLString,
14
+ type: GraphQLString,
15
+ meta: schemaComposer.createObjectTC({
16
+ name: `${prefix}directus_fields_meta`,
17
+ fields: Object.values(schema.read.collections['directus_fields'].fields).reduce((acc, field) => {
18
+ acc[field.field] = {
19
+ type: field.nullable || action === 'write'
20
+ ? getGraphQLType(field.type, field.special)
21
+ : new GraphQLNonNull(getGraphQLType(field.type, field.special)),
22
+ description: field.note,
23
+ };
24
+ return acc;
25
+ }, {}),
26
+ }),
27
+ schema: schemaComposer.createObjectTC({
28
+ name: `${prefix}directus_fields_schema`,
29
+ fields: {
30
+ name: GraphQLString,
31
+ table: GraphQLString,
32
+ data_type: GraphQLString,
33
+ default_value: GraphQLString,
34
+ max_length: GraphQLInt,
35
+ numeric_precision: GraphQLInt,
36
+ numeric_scale: GraphQLInt,
37
+ is_generated: GraphQLBoolean,
38
+ generation_expression: GraphQLString,
39
+ is_indexed: GraphQLBoolean,
40
+ is_nullable: GraphQLBoolean,
41
+ is_unique: GraphQLBoolean,
42
+ is_primary_key: GraphQLBoolean,
43
+ has_auto_increment: GraphQLBoolean,
44
+ foreign_key_column: GraphQLString,
45
+ foreign_key_table: GraphQLString,
46
+ comment: GraphQLString,
47
+ },
48
+ }),
49
+ });
50
+ return Field;
51
+ }
@@ -0,0 +1,3 @@
1
+ import type { SchemaComposer } from 'graphql-compose';
2
+ import type { Schema } from '../schema/index.js';
3
+ export declare function getRelationType(schemaComposer: SchemaComposer, schema: Schema, action: 'read' | 'write'): import("graphql-compose").ObjectTypeComposer<any, any>;
@@ -0,0 +1,39 @@
1
+ import { GraphQLNonNull, GraphQLString } from 'graphql';
2
+ import { getGraphQLType } from '../../../utils/get-graphql-type.js';
3
+ export function getRelationType(schemaComposer, schema, action) {
4
+ const prefix = action === 'read' ? '' : 'write_';
5
+ const Relation = schemaComposer.createObjectTC({
6
+ name: `${prefix}directus_relations`,
7
+ });
8
+ if ('directus_relations' in schema.read.collections === false) {
9
+ return Relation;
10
+ }
11
+ Relation.addFields({
12
+ collection: GraphQLString,
13
+ field: GraphQLString,
14
+ related_collection: GraphQLString,
15
+ schema: schemaComposer.createObjectTC({
16
+ name: `${prefix}directus_relations_schema`,
17
+ fields: {
18
+ table: new GraphQLNonNull(GraphQLString),
19
+ column: new GraphQLNonNull(GraphQLString),
20
+ foreign_key_table: new GraphQLNonNull(GraphQLString),
21
+ foreign_key_column: new GraphQLNonNull(GraphQLString),
22
+ constraint_name: GraphQLString,
23
+ on_update: new GraphQLNonNull(GraphQLString),
24
+ on_delete: new GraphQLNonNull(GraphQLString),
25
+ },
26
+ }),
27
+ meta: schemaComposer.createObjectTC({
28
+ name: `${prefix}directus_relations_meta`,
29
+ fields: Object.values(schema.read.collections['directus_relations'].fields).reduce((acc, field) => {
30
+ acc[field.field] = {
31
+ type: getGraphQLType(field.type, field.special),
32
+ description: field.note,
33
+ };
34
+ return acc;
35
+ }, {}),
36
+ }),
37
+ });
38
+ return Relation;
39
+ }
@@ -8,7 +8,7 @@ export async function resolveMutation(gql, args, info) {
8
8
  if (gql.scope === 'system')
9
9
  collection = `directus_${collection}`;
10
10
  const selections = replaceFragmentsInSelections(info.fieldNodes[0]?.selectionSet?.selections, info.fragments);
11
- const query = getQuery(args, selections || [], info.variableValues, gql.accountability);
11
+ const query = await getQuery(args, selections || [], info.variableValues, gql.schema, gql.accountability);
12
12
  const singleton = collection.endsWith('_batch') === false &&
13
13
  collection.endsWith('_items') === false &&
14
14
  collection.endsWith('_item') === false &&
@@ -2,10 +2,10 @@ import { parseFilterFunctionPath } from '@directus/utils';
2
2
  import { omit } from 'lodash-es';
3
3
  import { mergeVersionsRaw, mergeVersionsRecursive } from '../../../utils/merge-version-data.js';
4
4
  import { VersionsService } from '../../versions.js';
5
- import { getAggregateQuery } from '../utils/aggrgate-query.js';
6
- import { replaceFragmentsInSelections } from '../utils/replace-fragments.js';
7
5
  import { parseArgs } from '../schema/parse-args.js';
8
6
  import { getQuery } from '../schema/parse-query.js';
7
+ import { getAggregateQuery } from '../utils/aggregate-query.js';
8
+ import { replaceFragmentsInSelections } from '../utils/replace-fragments.js';
9
9
  /**
10
10
  * Generic resolver that's used for every "regular" items/system query. Converts the incoming GraphQL AST / fragments into
11
11
  * Directus' query structure which is then executed by the services.
@@ -22,11 +22,11 @@ export async function resolveQuery(gql, info) {
22
22
  let versionRaw = false;
23
23
  const isAggregate = collection.endsWith('_aggregated') && collection in gql.schema.collections === false;
24
24
  if (isAggregate) {
25
- query = getAggregateQuery(args, selections, gql.accountability);
25
+ query = await getAggregateQuery(args, selections, gql.schema, gql.accountability);
26
26
  collection = collection.slice(0, -11);
27
27
  }
28
28
  else {
29
- query = getQuery(args, selections, info.variableValues, gql.accountability);
29
+ query = await getQuery(args, selections, info.variableValues, gql.schema, gql.accountability);
30
30
  if (collection.endsWith('_by_id') && collection in gql.schema.collections === false) {
31
31
  collection = collection.slice(0, -6);
32
32
  }
@@ -1,5 +1,5 @@
1
1
  import { SchemaComposer } from 'graphql-compose';
2
2
  import type { GraphQLParams } from '../../../types/index.js';
3
3
  import { GraphQLService } from '../index.js';
4
- import type { BaseTypeComposers } from './system.js';
5
- export declare function resolveSystemAdmin(gql: GraphQLService, schemaComposer: SchemaComposer<GraphQLParams['contextValue']>, { Collection, Field, Relation, Extension }: BaseTypeComposers): void;
4
+ import type { Schema } from '../schema/index.js';
5
+ export declare function resolveSystemAdmin(gql: GraphQLService, schema: Schema, schemaComposer: SchemaComposer<GraphQLParams['contextValue']>): void;