@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,120 @@
1
+ 'use strict';
2
+
3
+ const { extendType } = require('nexus');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ const { service: getService } = strapi.plugin('graphql');
7
+
8
+ const { naming } = getService('utils');
9
+ const { transformArgs, getContentTypeArgs } = getService('builders').utils;
10
+ const { toEntityResponse, toEntityResponseCollection } = getService('format').returnTypes;
11
+
12
+ const {
13
+ getFindOneQueryName,
14
+ getEntityResponseName,
15
+ getFindQueryName,
16
+ getEntityResponseCollectionName,
17
+ } = naming;
18
+
19
+ const buildCollectionTypeQueries = contentType => {
20
+ const findOneQueryName = `Query.${getFindOneQueryName(contentType)}`;
21
+ const findQueryName = `Query.${getFindQueryName(contentType)}`;
22
+
23
+ const extension = getService('extension');
24
+
25
+ const registerAuthConfig = (action, auth) => {
26
+ return extension.use({ resolversConfig: { [action]: { auth } } });
27
+ };
28
+
29
+ const isActionEnabled = action => {
30
+ return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
31
+ };
32
+
33
+ const isFindOneEnabled = isActionEnabled('findOne');
34
+ const isFindEnabled = isActionEnabled('find');
35
+
36
+ if (isFindOneEnabled) {
37
+ registerAuthConfig(findOneQueryName, { scope: [`${contentType.uid}.findOne`] });
38
+ }
39
+
40
+ if (isFindEnabled) {
41
+ registerAuthConfig(findQueryName, { scope: [`${contentType.uid}.find`] });
42
+ }
43
+
44
+ return extendType({
45
+ type: 'Query',
46
+
47
+ definition(t) {
48
+ if (isFindOneEnabled) {
49
+ addFindOneQuery(t, contentType);
50
+ }
51
+
52
+ if (isFindEnabled) {
53
+ addFindQuery(t, contentType);
54
+ }
55
+ },
56
+ });
57
+ };
58
+
59
+ /**
60
+ * Register a "find one" query field to the nexus type definition
61
+ * @param {OutputDefinitionBlock<Query>} t
62
+ * @param contentType
63
+ */
64
+ const addFindOneQuery = (t, contentType) => {
65
+ const { uid } = contentType;
66
+
67
+ const findOneQueryName = getFindOneQueryName(contentType);
68
+ const responseTypeName = getEntityResponseName(contentType);
69
+
70
+ t.field(findOneQueryName, {
71
+ type: responseTypeName,
72
+
73
+ args: getContentTypeArgs(contentType, { multiple: false }),
74
+
75
+ async resolve(parent, args) {
76
+ const transformedArgs = transformArgs(args, { contentType });
77
+
78
+ const { findOne } = getService('builders')
79
+ .get('content-api')
80
+ .buildQueriesResolvers({ contentType });
81
+
82
+ const value = findOne(parent, transformedArgs);
83
+
84
+ return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
85
+ },
86
+ });
87
+ };
88
+
89
+ /**
90
+ * Register a "find" query field to the nexus type definition
91
+ * @param {OutputDefinitionBlock<Query>} t
92
+ * @param contentType
93
+ */
94
+ const addFindQuery = (t, contentType) => {
95
+ const { uid } = contentType;
96
+
97
+ const findQueryName = getFindQueryName(contentType);
98
+ const responseCollectionTypeName = getEntityResponseCollectionName(contentType);
99
+
100
+ t.field(findQueryName, {
101
+ type: responseCollectionTypeName,
102
+
103
+ args: getContentTypeArgs(contentType),
104
+
105
+ async resolve(parent, args) {
106
+ const transformedArgs = transformArgs(args, { contentType, usePagination: true });
107
+
108
+ const { find } = getService('builders')
109
+ .get('content-api')
110
+ .buildQueriesResolvers({ contentType });
111
+
112
+ const nodes = await find(parent, transformedArgs);
113
+
114
+ return toEntityResponseCollection(nodes, { args: transformedArgs, resourceUID: uid });
115
+ },
116
+ });
117
+ };
118
+
119
+ return { buildCollectionTypeQueries };
120
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const createCollectionTypeQueriesBuilder = require('./collection-type');
4
+ const createSingleTypeQueriesBuilder = require('./single-type');
5
+
6
+ module.exports = context => ({
7
+ ...createCollectionTypeQueriesBuilder(context),
8
+ ...createSingleTypeQueriesBuilder(context),
9
+ });
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ const { extendType } = require('nexus');
4
+
5
+ module.exports = ({ strapi }) => {
6
+ const { service: getService } = strapi.plugin('graphql');
7
+
8
+ const { naming } = getService('utils');
9
+ const { transformArgs, getContentTypeArgs } = getService('builders').utils;
10
+ const { toEntityResponse } = getService('format').returnTypes;
11
+
12
+ const { getFindOneQueryName, getEntityResponseName } = naming;
13
+
14
+ const buildSingleTypeQueries = contentType => {
15
+ const findQueryName = `Query.${getFindOneQueryName(contentType)}`;
16
+
17
+ const extension = getService('extension');
18
+
19
+ const registerAuthConfig = (action, auth) => {
20
+ return extension.use({ resolversConfig: { [action]: { auth } } });
21
+ };
22
+
23
+ const isActionEnabled = action => {
24
+ return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
25
+ };
26
+
27
+ const isFindEnabled = isActionEnabled('find');
28
+
29
+ if (isFindEnabled) {
30
+ registerAuthConfig(findQueryName, { scope: [`${contentType.uid}.find`] });
31
+ }
32
+
33
+ return extendType({
34
+ type: 'Query',
35
+
36
+ definition(t) {
37
+ if (isFindEnabled) {
38
+ addFindQuery(t, contentType);
39
+ }
40
+ },
41
+ });
42
+ };
43
+
44
+ const addFindQuery = (t, contentType) => {
45
+ const { uid } = contentType;
46
+
47
+ const findQueryName = getFindOneQueryName(contentType);
48
+ const responseTypeName = getEntityResponseName(contentType);
49
+
50
+ t.field(findQueryName, {
51
+ type: responseTypeName,
52
+
53
+ args: getContentTypeArgs(contentType),
54
+
55
+ async resolve(parent, args) {
56
+ const transformedArgs = transformArgs(args, { contentType });
57
+
58
+ const queriesResolvers = getService('builders')
59
+ .get('content-api')
60
+ .buildQueriesResolvers({ contentType });
61
+
62
+ const value = queriesResolvers.find(parent, transformedArgs);
63
+
64
+ return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
65
+ },
66
+ });
67
+ };
68
+
69
+ return { buildSingleTypeQueries };
70
+ };
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const { objectType, nonNull } = require('nexus');
4
+ const { defaultTo, prop, pipe } = require('lodash/fp');
5
+
6
+ module.exports = ({ strapi }) => {
7
+ const { naming } = strapi.plugin('graphql').service('utils');
8
+
9
+ return {
10
+ /**
11
+ * Build a type definition for a content API relation's collection response for a given content type
12
+ * @param {object} contentType The content type which will be used to build its content API response definition
13
+ * @return {NexusObjectTypeDef}
14
+ */
15
+ buildRelationResponseCollectionDefinition(contentType) {
16
+ const name = naming.getRelationResponseCollectionName(contentType);
17
+ const entityName = naming.getEntityName(contentType);
18
+
19
+ return objectType({
20
+ name,
21
+
22
+ definition(t) {
23
+ t.nonNull.list.field('data', {
24
+ type: nonNull(entityName),
25
+
26
+ resolve: pipe(
27
+ prop('nodes'),
28
+ defaultTo([])
29
+ ),
30
+ });
31
+ },
32
+ });
33
+ },
34
+ };
35
+ };
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ module.exports = ({ strapi }) => {
4
+ const { service: getGraphQLService } = strapi.plugin('graphql');
5
+
6
+ const { isMorphRelation, isMedia } = getGraphQLService('utils').attributes;
7
+ const { transformArgs } = getGraphQLService('builders').utils;
8
+ const { toEntityResponse, toEntityResponseCollection } = getGraphQLService('format').returnTypes;
9
+
10
+ return {
11
+ buildAssociationResolver({ contentTypeUID, attributeName }) {
12
+ const contentType = strapi.getModel(contentTypeUID);
13
+ const attribute = contentType.attributes[attributeName];
14
+
15
+ if (!attribute) {
16
+ throw new Error(
17
+ `Failed to build an association resolver for ${contentTypeUID}::${attributeName}`
18
+ );
19
+ }
20
+
21
+ const isMediaAttribute = isMedia(attribute);
22
+ const isMorphAttribute = isMorphRelation(attribute);
23
+
24
+ const targetUID = isMediaAttribute ? 'plugins::upload.file' : attribute.target;
25
+ const isToMany = isMediaAttribute ? attribute.multiple : attribute.relation.endsWith('Many');
26
+
27
+ const targetContentType = strapi.getModel(targetUID);
28
+
29
+ return async (parent, args = {}) => {
30
+ const transformedArgs = transformArgs(args, {
31
+ contentType: targetContentType,
32
+ usePagination: true,
33
+ });
34
+
35
+ const data = await strapi.entityService.load(
36
+ contentTypeUID,
37
+ parent,
38
+ attributeName,
39
+ transformedArgs
40
+ );
41
+
42
+ const info = {
43
+ args: transformedArgs,
44
+ resourceUID: targetUID,
45
+ };
46
+
47
+ // If this a polymorphic association, it returns the raw data
48
+ if (isMorphAttribute) {
49
+ return data;
50
+ }
51
+
52
+ // If this is a to-many relation, it returns an object that
53
+ // matches what the entity-response-collection's resolvers expect
54
+ else if (isToMany) {
55
+ return toEntityResponseCollection(data, info);
56
+ }
57
+
58
+ // Else, it returns an object that matches
59
+ // what the entity-response's resolvers expect
60
+ return toEntityResponse(data, info);
61
+ };
62
+ },
63
+ };
64
+ };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ module.exports = ({ strapi }) => ({
4
+ buildComponentResolver({ contentTypeUID, attributeName }) {
5
+ const { transformArgs } = strapi.plugin('graphql').service('builders').utils;
6
+
7
+ return async (parent, args = {}) => {
8
+ const contentType = strapi.contentTypes[contentTypeUID];
9
+ const transformedArgs = transformArgs(args, { contentType, usePagination: true });
10
+
11
+ return strapi.entityService.load(contentTypeUID, parent, attributeName, transformedArgs);
12
+ };
13
+ },
14
+ });
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ module.exports = ({ strapi }) => ({
4
+ buildDynamicZoneResolver({ contentTypeUID, attributeName }) {
5
+ return async parent => {
6
+ return strapi.entityService.load(contentTypeUID, parent, attributeName);
7
+ };
8
+ },
9
+ });
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const associationResolvers = require('./association');
4
+ const queriesResolvers = require('./query');
5
+ const mutationsResolvers = require('./mutation');
6
+ const componentResolvers = require('./component');
7
+ const dynamicZoneResolvers = require('./dynamic-zone');
8
+
9
+ module.exports = context => ({
10
+ // Generics
11
+ ...associationResolvers(context),
12
+
13
+ // Builders
14
+ ...mutationsResolvers(context),
15
+ ...queriesResolvers(context),
16
+ ...componentResolvers(context),
17
+ ...dynamicZoneResolvers(context),
18
+ });
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const { pick } = require('lodash/fp');
4
+
5
+ const pickCreateArgs = pick(['params', 'data', 'files']);
6
+
7
+ module.exports = ({ strapi }) => ({
8
+ buildMutationsResolvers({ contentType }) {
9
+ const { uid } = contentType;
10
+
11
+ return {
12
+ async create(parent, args) {
13
+ // todo[v4]: Might be interesting to generate dynamic yup schema to validate payloads with more complex checks (on top of graphql validation)
14
+ const params = pickCreateArgs(args);
15
+
16
+ // todo[v4]: Sanitize args to only keep params / data / files (or do it in the base resolver)
17
+ return strapi.entityService.create(uid, params);
18
+ },
19
+
20
+ async update(parent, args) {
21
+ const { id, data } = args;
22
+
23
+ return strapi.entityService.update(uid, id, { data });
24
+ },
25
+
26
+ async delete(parent, args) {
27
+ const { id, ...rest } = args;
28
+
29
+ return strapi.entityService.delete(uid, id, rest);
30
+ },
31
+ };
32
+ },
33
+ });
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { omit } = require('lodash/fp');
4
+
5
+ module.exports = ({ strapi }) => ({
6
+ buildQueriesResolvers({ contentType }) {
7
+ const { uid } = contentType;
8
+
9
+ return {
10
+ async find(parent, args) {
11
+ return strapi.entityService.findMany(uid, args);
12
+ },
13
+
14
+ async findOne(parent, args) {
15
+ return strapi.entityService.findOne(uid, args.id, omit('id', args));
16
+ },
17
+ };
18
+ },
19
+ });
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ const { objectType, nonNull } = require('nexus');
4
+ const { defaultTo, prop, pipe } = require('lodash/fp');
5
+
6
+ module.exports = ({ strapi }) => {
7
+ const { naming } = strapi.plugin('graphql').service('utils');
8
+ const { RESPONSE_COLLECTION_META_TYPE_NAME } = strapi.plugin('graphql').service('constants');
9
+
10
+ return {
11
+ /**
12
+ * Build a type definition for a content API collection response for a given content type
13
+ * @param {object} contentType The content type which will be used to build its content API response definition
14
+ * @return {NexusObjectTypeDef}
15
+ */
16
+ buildResponseCollectionDefinition(contentType) {
17
+ const name = naming.getEntityResponseCollectionName(contentType);
18
+ const entityName = naming.getEntityName(contentType);
19
+
20
+ return objectType({
21
+ name,
22
+
23
+ definition(t) {
24
+ t.nonNull.list.field('data', {
25
+ type: nonNull(entityName),
26
+
27
+ resolve: pipe(
28
+ prop('nodes'),
29
+ defaultTo([])
30
+ ),
31
+ });
32
+
33
+ t.nonNull.field('meta', {
34
+ type: RESPONSE_COLLECTION_META_TYPE_NAME,
35
+
36
+ // Pass down the args stored in the source object
37
+ resolve: prop('info'),
38
+ });
39
+ },
40
+ });
41
+ },
42
+ };
43
+ };
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ const { objectType } = require('nexus');
4
+ const { prop } = require('lodash/fp');
5
+
6
+ module.exports = ({ strapi }) => {
7
+ const { naming } = strapi.plugin('graphql').service('utils');
8
+
9
+ return {
10
+ /**
11
+ * Build a type definition for a content API response for a given content type
12
+ * @param {object} contentType The content type which will be used to build its content API response definition
13
+ * @return {NexusObjectTypeDef}
14
+ */
15
+ buildResponseDefinition(contentType) {
16
+ const name = naming.getEntityResponseName(contentType);
17
+ const entityName = naming.getEntityName(contentType);
18
+
19
+ return objectType({
20
+ name,
21
+
22
+ definition(t) {
23
+ t.field('data', {
24
+ type: entityName,
25
+
26
+ resolve: prop('value'),
27
+ });
28
+ },
29
+ });
30
+ },
31
+ };
32
+ };