@strapi/plugin-graphql 4.0.0-next.6 → 4.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 (122) hide show
  1. package/README.md +1 -1
  2. package/admin/src/index.js +0 -8
  3. package/package.json +39 -32
  4. package/server/bootstrap.js +148 -0
  5. package/server/config/default-config.js +13 -0
  6. package/server/config/index.js +7 -0
  7. package/server/format-graphql-error.js +50 -0
  8. package/server/services/builders/dynamic-zones.js +97 -0
  9. package/server/services/builders/entity-meta.js +7 -0
  10. package/server/services/builders/entity.js +43 -0
  11. package/server/services/builders/enums.js +24 -0
  12. package/server/services/builders/filters/content-type.js +93 -0
  13. package/server/services/builders/filters/index.js +7 -0
  14. package/server/services/builders/filters/operators/and.js +15 -0
  15. package/server/services/builders/filters/operators/between.js +15 -0
  16. package/server/services/builders/filters/operators/contains.js +13 -0
  17. package/server/services/builders/filters/operators/containsi.js +13 -0
  18. package/server/services/builders/filters/operators/ends-with.js +13 -0
  19. package/server/services/builders/filters/operators/eq.js +23 -0
  20. package/server/services/builders/filters/operators/gt.js +13 -0
  21. package/server/services/builders/filters/operators/gte.js +13 -0
  22. package/server/services/builders/filters/operators/in.js +15 -0
  23. package/server/services/builders/filters/operators/index.js +38 -0
  24. package/server/services/builders/filters/operators/lt.js +13 -0
  25. package/server/services/builders/filters/operators/lte.js +13 -0
  26. package/server/services/builders/filters/operators/ne.js +13 -0
  27. package/server/services/builders/filters/operators/not-contains.js +13 -0
  28. package/server/services/builders/filters/operators/not-containsi.js +13 -0
  29. package/server/services/builders/filters/operators/not-in.js +15 -0
  30. package/server/services/builders/filters/operators/not-null.js +13 -0
  31. package/server/services/builders/filters/operators/not.js +19 -0
  32. package/server/services/builders/filters/operators/null.js +13 -0
  33. package/server/services/builders/filters/operators/or.js +15 -0
  34. package/server/services/builders/filters/operators/starts-with.js +13 -0
  35. package/server/services/builders/generic-morph.js +41 -0
  36. package/server/services/builders/index.js +92 -0
  37. package/server/services/builders/input.js +121 -0
  38. package/server/services/builders/mutations/collection-type.js +191 -0
  39. package/server/services/builders/mutations/index.js +9 -0
  40. package/server/services/builders/mutations/single-type.js +141 -0
  41. package/server/services/builders/queries/collection-type.js +120 -0
  42. package/server/services/builders/queries/index.js +9 -0
  43. package/server/services/builders/queries/single-type.js +70 -0
  44. package/server/services/builders/relation-response-collection.js +35 -0
  45. package/server/services/builders/resolvers/association.js +85 -0
  46. package/server/services/builders/resolvers/component.js +18 -0
  47. package/server/services/builders/resolvers/dynamic-zone.js +9 -0
  48. package/server/services/builders/resolvers/index.js +18 -0
  49. package/server/services/builders/resolvers/mutation.js +33 -0
  50. package/server/services/builders/resolvers/query.js +19 -0
  51. package/server/services/builders/response-collection.js +43 -0
  52. package/server/services/builders/response.js +32 -0
  53. package/server/services/builders/type.js +364 -0
  54. package/server/services/builders/utils.js +134 -0
  55. package/server/services/constants.js +147 -0
  56. package/server/services/content-api/index.js +179 -0
  57. package/server/services/content-api/policy.js +60 -0
  58. package/server/services/content-api/register-functions/collection-type.js +72 -0
  59. package/server/services/content-api/register-functions/component.js +15 -0
  60. package/server/services/content-api/register-functions/content-type/dynamic-zones.js +36 -0
  61. package/server/services/content-api/register-functions/content-type/enums.js +33 -0
  62. package/server/services/content-api/register-functions/content-type/filters.js +15 -0
  63. package/server/services/content-api/register-functions/content-type/index.js +13 -0
  64. package/server/services/content-api/register-functions/content-type/inputs.js +21 -0
  65. package/server/services/content-api/register-functions/index.js +22 -0
  66. package/server/services/content-api/register-functions/internals.js +13 -0
  67. package/server/services/content-api/register-functions/polymorphic.js +69 -0
  68. package/server/services/content-api/register-functions/scalars.js +14 -0
  69. package/server/services/content-api/register-functions/single-type.js +72 -0
  70. package/server/services/content-api/wrap-resolvers.js +144 -0
  71. package/server/services/extension/extension.js +95 -0
  72. package/server/services/extension/index.js +5 -0
  73. package/server/services/extension/shadow-crud-manager.js +159 -0
  74. package/server/services/format/index.js +7 -0
  75. package/server/services/format/return-types.js +27 -0
  76. package/server/services/index.js +21 -0
  77. package/server/services/internals/args/index.js +11 -0
  78. package/server/services/internals/args/pagination.js +19 -0
  79. package/server/services/internals/args/publication-state.js +12 -0
  80. package/server/services/internals/args/sort.js +10 -0
  81. package/server/services/internals/helpers/get-enabled-scalars.js +15 -0
  82. package/server/services/internals/helpers/index.js +7 -0
  83. package/server/services/internals/index.js +13 -0
  84. package/server/services/internals/scalars/index.js +18 -0
  85. package/server/services/internals/scalars/time.js +36 -0
  86. package/server/services/internals/types/error.js +34 -0
  87. package/server/services/internals/types/filters.js +39 -0
  88. package/server/services/internals/types/index.js +29 -0
  89. package/server/services/internals/types/pagination.js +24 -0
  90. package/server/services/internals/types/publication-state.js +24 -0
  91. package/server/services/internals/types/response-collection-meta.js +38 -0
  92. package/server/services/type-registry.js +104 -0
  93. package/server/services/utils/attributes.js +84 -0
  94. package/server/services/utils/index.js +11 -0
  95. package/server/services/utils/mappers/entity-to-response-entity.js +12 -0
  96. package/server/services/utils/mappers/graphql-filters-to-strapi-query.js +109 -0
  97. package/server/services/utils/mappers/graphql-scalar-to-operators.js +17 -0
  98. package/server/services/utils/mappers/index.js +13 -0
  99. package/server/services/utils/mappers/strapi-scalar-to-graphql-scalar.js +25 -0
  100. package/server/services/utils/naming.js +287 -0
  101. package/strapi-admin.js +3 -0
  102. package/strapi-server.js +13 -0
  103. package/admin/src/assets/images/logo.svg +0 -38
  104. package/config/routes.json +0 -3
  105. package/config/schema.graphql +0 -1
  106. package/config/settings.json +0 -12
  107. package/controllers/GraphQL.js +0 -9
  108. package/hooks/graphql/defaults.json +0 -5
  109. package/hooks/graphql/index.js +0 -174
  110. package/hooks/graphql/load-config.js +0 -42
  111. package/services/build-aggregation.js +0 -565
  112. package/services/data-loaders.js +0 -55
  113. package/services/naming.js +0 -15
  114. package/services/resolvers-builder.js +0 -204
  115. package/services/schema-definitions.js +0 -131
  116. package/services/schema-generator.js +0 -178
  117. package/services/shadow-crud.js +0 -612
  118. package/services/type-builder.js +0 -311
  119. package/services/utils.js +0 -200
  120. package/types/dynamiczoneScalar.js +0 -40
  121. package/types/publication-state.js +0 -16
  122. package/types/time.js +0 -26
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const getEnabledScalars = require('./get-enabled-scalars');
4
+
5
+ module.exports = context => ({
6
+ getEnabledScalars: getEnabledScalars(context),
7
+ });
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const args = require('./args');
4
+ const scalars = require('./scalars');
5
+ const types = require('./types');
6
+ const helpers = require('./helpers');
7
+
8
+ module.exports = context => ({
9
+ args: args(context),
10
+ scalars: scalars(context),
11
+ buildInternalTypes: types(context),
12
+ helpers: helpers(context),
13
+ });
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const GraphQLJSON = require('graphql-type-json');
4
+ const GraphQLLong = require('graphql-type-long');
5
+ const { GraphQLDateTime, GraphQLDate } = require('graphql-iso-date/dist');
6
+ const { GraphQLUpload } = require('graphql-upload');
7
+ const { asNexusMethod } = require('nexus');
8
+
9
+ const TimeScalar = require('./time');
10
+
11
+ module.exports = () => ({
12
+ JSON: asNexusMethod(GraphQLJSON, 'json'),
13
+ DateTime: asNexusMethod(GraphQLDateTime, 'dateTime'),
14
+ Time: asNexusMethod(TimeScalar, 'time'),
15
+ Date: asNexusMethod(GraphQLDate, 'date'),
16
+ Long: asNexusMethod(GraphQLLong, 'long'),
17
+ Upload: asNexusMethod(GraphQLUpload, 'upload'),
18
+ });
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const { GraphQLScalarType } = require('graphql');
4
+ const { Kind } = require('graphql');
5
+ const { parseType } = require('@strapi/utils');
6
+ const { ValidationError } = require('@strapi/utils').errors;
7
+
8
+ /**
9
+ * A GraphQL scalar used to store Time (HH:mm:ss.SSS) values
10
+ * @type {GraphQLScalarType}
11
+ */
12
+ const TimeScalar = new GraphQLScalarType({
13
+ name: 'Time',
14
+
15
+ description: 'A time string with format HH:mm:ss.SSS',
16
+
17
+ serialize(value) {
18
+ return parseType({ type: 'time', value });
19
+ },
20
+
21
+ parseValue(value) {
22
+ return parseType({ type: 'time', value });
23
+ },
24
+
25
+ parseLiteral(ast) {
26
+ if (ast.kind !== Kind.STRING) {
27
+ throw new ValidationError('Time cannot represent non string type');
28
+ }
29
+
30
+ const value = ast.value;
31
+
32
+ return parseType({ type: 'time', value });
33
+ },
34
+ });
35
+
36
+ module.exports = TimeScalar;
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ const { objectType } = require('nexus');
4
+ const { get } = require('lodash/fp');
5
+ const { ValidationError } = require('@strapi/utils').errors;
6
+
7
+ /**
8
+ * Build an Error object type
9
+ * @return {Object<string, NexusObjectTypeDef>}
10
+ */
11
+ module.exports = ({ strapi }) => {
12
+ const { ERROR_CODES, ERROR_TYPE_NAME } = strapi.plugin('graphql').service('constants');
13
+
14
+ return objectType({
15
+ name: ERROR_TYPE_NAME,
16
+
17
+ definition(t) {
18
+ t.nonNull.string('code', {
19
+ resolve(parent) {
20
+ const code = get('code', parent);
21
+
22
+ const isValidPlaceholderCode = Object.values(ERROR_CODES).includes(code);
23
+ if (!isValidPlaceholderCode) {
24
+ throw new ValidationError(`"${code}" is not a valid code value`);
25
+ }
26
+
27
+ return code;
28
+ },
29
+ });
30
+
31
+ t.string('message');
32
+ },
33
+ });
34
+ };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const { inputObjectType } = require('nexus');
4
+
5
+ /**
6
+ * Build a map of filters type for every GraphQL scalars
7
+ * @return {Object<string, NexusInputTypeDef>}
8
+ */
9
+ const buildScalarFilters = ({ strapi }) => {
10
+ const { naming, mappers } = strapi.plugin('graphql').service('utils');
11
+ const { helpers } = strapi.plugin('graphql').service('internals');
12
+
13
+ return helpers.getEnabledScalars().reduce((acc, type) => {
14
+ const operators = mappers.graphqlScalarToOperators(type);
15
+ const typeName = naming.getScalarFilterInputTypeName(type);
16
+
17
+ if (!operators || operators.length === 0) {
18
+ return acc;
19
+ }
20
+
21
+ return {
22
+ ...acc,
23
+
24
+ [typeName]: inputObjectType({
25
+ name: typeName,
26
+
27
+ definition(t) {
28
+ for (const operator of operators) {
29
+ operator.add(t, type);
30
+ }
31
+ },
32
+ }),
33
+ };
34
+ }, {});
35
+ };
36
+
37
+ module.exports = context => ({
38
+ scalars: buildScalarFilters(context),
39
+ });
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const pagination = require('./pagination');
4
+ const buildResponseCollectionMeta = require('./response-collection-meta');
5
+ const publicationState = require('./publication-state');
6
+ const filters = require('./filters');
7
+ const error = require('./error');
8
+
9
+ module.exports = context => () => {
10
+ const { strapi } = context;
11
+
12
+ const { KINDS } = strapi.plugin('graphql').service('constants');
13
+
14
+ return {
15
+ [KINDS.internal]: {
16
+ error: error(context),
17
+ pagination: pagination(context),
18
+ responseCollectionMeta: buildResponseCollectionMeta(context),
19
+ },
20
+
21
+ [KINDS.enum]: {
22
+ publicationState: publicationState(context),
23
+ },
24
+
25
+ [KINDS.filtersInput]: {
26
+ ...filters(context),
27
+ },
28
+ };
29
+ };
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const { objectType } = require('nexus');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ const { PAGINATION_TYPE_NAME } = strapi.plugin('graphql').service('constants');
7
+
8
+ return {
9
+ /**
10
+ * Type definition for a Pagination object
11
+ * @type {NexusObjectTypeDef}
12
+ */
13
+ Pagination: objectType({
14
+ name: PAGINATION_TYPE_NAME,
15
+
16
+ definition(t) {
17
+ t.nonNull.int('total');
18
+ t.nonNull.int('page');
19
+ t.nonNull.int('pageSize');
20
+ t.nonNull.int('pageCount');
21
+ },
22
+ }),
23
+ };
24
+ };
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const { enumType } = require('nexus');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ const { PUBLICATION_STATE_TYPE_NAME } = strapi.plugin('graphql').service('constants');
7
+
8
+ return {
9
+ /**
10
+ * An enum type definition representing a publication state
11
+ * @type {NexusEnumTypeDef}
12
+ */
13
+ PublicationState: enumType({
14
+ name: PUBLICATION_STATE_TYPE_NAME,
15
+
16
+ members: {
17
+ // Published only
18
+ LIVE: 'live',
19
+ // Published & draft
20
+ PREVIEW: 'preview',
21
+ },
22
+ }),
23
+ };
24
+ };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ const { objectType } = require('nexus');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ const { RESPONSE_COLLECTION_META_TYPE_NAME, PAGINATION_TYPE_NAME } = strapi
7
+ .plugin('graphql')
8
+ .service('constants');
9
+
10
+ return {
11
+ /**
12
+ * A shared type definition used in EntitiesResponseCollection
13
+ * to have information about the collection as a whole
14
+ * @type {NexusObjectTypeDef}
15
+ */
16
+ ResponseCollectionMeta: objectType({
17
+ name: RESPONSE_COLLECTION_META_TYPE_NAME,
18
+
19
+ definition(t) {
20
+ t.nonNull.field('pagination', {
21
+ type: PAGINATION_TYPE_NAME,
22
+
23
+ async resolve(parent) {
24
+ const { args, resourceUID } = parent;
25
+ const { start, limit } = args;
26
+
27
+ const total = await strapi.entityService.count(resourceUID, args);
28
+ const pageSize = limit;
29
+ const pageCount = limit === 0 ? 0 : Math.ceil(total / limit);
30
+ const page = limit === 0 ? 1 : Math.floor(start / limit) + 1;
31
+
32
+ return { total, page, pageSize, pageCount };
33
+ },
34
+ });
35
+ },
36
+ }),
37
+ };
38
+ };
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+
3
+ const { isFunction } = require('lodash/fp');
4
+ const { ApplicationError } = require('@strapi/utils').errors;
5
+
6
+ /**
7
+ * @typedef RegisteredTypeDef
8
+ *
9
+ * @property {string} name
10
+ * @property {NexusAcceptedTypeDef} definition
11
+ * @property {object} config
12
+ */
13
+
14
+ /**
15
+ * Create a new type registry
16
+ */
17
+ const createTypeRegistry = () => {
18
+ const registry = new Map();
19
+
20
+ return {
21
+ /**
22
+ * Register a new type definition
23
+ * @param {string} name The name of the type
24
+ * @param {NexusAcceptedTypeDef} definition The Nexus definition for the type
25
+ * @param {object} [config] An optional config object with any metadata inside
26
+ */
27
+ register(name, definition, config = {}) {
28
+ if (registry.has(name)) {
29
+ throw new ApplicationError(`"${name}" has already been registered`);
30
+ }
31
+
32
+ registry.set(name, { name, definition, config });
33
+
34
+ return this;
35
+ },
36
+
37
+ /**
38
+ * Register many types definitions at once
39
+ * @param {[string, NexusAcceptedTypeDef][]} definitionsEntries
40
+ * @param {object | function} [config]
41
+ */
42
+ registerMany(definitionsEntries, config = {}) {
43
+ for (const [name, definition] of definitionsEntries) {
44
+ this.register(name, definition, isFunction(config) ? config(name, definition) : config);
45
+ }
46
+
47
+ return this;
48
+ },
49
+
50
+ /**
51
+ * Check if the given type name has already been added to the registry
52
+ * @param {string} name
53
+ * @return {boolean}
54
+ */
55
+ has(name) {
56
+ return registry.has(name);
57
+ },
58
+
59
+ /**
60
+ * Get the type definition for `name`
61
+ * @param {string} name - The name of the type
62
+ */
63
+ get(name) {
64
+ return registry.get(name);
65
+ },
66
+
67
+ /**
68
+ * Transform and return the registry as an object
69
+ * @return {Object<string, RegisteredTypeDef>}
70
+ */
71
+ toObject() {
72
+ return Object.fromEntries(registry.entries());
73
+ },
74
+
75
+ /**
76
+ * Return the name of every registered type
77
+ * @return {string[]}
78
+ */
79
+ get types() {
80
+ return Array.from(registry.keys());
81
+ },
82
+
83
+ /**
84
+ * Return all the registered definitions as an array
85
+ * @return {RegisteredTypeDef[]}
86
+ */
87
+ get definitions() {
88
+ return Array.from(registry.values());
89
+ },
90
+
91
+ /**
92
+ * Filter and return the types definitions that matches the given predicate
93
+ * @param {function(RegisteredTypeDef): boolean} predicate
94
+ * @return {RegisteredTypeDef[]}
95
+ */
96
+ where(predicate) {
97
+ return this.definitions.filter(predicate);
98
+ },
99
+ };
100
+ };
101
+
102
+ module.exports = () => ({
103
+ new: createTypeRegistry,
104
+ });
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ const { propEq } = require('lodash/fp');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ /**
7
+ * Check if the given attribute is a Strapi scalar
8
+ * @param {object} attribute
9
+ * @return {boolean}
10
+ */
11
+ const isStrapiScalar = attribute => {
12
+ return strapi
13
+ .plugin('graphql')
14
+ .service('constants')
15
+ .STRAPI_SCALARS.includes(attribute.type);
16
+ };
17
+
18
+ /**
19
+ * Check if the given attribute is a GraphQL scalar
20
+ * @param {object} attribute
21
+ * @return {boolean}
22
+ */
23
+ const isGraphQLScalar = attribute => {
24
+ return strapi
25
+ .plugin('graphql')
26
+ .service('constants')
27
+ .GRAPHQL_SCALARS.includes(attribute.type);
28
+ };
29
+
30
+ /**
31
+ * Check if the given attribute is a polymorphic relation
32
+ * @param {object} attribute
33
+ * @return {boolean}
34
+ */
35
+ const isMorphRelation = attribute => {
36
+ return isRelation(attribute) && attribute.relation.includes('morph');
37
+ };
38
+
39
+ /**
40
+ * Check if the given attribute is a media
41
+ * @param {object} attribute
42
+ * @return {boolean}
43
+ */
44
+ const isMedia = propEq('type', 'media');
45
+
46
+ /**
47
+ * Check if the given attribute is a relation
48
+ * @param {object} attribute
49
+ * @return {boolean}
50
+ */
51
+ const isRelation = propEq('type', 'relation');
52
+
53
+ /**
54
+ * Check if the given attribute is an enum
55
+ * @param {object} attribute
56
+ * @return {boolean}
57
+ */
58
+ const isEnumeration = propEq('type', 'enumeration');
59
+
60
+ /**
61
+ * Check if the given attribute is a component
62
+ * @param {object} attribute
63
+ * @return {boolean}
64
+ */
65
+ const isComponent = propEq('type', 'component');
66
+
67
+ /**
68
+ * Check if the given attribute is a dynamic zone
69
+ * @param {object} attribute
70
+ * @return {boolean}
71
+ */
72
+ const isDynamicZone = propEq('type', 'dynamiczone');
73
+
74
+ return {
75
+ isStrapiScalar,
76
+ isGraphQLScalar,
77
+ isMorphRelation,
78
+ isMedia,
79
+ isRelation,
80
+ isEnumeration,
81
+ isComponent,
82
+ isDynamicZone,
83
+ };
84
+ };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const mappers = require('./mappers');
4
+ const attributes = require('./attributes');
5
+ const naming = require('./naming');
6
+
7
+ module.exports = context => ({
8
+ naming: naming(context),
9
+ attributes: attributes(context),
10
+ mappers: mappers(context),
11
+ });
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { map } = require('lodash/fp');
4
+
5
+ const entityToResponseEntity = entity => ({ id: entity.id, attributes: entity });
6
+
7
+ const entitiesToResponseEntities = map(entityToResponseEntity);
8
+
9
+ module.exports = () => ({
10
+ entityToResponseEntity,
11
+ entitiesToResponseEntities,
12
+ });
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ const { has, propEq, isNil, isDate, isObject } = require('lodash/fp');
4
+
5
+ // todo[v4]: Find a way to get that dynamically
6
+ const virtualScalarAttributes = ['id'];
7
+
8
+ module.exports = ({ strapi }) => {
9
+ const { service: getService } = strapi.plugin('graphql');
10
+
11
+ const recursivelyReplaceScalarOperators = data => {
12
+ const { operators } = getService('builders').filters;
13
+
14
+ if (Array.isArray(data)) {
15
+ return data.map(recursivelyReplaceScalarOperators);
16
+ }
17
+
18
+ // Note: We need to make an exception for date since GraphQL
19
+ // automatically cast date strings to date instances in args
20
+ if (isDate(data) || !isObject(data)) {
21
+ return data;
22
+ }
23
+
24
+ const result = {};
25
+
26
+ for (const [key, value] of Object.entries(data)) {
27
+ const isOperator = !!operators[key];
28
+
29
+ const newKey = isOperator ? operators[key].strapiOperator : key;
30
+
31
+ result[newKey] = recursivelyReplaceScalarOperators(value);
32
+ }
33
+
34
+ return result;
35
+ };
36
+
37
+ return {
38
+ /**
39
+ * Transform one or many GraphQL filters object into a valid Strapi query
40
+ * @param {object | object[]} filters
41
+ * @param {object} contentType
42
+ * @return {object | object[]}
43
+ */
44
+ graphQLFiltersToStrapiQuery(filters, contentType = {}) {
45
+ const { isStrapiScalar, isMedia, isRelation } = getService('utils').attributes;
46
+ const { operators } = getService('builders').filters;
47
+
48
+ const ROOT_LEVEL_OPERATORS = [operators.and, operators.or, operators.not];
49
+
50
+ // Handle unwanted scenario where there is no filters defined
51
+ if (isNil(filters)) {
52
+ return {};
53
+ }
54
+
55
+ // If filters is a collection, then apply the transformation to every item of the list
56
+ if (Array.isArray(filters)) {
57
+ return filters.map(filtersItem =>
58
+ this.graphQLFiltersToStrapiQuery(filtersItem, contentType)
59
+ );
60
+ }
61
+
62
+ const resultMap = {};
63
+ const { attributes } = contentType;
64
+
65
+ const isAttribute = attributeName => {
66
+ return virtualScalarAttributes.includes(attributeName) || has(attributeName, attributes);
67
+ };
68
+
69
+ for (const [key, value] of Object.entries(filters)) {
70
+ // If the key is an attribute, update the value
71
+ if (isAttribute(key)) {
72
+ const attribute = attributes[key];
73
+
74
+ // If it's a scalar attribute
75
+ if (virtualScalarAttributes.includes(key) || isStrapiScalar(attribute)) {
76
+ // Replace (recursively) every GraphQL scalar operator with the associated Strapi operator
77
+ resultMap[key] = recursivelyReplaceScalarOperators(value);
78
+ }
79
+
80
+ // If it's a deep filter on a relation
81
+ else if (isRelation(attribute) || isMedia(attribute)) {
82
+ // Fetch the model from the relation
83
+ const relModel = strapi.getModel(attribute.target);
84
+
85
+ // Recursively apply the mapping to the value using the fetched model,
86
+ // and update the value within `resultMap`
87
+ resultMap[key] = this.graphQLFiltersToStrapiQuery(value, relModel);
88
+ }
89
+ }
90
+
91
+ // Handle the case where the key is not an attribute (operator, ...)
92
+ else {
93
+ const rootLevelOperator = ROOT_LEVEL_OPERATORS.find(propEq('fieldName', key));
94
+
95
+ // If it's a root level operator (AND, NOT, OR, ...)
96
+ if (rootLevelOperator) {
97
+ const { strapiOperator } = rootLevelOperator;
98
+
99
+ // Transform the current value recursively and add it to the resultMap
100
+ // object using the strapiOperator equivalent of the GraphQL key
101
+ resultMap[strapiOperator] = this.graphQLFiltersToStrapiQuery(value, contentType);
102
+ }
103
+ }
104
+ }
105
+
106
+ return resultMap;
107
+ },
108
+ };
109
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const { get, map, mapValues } = require('lodash/fp');
4
+
5
+ module.exports = ({ strapi }) => ({
6
+ graphqlScalarToOperators(graphqlScalar) {
7
+ const { GRAPHQL_SCALAR_OPERATORS } = strapi.plugin('graphql').service('constants');
8
+ const { operators } = strapi.plugin('graphql').service('builders').filters;
9
+
10
+ const associations = mapValues(
11
+ map(operatorName => operators[operatorName]),
12
+ GRAPHQL_SCALAR_OPERATORS
13
+ );
14
+
15
+ return get(graphqlScalar, associations);
16
+ },
17
+ });
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const strapiScalarToGraphQLScalar = require('./strapi-scalar-to-graphql-scalar');
4
+ const graphQLFiltersToStrapiQuery = require('./graphql-filters-to-strapi-query');
5
+ const graphqlScalarToOperators = require('./graphql-scalar-to-operators');
6
+ const entityToResponseEntity = require('./entity-to-response-entity');
7
+
8
+ module.exports = context => ({
9
+ ...strapiScalarToGraphQLScalar(context),
10
+ ...graphQLFiltersToStrapiQuery(context),
11
+ ...graphqlScalarToOperators(context),
12
+ ...entityToResponseEntity(context),
13
+ });
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const { get, difference } = require('lodash/fp');
4
+ const { ApplicationError } = require('@strapi/utils').errors;
5
+
6
+ module.exports = ({ strapi }) => {
7
+ const { STRAPI_SCALARS, SCALARS_ASSOCIATIONS } = strapi.plugin('graphql').service('constants');
8
+
9
+ const missingStrapiScalars = difference(STRAPI_SCALARS, Object.keys(SCALARS_ASSOCIATIONS));
10
+
11
+ if (missingStrapiScalars.length > 0) {
12
+ throw new ApplicationError('Some Strapi scalars are not handled in the GraphQL scalars mapper');
13
+ }
14
+
15
+ return {
16
+ /**
17
+ * Used to transform a Strapi scalar type into its GraphQL equivalent
18
+ * @param {string} strapiScalar
19
+ * @return {NexusGenScalars}
20
+ */
21
+ strapiScalarToGraphQLScalar(strapiScalar) {
22
+ return get(strapiScalar, SCALARS_ASSOCIATIONS);
23
+ },
24
+ };
25
+ };