@strapi/plugin-graphql 4.0.0-next.2 → 4.0.0-next.20

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 (117) hide show
  1. package/admin/src/translations/zh-Hans.json +4 -0
  2. package/package.json +17 -15
  3. package/server/bootstrap.js +124 -0
  4. package/server/services/builders/dynamic-zones.js +96 -0
  5. package/server/services/builders/entity-meta.js +7 -0
  6. package/server/services/builders/entity.js +43 -0
  7. package/server/services/builders/enums.js +24 -0
  8. package/server/services/builders/filters/content-type.js +84 -0
  9. package/server/services/builders/filters/index.js +7 -0
  10. package/server/services/builders/filters/operators/and.js +15 -0
  11. package/server/services/builders/filters/operators/between.js +15 -0
  12. package/server/services/builders/filters/operators/contains.js +13 -0
  13. package/server/services/builders/filters/operators/containsi.js +13 -0
  14. package/server/services/builders/filters/operators/ends-with.js +13 -0
  15. package/server/services/builders/filters/operators/eq.js +19 -0
  16. package/server/services/builders/filters/operators/gt.js +13 -0
  17. package/server/services/builders/filters/operators/gte.js +13 -0
  18. package/server/services/builders/filters/operators/in.js +15 -0
  19. package/server/services/builders/filters/operators/index.js +38 -0
  20. package/server/services/builders/filters/operators/lt.js +13 -0
  21. package/server/services/builders/filters/operators/lte.js +13 -0
  22. package/server/services/builders/filters/operators/ne.js +13 -0
  23. package/server/services/builders/filters/operators/not-contains.js +13 -0
  24. package/server/services/builders/filters/operators/not-containsi.js +13 -0
  25. package/server/services/builders/filters/operators/not-in.js +15 -0
  26. package/server/services/builders/filters/operators/not-null.js +13 -0
  27. package/server/services/builders/filters/operators/not.js +19 -0
  28. package/server/services/builders/filters/operators/null.js +13 -0
  29. package/server/services/builders/filters/operators/or.js +15 -0
  30. package/server/services/builders/filters/operators/starts-with.js +13 -0
  31. package/server/services/builders/generic-morph.js +41 -0
  32. package/server/services/builders/index.js +92 -0
  33. package/server/services/builders/input.js +118 -0
  34. package/server/services/builders/mutations/collection-type.js +170 -0
  35. package/server/services/builders/mutations/index.js +9 -0
  36. package/server/services/builders/mutations/single-type.js +135 -0
  37. package/server/services/builders/queries/collection-type.js +120 -0
  38. package/server/services/builders/queries/index.js +9 -0
  39. package/server/services/builders/queries/single-type.js +70 -0
  40. package/server/services/builders/relation-response-collection.js +35 -0
  41. package/server/services/builders/resolvers/association.js +64 -0
  42. package/server/services/builders/resolvers/component.js +14 -0
  43. package/server/services/builders/resolvers/dynamic-zone.js +9 -0
  44. package/server/services/builders/resolvers/index.js +18 -0
  45. package/server/services/builders/resolvers/mutation.js +33 -0
  46. package/server/services/builders/resolvers/query.js +19 -0
  47. package/server/services/builders/response-collection.js +43 -0
  48. package/server/services/builders/response.js +32 -0
  49. package/server/services/builders/type.js +370 -0
  50. package/server/services/builders/utils.js +131 -0
  51. package/server/services/constants.js +147 -0
  52. package/server/services/content-api/index.js +168 -0
  53. package/server/services/content-api/policy.js +59 -0
  54. package/server/services/content-api/register-functions/collection-type.js +72 -0
  55. package/server/services/content-api/register-functions/component.js +15 -0
  56. package/server/services/content-api/register-functions/content-type/dynamic-zones.js +36 -0
  57. package/server/services/content-api/register-functions/content-type/enums.js +33 -0
  58. package/server/services/content-api/register-functions/content-type/filters.js +15 -0
  59. package/server/services/content-api/register-functions/content-type/index.js +13 -0
  60. package/server/services/content-api/register-functions/content-type/inputs.js +21 -0
  61. package/server/services/content-api/register-functions/index.js +22 -0
  62. package/server/services/content-api/register-functions/internals.js +13 -0
  63. package/server/services/content-api/register-functions/polymorphic.js +69 -0
  64. package/server/services/content-api/register-functions/scalars.js +14 -0
  65. package/server/services/content-api/register-functions/single-type.js +72 -0
  66. package/server/services/content-api/wrap-resolvers.js +146 -0
  67. package/server/services/extension/extension.js +95 -0
  68. package/server/services/extension/index.js +5 -0
  69. package/server/services/extension/shadow-crud-manager.js +159 -0
  70. package/server/services/format/index.js +7 -0
  71. package/server/services/format/return-types.js +27 -0
  72. package/server/services/index.js +21 -0
  73. package/server/services/internals/args/index.js +11 -0
  74. package/server/services/internals/args/pagination.js +19 -0
  75. package/server/services/internals/args/publication-state.js +12 -0
  76. package/server/services/internals/args/sort.js +10 -0
  77. package/server/services/internals/helpers/get-enabled-scalars.js +15 -0
  78. package/server/services/internals/helpers/index.js +7 -0
  79. package/server/services/internals/index.js +13 -0
  80. package/server/services/internals/scalars/index.js +18 -0
  81. package/server/services/internals/scalars/time.js +35 -0
  82. package/server/services/internals/types/error.js +33 -0
  83. package/server/services/internals/types/filters.js +39 -0
  84. package/server/services/internals/types/index.js +29 -0
  85. package/server/services/internals/types/pagination.js +24 -0
  86. package/server/services/internals/types/publication-state.js +24 -0
  87. package/server/services/internals/types/response-collection-meta.js +38 -0
  88. package/server/services/type-registry.js +103 -0
  89. package/server/services/utils/attributes.js +84 -0
  90. package/server/services/utils/index.js +11 -0
  91. package/server/services/utils/mappers/entity-to-response-entity.js +12 -0
  92. package/server/services/utils/mappers/graphql-filters-to-strapi-query.js +107 -0
  93. package/server/services/utils/mappers/graphql-scalar-to-operators.js +17 -0
  94. package/server/services/utils/mappers/index.js +13 -0
  95. package/server/services/utils/mappers/strapi-scalar-to-graphql-scalar.js +24 -0
  96. package/server/services/utils/naming.js +282 -0
  97. package/strapi-admin.js +3 -0
  98. package/strapi-server.js +11 -0
  99. package/config/routes.json +0 -3
  100. package/config/schema.graphql +0 -1
  101. package/config/settings.json +0 -12
  102. package/controllers/GraphQL.js +0 -9
  103. package/hooks/graphql/defaults.json +0 -5
  104. package/hooks/graphql/index.js +0 -174
  105. package/hooks/graphql/load-config.js +0 -42
  106. package/services/build-aggregation.js +0 -565
  107. package/services/data-loaders.js +0 -55
  108. package/services/naming.js +0 -15
  109. package/services/resolvers-builder.js +0 -204
  110. package/services/schema-definitions.js +0 -131
  111. package/services/schema-generator.js +0 -178
  112. package/services/shadow-crud.js +0 -612
  113. package/services/type-builder.js +0 -311
  114. package/services/utils.js +0 -200
  115. package/types/dynamiczoneScalar.js +0 -40
  116. package/types/publication-state.js +0 -16
  117. package/types/time.js +0 -26
@@ -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,103 @@
1
+ 'use strict';
2
+
3
+ const { isFunction } = require('lodash/fp');
4
+
5
+ /**
6
+ * @typedef RegisteredTypeDef
7
+ *
8
+ * @property {string} name
9
+ * @property {NexusAcceptedTypeDef} definition
10
+ * @property {object} config
11
+ */
12
+
13
+ /**
14
+ * Create a new type registry
15
+ */
16
+ const createTypeRegistry = () => {
17
+ const registry = new Map();
18
+
19
+ return {
20
+ /**
21
+ * Register a new type definition
22
+ * @param {string} name The name of the type
23
+ * @param {NexusAcceptedTypeDef} definition The Nexus definition for the type
24
+ * @param {object} [config] An optional config object with any metadata inside
25
+ */
26
+ register(name, definition, config = {}) {
27
+ if (registry.has(name)) {
28
+ throw new Error(`"${name}" has already been registered`);
29
+ }
30
+
31
+ registry.set(name, { name, definition, config });
32
+
33
+ return this;
34
+ },
35
+
36
+ /**
37
+ * Register many types definitions at once
38
+ * @param {[string, NexusAcceptedTypeDef][]} definitionsEntries
39
+ * @param {object | function} [config]
40
+ */
41
+ registerMany(definitionsEntries, config = {}) {
42
+ for (const [name, definition] of definitionsEntries) {
43
+ this.register(name, definition, isFunction(config) ? config(name, definition) : config);
44
+ }
45
+
46
+ return this;
47
+ },
48
+
49
+ /**
50
+ * Check if the given type name has already been added to the registry
51
+ * @param {string} name
52
+ * @return {boolean}
53
+ */
54
+ has(name) {
55
+ return registry.has(name);
56
+ },
57
+
58
+ /**
59
+ * Get the type definition for `name`
60
+ * @param {string} name - The name of the type
61
+ */
62
+ get(name) {
63
+ return registry.get(name);
64
+ },
65
+
66
+ /**
67
+ * Transform and return the registry as an object
68
+ * @return {Object<string, RegisteredTypeDef>}
69
+ */
70
+ toObject() {
71
+ return Object.fromEntries(registry.entries());
72
+ },
73
+
74
+ /**
75
+ * Return the name of every registered type
76
+ * @return {string[]}
77
+ */
78
+ get types() {
79
+ return Array.from(registry.keys());
80
+ },
81
+
82
+ /**
83
+ * Return all the registered definitions as an array
84
+ * @return {RegisteredTypeDef[]}
85
+ */
86
+ get definitions() {
87
+ return Array.from(registry.values());
88
+ },
89
+
90
+ /**
91
+ * Filter and return the types definitions that matches the given predicate
92
+ * @param {function(RegisteredTypeDef): boolean} predicate
93
+ * @return {RegisteredTypeDef[]}
94
+ */
95
+ where(predicate) {
96
+ return this.definitions.filter(predicate);
97
+ },
98
+ };
99
+ };
100
+
101
+ module.exports = () => ({
102
+ new: createTypeRegistry,
103
+ });
@@ -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,107 @@
1
+ 'use strict';
2
+
3
+ const { has, propEq, isNil } = 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
+ if (typeof data !== 'object') {
19
+ return data;
20
+ }
21
+
22
+ const result = {};
23
+
24
+ for (const [key, value] of Object.entries(data)) {
25
+ const isOperator = !!operators[key];
26
+
27
+ const newKey = isOperator ? operators[key].strapiOperator : key;
28
+
29
+ result[newKey] = recursivelyReplaceScalarOperators(value);
30
+ }
31
+
32
+ return result;
33
+ };
34
+
35
+ return {
36
+ /**
37
+ * Transform one or many GraphQL filters object into a valid Strapi query
38
+ * @param {object | object[]} filters
39
+ * @param {object} contentType
40
+ * @return {object | object[]}
41
+ */
42
+ graphQLFiltersToStrapiQuery(filters, contentType = {}) {
43
+ const { isStrapiScalar, isMedia, isRelation } = getService('utils').attributes;
44
+ const { operators } = getService('builders').filters;
45
+
46
+ const ROOT_LEVEL_OPERATORS = [operators.and, operators.or, operators.not];
47
+
48
+ // Handle unwanted scenario where there is no filters defined
49
+ if (isNil(filters)) {
50
+ return {};
51
+ }
52
+
53
+ // If filters is a collection, then apply the transformation to every item of the list
54
+ if (Array.isArray(filters)) {
55
+ return filters.map(filtersItem =>
56
+ this.graphQLFiltersToStrapiQuery(filtersItem, contentType)
57
+ );
58
+ }
59
+
60
+ const resultMap = {};
61
+ const { attributes } = contentType;
62
+
63
+ const isAttribute = attributeName => {
64
+ return virtualScalarAttributes.includes(attributeName) || has(attributeName, attributes);
65
+ };
66
+
67
+ for (const [key, value] of Object.entries(filters)) {
68
+ // If the key is an attribute, update the value
69
+ if (isAttribute(key)) {
70
+ const attribute = attributes[key];
71
+
72
+ // If it's a scalar attribute
73
+ if (virtualScalarAttributes.includes(key) || isStrapiScalar(attribute)) {
74
+ // Replace (recursively) every GraphQL scalar operator with the associated Strapi operator
75
+ resultMap[key] = recursivelyReplaceScalarOperators(value);
76
+ }
77
+
78
+ // If it's a deep filter on a relation
79
+ else if (isRelation(attribute) || isMedia(attribute)) {
80
+ // Fetch the model from the relation
81
+ const relModel = strapi.getModel(attribute.target);
82
+
83
+ // Recursively apply the mapping to the value using the fetched model,
84
+ // and update the value within `resultMap`
85
+ resultMap[key] = this.graphQLFiltersToStrapiQuery(value, relModel);
86
+ }
87
+ }
88
+
89
+ // Handle the case where the key is not an attribute (operator, ...)
90
+ else {
91
+ const rootLevelOperator = ROOT_LEVEL_OPERATORS.find(propEq('fieldName', key));
92
+
93
+ // If it's a root level operator (AND, NOT, OR, ...)
94
+ if (rootLevelOperator) {
95
+ const { strapiOperator } = rootLevelOperator;
96
+
97
+ // Transform the current value recursively and add it to the resultMap
98
+ // object using the strapiOperator equivalent of the GraphQL key
99
+ resultMap[strapiOperator] = this.graphQLFiltersToStrapiQuery(value, contentType);
100
+ }
101
+ }
102
+ }
103
+
104
+ return resultMap;
105
+ },
106
+ };
107
+ };
@@ -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,24 @@
1
+ 'use strict';
2
+
3
+ const { get, difference } = require('lodash/fp');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ const { STRAPI_SCALARS, SCALARS_ASSOCIATIONS } = strapi.plugin('graphql').service('constants');
7
+
8
+ const missingStrapiScalars = difference(STRAPI_SCALARS, Object.keys(SCALARS_ASSOCIATIONS));
9
+
10
+ if (missingStrapiScalars.length > 0) {
11
+ throw new Error('Some Strapi scalars are not handled in the GraphQL scalars mapper');
12
+ }
13
+
14
+ return {
15
+ /**
16
+ * Used to transform a Strapi scalar type into its GraphQL equivalent
17
+ * @param {string} strapiScalar
18
+ * @return {NexusGenScalars}
19
+ */
20
+ strapiScalarToGraphQLScalar(strapiScalar) {
21
+ return get(strapiScalar, SCALARS_ASSOCIATIONS);
22
+ },
23
+ };
24
+ };