@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.
- package/admin/src/translations/zh-Hans.json +4 -0
- package/package.json +17 -15
- package/server/bootstrap.js +124 -0
- package/server/services/builders/dynamic-zones.js +96 -0
- package/server/services/builders/entity-meta.js +7 -0
- package/server/services/builders/entity.js +43 -0
- package/server/services/builders/enums.js +24 -0
- package/server/services/builders/filters/content-type.js +84 -0
- package/server/services/builders/filters/index.js +7 -0
- package/server/services/builders/filters/operators/and.js +15 -0
- package/server/services/builders/filters/operators/between.js +15 -0
- package/server/services/builders/filters/operators/contains.js +13 -0
- package/server/services/builders/filters/operators/containsi.js +13 -0
- package/server/services/builders/filters/operators/ends-with.js +13 -0
- package/server/services/builders/filters/operators/eq.js +19 -0
- package/server/services/builders/filters/operators/gt.js +13 -0
- package/server/services/builders/filters/operators/gte.js +13 -0
- package/server/services/builders/filters/operators/in.js +15 -0
- package/server/services/builders/filters/operators/index.js +38 -0
- package/server/services/builders/filters/operators/lt.js +13 -0
- package/server/services/builders/filters/operators/lte.js +13 -0
- package/server/services/builders/filters/operators/ne.js +13 -0
- package/server/services/builders/filters/operators/not-contains.js +13 -0
- package/server/services/builders/filters/operators/not-containsi.js +13 -0
- package/server/services/builders/filters/operators/not-in.js +15 -0
- package/server/services/builders/filters/operators/not-null.js +13 -0
- package/server/services/builders/filters/operators/not.js +19 -0
- package/server/services/builders/filters/operators/null.js +13 -0
- package/server/services/builders/filters/operators/or.js +15 -0
- package/server/services/builders/filters/operators/starts-with.js +13 -0
- package/server/services/builders/generic-morph.js +41 -0
- package/server/services/builders/index.js +92 -0
- package/server/services/builders/input.js +118 -0
- package/server/services/builders/mutations/collection-type.js +170 -0
- package/server/services/builders/mutations/index.js +9 -0
- package/server/services/builders/mutations/single-type.js +135 -0
- package/server/services/builders/queries/collection-type.js +120 -0
- package/server/services/builders/queries/index.js +9 -0
- package/server/services/builders/queries/single-type.js +70 -0
- package/server/services/builders/relation-response-collection.js +35 -0
- package/server/services/builders/resolvers/association.js +64 -0
- package/server/services/builders/resolvers/component.js +14 -0
- package/server/services/builders/resolvers/dynamic-zone.js +9 -0
- package/server/services/builders/resolvers/index.js +18 -0
- package/server/services/builders/resolvers/mutation.js +33 -0
- package/server/services/builders/resolvers/query.js +19 -0
- package/server/services/builders/response-collection.js +43 -0
- package/server/services/builders/response.js +32 -0
- package/server/services/builders/type.js +370 -0
- package/server/services/builders/utils.js +131 -0
- package/server/services/constants.js +147 -0
- package/server/services/content-api/index.js +168 -0
- package/server/services/content-api/policy.js +59 -0
- package/server/services/content-api/register-functions/collection-type.js +72 -0
- package/server/services/content-api/register-functions/component.js +15 -0
- package/server/services/content-api/register-functions/content-type/dynamic-zones.js +36 -0
- package/server/services/content-api/register-functions/content-type/enums.js +33 -0
- package/server/services/content-api/register-functions/content-type/filters.js +15 -0
- package/server/services/content-api/register-functions/content-type/index.js +13 -0
- package/server/services/content-api/register-functions/content-type/inputs.js +21 -0
- package/server/services/content-api/register-functions/index.js +22 -0
- package/server/services/content-api/register-functions/internals.js +13 -0
- package/server/services/content-api/register-functions/polymorphic.js +69 -0
- package/server/services/content-api/register-functions/scalars.js +14 -0
- package/server/services/content-api/register-functions/single-type.js +72 -0
- package/server/services/content-api/wrap-resolvers.js +146 -0
- package/server/services/extension/extension.js +95 -0
- package/server/services/extension/index.js +5 -0
- package/server/services/extension/shadow-crud-manager.js +159 -0
- package/server/services/format/index.js +7 -0
- package/server/services/format/return-types.js +27 -0
- package/server/services/index.js +21 -0
- package/server/services/internals/args/index.js +11 -0
- package/server/services/internals/args/pagination.js +19 -0
- package/server/services/internals/args/publication-state.js +12 -0
- package/server/services/internals/args/sort.js +10 -0
- package/server/services/internals/helpers/get-enabled-scalars.js +15 -0
- package/server/services/internals/helpers/index.js +7 -0
- package/server/services/internals/index.js +13 -0
- package/server/services/internals/scalars/index.js +18 -0
- package/server/services/internals/scalars/time.js +35 -0
- package/server/services/internals/types/error.js +33 -0
- package/server/services/internals/types/filters.js +39 -0
- package/server/services/internals/types/index.js +29 -0
- package/server/services/internals/types/pagination.js +24 -0
- package/server/services/internals/types/publication-state.js +24 -0
- package/server/services/internals/types/response-collection-meta.js +38 -0
- package/server/services/type-registry.js +103 -0
- package/server/services/utils/attributes.js +84 -0
- package/server/services/utils/index.js +11 -0
- package/server/services/utils/mappers/entity-to-response-entity.js +12 -0
- package/server/services/utils/mappers/graphql-filters-to-strapi-query.js +107 -0
- package/server/services/utils/mappers/graphql-scalar-to-operators.js +17 -0
- package/server/services/utils/mappers/index.js +13 -0
- package/server/services/utils/mappers/strapi-scalar-to-graphql-scalar.js +24 -0
- package/server/services/utils/naming.js +282 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +11 -0
- package/config/routes.json +0 -3
- package/config/schema.graphql +0 -1
- package/config/settings.json +0 -12
- package/controllers/GraphQL.js +0 -9
- package/hooks/graphql/defaults.json +0 -5
- package/hooks/graphql/index.js +0 -174
- package/hooks/graphql/load-config.js +0 -42
- package/services/build-aggregation.js +0 -565
- package/services/data-loaders.js +0 -55
- package/services/naming.js +0 -15
- package/services/resolvers-builder.js +0 -204
- package/services/schema-definitions.js +0 -131
- package/services/schema-generator.js +0 -178
- package/services/shadow-crud.js +0 -612
- package/services/type-builder.js +0 -311
- package/services/utils.js +0 -200
- package/types/dynamiczoneScalar.js +0 -40
- package/types/publication-state.js +0 -16
- package/types/time.js +0 -26
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { list } = require('nexus');
|
|
4
|
+
|
|
5
|
+
const NOT_IN_FIELD_NAME = 'notIn';
|
|
6
|
+
|
|
7
|
+
module.exports = () => ({
|
|
8
|
+
fieldName: NOT_IN_FIELD_NAME,
|
|
9
|
+
|
|
10
|
+
strapiOperator: '$notIn',
|
|
11
|
+
|
|
12
|
+
add(t, type) {
|
|
13
|
+
t.field(NOT_IN_FIELD_NAME, { type: list(type) });
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const NOT_FIELD_NAME = 'not';
|
|
4
|
+
|
|
5
|
+
module.exports = ({ strapi }) => ({
|
|
6
|
+
fieldName: NOT_FIELD_NAME,
|
|
7
|
+
|
|
8
|
+
strapiOperator: '$not',
|
|
9
|
+
|
|
10
|
+
add(t, type) {
|
|
11
|
+
const { naming, attributes } = strapi.plugin('graphql').service('utils');
|
|
12
|
+
|
|
13
|
+
if (attributes.isGraphQLScalar({ type })) {
|
|
14
|
+
t.field(NOT_FIELD_NAME, { type: naming.getScalarFilterInputTypeName(type) });
|
|
15
|
+
} else {
|
|
16
|
+
t.field(NOT_FIELD_NAME, { type });
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { list } = require('nexus');
|
|
4
|
+
|
|
5
|
+
const OR_FIELD_NAME = 'or';
|
|
6
|
+
|
|
7
|
+
module.exports = () => ({
|
|
8
|
+
fieldName: OR_FIELD_NAME,
|
|
9
|
+
|
|
10
|
+
strapiOperator: '$or',
|
|
11
|
+
|
|
12
|
+
add(t, type) {
|
|
13
|
+
t.field(OR_FIELD_NAME, { type: list(type) });
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { unionType } = require('nexus');
|
|
4
|
+
const { prop } = require('lodash/fp');
|
|
5
|
+
|
|
6
|
+
module.exports = ({ strapi, registry }) => {
|
|
7
|
+
const { naming } = strapi.plugin('graphql').service('utils');
|
|
8
|
+
const { KINDS, GENERIC_MORPH_TYPENAME } = strapi.plugin('graphql').service('constants');
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
buildGenericMorphDefinition() {
|
|
12
|
+
return unionType({
|
|
13
|
+
name: GENERIC_MORPH_TYPENAME,
|
|
14
|
+
|
|
15
|
+
resolveType(obj) {
|
|
16
|
+
const contentType = strapi.getModel(obj.__type);
|
|
17
|
+
|
|
18
|
+
if (!contentType) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (contentType.modelType === 'component') {
|
|
23
|
+
return naming.getComponentName(contentType);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return naming.getTypeName(contentType);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
definition(t) {
|
|
30
|
+
const members = registry
|
|
31
|
+
// Resolve every content-type or component
|
|
32
|
+
.where(({ config }) => [KINDS.type, KINDS.component].includes(config.kind))
|
|
33
|
+
// Only keep their name (the type's id)
|
|
34
|
+
.map(prop('name'));
|
|
35
|
+
|
|
36
|
+
t.members(...members);
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { merge, map, pipe, reduce } = require('lodash/fp');
|
|
4
|
+
|
|
5
|
+
// Builders Factories
|
|
6
|
+
|
|
7
|
+
const enums = require('./enums');
|
|
8
|
+
const dynamicZone = require('./dynamic-zones');
|
|
9
|
+
const entity = require('./entity');
|
|
10
|
+
const entityMeta = require('./entity-meta');
|
|
11
|
+
const type = require('./type');
|
|
12
|
+
const response = require('./response');
|
|
13
|
+
const responseCollection = require('./response-collection');
|
|
14
|
+
const relationResponseCollection = require('./relation-response-collection');
|
|
15
|
+
const queries = require('./queries');
|
|
16
|
+
const mutations = require('./mutations');
|
|
17
|
+
const filters = require('./filters');
|
|
18
|
+
const inputs = require('./input');
|
|
19
|
+
const genericMorph = require('./generic-morph');
|
|
20
|
+
const resolvers = require('./resolvers');
|
|
21
|
+
|
|
22
|
+
// Misc
|
|
23
|
+
|
|
24
|
+
const operators = require('./filters/operators');
|
|
25
|
+
const utils = require('./utils');
|
|
26
|
+
|
|
27
|
+
const buildersFactories = [
|
|
28
|
+
enums,
|
|
29
|
+
dynamicZone,
|
|
30
|
+
entity,
|
|
31
|
+
entityMeta,
|
|
32
|
+
type,
|
|
33
|
+
response,
|
|
34
|
+
responseCollection,
|
|
35
|
+
relationResponseCollection,
|
|
36
|
+
queries,
|
|
37
|
+
mutations,
|
|
38
|
+
filters,
|
|
39
|
+
inputs,
|
|
40
|
+
genericMorph,
|
|
41
|
+
resolvers,
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
module.exports = ({ strapi }) => {
|
|
45
|
+
const buildersMap = new Map();
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
/**
|
|
49
|
+
* Instantiate every builder with a strapi instance & a type registry
|
|
50
|
+
* @param {string} name
|
|
51
|
+
* @param {object} registry
|
|
52
|
+
*/
|
|
53
|
+
new(name, registry) {
|
|
54
|
+
const context = { strapi, registry };
|
|
55
|
+
|
|
56
|
+
const builders = pipe(
|
|
57
|
+
// Create a new instance of every builders
|
|
58
|
+
map(factory => factory(context)),
|
|
59
|
+
// Merge every builder into the same object
|
|
60
|
+
reduce(merge, {})
|
|
61
|
+
).call(null, buildersFactories);
|
|
62
|
+
|
|
63
|
+
buildersMap.set(name, builders);
|
|
64
|
+
|
|
65
|
+
return builders;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Delete a set of builders instances from
|
|
70
|
+
* the builders map for a given name
|
|
71
|
+
* @param {string} name
|
|
72
|
+
*/
|
|
73
|
+
delete(name) {
|
|
74
|
+
buildersMap.delete(name);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Retrieve a set of builders instances from
|
|
79
|
+
* the builders map for a given name
|
|
80
|
+
* @param {string} name
|
|
81
|
+
*/
|
|
82
|
+
get(name) {
|
|
83
|
+
return buildersMap.get(name);
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
filters: {
|
|
87
|
+
operators: operators({ strapi }),
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
utils: utils({ strapi }),
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { inputObjectType, nonNull } = require('nexus');
|
|
4
|
+
|
|
5
|
+
module.exports = context => {
|
|
6
|
+
const { strapi } = context;
|
|
7
|
+
|
|
8
|
+
const { naming, mappers, attributes } = strapi.plugin('graphql').service('utils');
|
|
9
|
+
const extension = strapi.plugin('graphql').service('extension');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
getComponentInputName,
|
|
13
|
+
getContentTypeInputName,
|
|
14
|
+
getEnumName,
|
|
15
|
+
getDynamicZoneInputName,
|
|
16
|
+
} = naming;
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
isStrapiScalar,
|
|
20
|
+
isRelation,
|
|
21
|
+
isMorphRelation,
|
|
22
|
+
isMedia,
|
|
23
|
+
isEnumeration,
|
|
24
|
+
isComponent,
|
|
25
|
+
isDynamicZone,
|
|
26
|
+
} = attributes;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
buildInputType(contentType) {
|
|
30
|
+
const { attributes, modelType } = contentType;
|
|
31
|
+
|
|
32
|
+
const name = (modelType === 'component'
|
|
33
|
+
? getComponentInputName
|
|
34
|
+
: getContentTypeInputName
|
|
35
|
+
).call(null, contentType);
|
|
36
|
+
|
|
37
|
+
return inputObjectType({
|
|
38
|
+
name,
|
|
39
|
+
|
|
40
|
+
definition(t) {
|
|
41
|
+
const isFieldEnabled = fieldName => {
|
|
42
|
+
return extension
|
|
43
|
+
.shadowCRUD(contentType.uid)
|
|
44
|
+
.field(fieldName)
|
|
45
|
+
.hasInputEnabled();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const validAttributes = Object.entries(attributes).filter(([attributeName]) =>
|
|
49
|
+
isFieldEnabled(attributeName)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Add the ID for the component to enable inplace updates
|
|
53
|
+
if (modelType === 'component' && isFieldEnabled('id')) {
|
|
54
|
+
t.id('id');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
validAttributes.forEach(([attributeName, attribute]) => {
|
|
58
|
+
// Scalars
|
|
59
|
+
if (isStrapiScalar(attribute)) {
|
|
60
|
+
const gqlScalar = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
|
61
|
+
|
|
62
|
+
t.field(attributeName, { type: gqlScalar });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Media
|
|
66
|
+
else if (isMedia(attribute)) {
|
|
67
|
+
const isMultiple = attribute.multiple === true;
|
|
68
|
+
|
|
69
|
+
if (extension.shadowCRUD('plugin::upload.file').isDisabled()) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
isMultiple ? t.list.id(attributeName) : t.id(attributeName);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Regular Relations (ignore polymorphic relations)
|
|
77
|
+
else if (isRelation(attribute) && !isMorphRelation(attribute)) {
|
|
78
|
+
if (extension.shadowCRUD(attribute.target).isDisabled()) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const isToManyRelation = attribute.relation.endsWith('Many');
|
|
83
|
+
|
|
84
|
+
isToManyRelation ? t.list.id(attributeName) : t.id(attributeName);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Enums
|
|
88
|
+
else if (isEnumeration(attribute)) {
|
|
89
|
+
const enumTypeName = getEnumName(contentType, attributeName);
|
|
90
|
+
|
|
91
|
+
t.field(attributeName, { type: enumTypeName });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Components
|
|
95
|
+
else if (isComponent(attribute)) {
|
|
96
|
+
const isRepeatable = attribute.repeatable === true;
|
|
97
|
+
const component = strapi.components[attribute.component];
|
|
98
|
+
const componentInputType = getComponentInputName(component);
|
|
99
|
+
|
|
100
|
+
if (isRepeatable) {
|
|
101
|
+
t.list.field(attributeName, { type: componentInputType });
|
|
102
|
+
} else {
|
|
103
|
+
t.field(attributeName, { type: componentInputType });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Dynamic Zones
|
|
108
|
+
else if (isDynamicZone(attribute)) {
|
|
109
|
+
const dzInputName = getDynamicZoneInputName(contentType, attributeName);
|
|
110
|
+
|
|
111
|
+
t.list.field(attributeName, { type: nonNull(dzInputName) });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { extendType, nonNull } = require('nexus');
|
|
4
|
+
|
|
5
|
+
module.exports = ({ strapi }) => {
|
|
6
|
+
const { service: getService } = strapi.plugin('graphql');
|
|
7
|
+
|
|
8
|
+
const { naming } = getService('utils');
|
|
9
|
+
const { transformArgs } = getService('builders').utils;
|
|
10
|
+
const { toEntityResponse } = getService('format').returnTypes;
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
getCreateMutationTypeName,
|
|
14
|
+
getUpdateMutationTypeName,
|
|
15
|
+
getDeleteMutationTypeName,
|
|
16
|
+
getEntityResponseName,
|
|
17
|
+
getContentTypeInputName,
|
|
18
|
+
} = naming;
|
|
19
|
+
|
|
20
|
+
const addCreateMutation = (t, contentType) => {
|
|
21
|
+
const { uid } = contentType;
|
|
22
|
+
|
|
23
|
+
const createMutationName = getCreateMutationTypeName(contentType);
|
|
24
|
+
const responseTypeName = getEntityResponseName(contentType);
|
|
25
|
+
|
|
26
|
+
t.field(createMutationName, {
|
|
27
|
+
type: responseTypeName,
|
|
28
|
+
|
|
29
|
+
args: {
|
|
30
|
+
// Create payload
|
|
31
|
+
data: nonNull(getContentTypeInputName(contentType)),
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async resolve(parent, args) {
|
|
35
|
+
const transformedArgs = transformArgs(args, { contentType });
|
|
36
|
+
|
|
37
|
+
const { create } = getService('builders')
|
|
38
|
+
.get('content-api')
|
|
39
|
+
.buildMutationsResolvers({ contentType });
|
|
40
|
+
|
|
41
|
+
const value = await create(parent, transformedArgs);
|
|
42
|
+
|
|
43
|
+
return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const addUpdateMutation = (t, contentType) => {
|
|
49
|
+
const { uid } = contentType;
|
|
50
|
+
|
|
51
|
+
const updateMutationName = getUpdateMutationTypeName(contentType);
|
|
52
|
+
const responseTypeName = getEntityResponseName(contentType);
|
|
53
|
+
|
|
54
|
+
// todo[v4]: Don't allow to filter using every unique attributes for now
|
|
55
|
+
// Only authorize filtering using unique scalar fields for updateOne queries
|
|
56
|
+
// const uniqueAttributes = getUniqueAttributesFiltersMap(attributes);
|
|
57
|
+
|
|
58
|
+
t.field(updateMutationName, {
|
|
59
|
+
type: responseTypeName,
|
|
60
|
+
|
|
61
|
+
args: {
|
|
62
|
+
// Query args
|
|
63
|
+
id: nonNull('ID'),
|
|
64
|
+
// todo[v4]: Don't allow to filter using every unique attributes for now
|
|
65
|
+
// ...uniqueAttributes,
|
|
66
|
+
|
|
67
|
+
// Update payload
|
|
68
|
+
data: nonNull(getContentTypeInputName(contentType)),
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
async resolve(parent, args) {
|
|
72
|
+
const transformedArgs = transformArgs(args, { contentType });
|
|
73
|
+
|
|
74
|
+
const { update } = getService('builders')
|
|
75
|
+
.get('content-api')
|
|
76
|
+
.buildMutationsResolvers({ contentType });
|
|
77
|
+
|
|
78
|
+
const value = await update(parent, transformedArgs);
|
|
79
|
+
|
|
80
|
+
return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const addDeleteMutation = (t, contentType) => {
|
|
86
|
+
const { uid } = contentType;
|
|
87
|
+
|
|
88
|
+
const deleteMutationName = getDeleteMutationTypeName(contentType);
|
|
89
|
+
const responseTypeName = getEntityResponseName(contentType);
|
|
90
|
+
|
|
91
|
+
// todo[v4]: Don't allow to filter using every unique attributes for now
|
|
92
|
+
// Only authorize filtering using unique scalar fields for updateOne queries
|
|
93
|
+
// const uniqueAttributes = getUniqueAttributesFiltersMap(attributes);
|
|
94
|
+
|
|
95
|
+
t.field(deleteMutationName, {
|
|
96
|
+
type: responseTypeName,
|
|
97
|
+
|
|
98
|
+
args: {
|
|
99
|
+
// Query args
|
|
100
|
+
id: nonNull('ID'),
|
|
101
|
+
// todo[v4]: Don't allow to filter using every unique attributes for now
|
|
102
|
+
// ...uniqueAttributes,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async resolve(parent, args) {
|
|
106
|
+
const transformedArgs = transformArgs(args, { contentType });
|
|
107
|
+
|
|
108
|
+
const { delete: deleteResolver } = getService('builders')
|
|
109
|
+
.get('content-api')
|
|
110
|
+
.buildMutationsResolvers({ contentType });
|
|
111
|
+
|
|
112
|
+
const value = await deleteResolver(parent, args);
|
|
113
|
+
|
|
114
|
+
return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
buildCollectionTypeMutations(contentType) {
|
|
121
|
+
const createMutationName = `Mutation.${getCreateMutationTypeName(contentType)}`;
|
|
122
|
+
const updateMutationName = `Mutation.${getUpdateMutationTypeName(contentType)}`;
|
|
123
|
+
const deleteMutationName = `Mutation.${getDeleteMutationTypeName(contentType)}`;
|
|
124
|
+
|
|
125
|
+
const extension = getService('extension');
|
|
126
|
+
|
|
127
|
+
const registerAuthConfig = (action, auth) => {
|
|
128
|
+
return extension.use({ resolversConfig: { [action]: { auth } } });
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const isActionEnabled = action => {
|
|
132
|
+
return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const isCreateEnabled = isActionEnabled('create');
|
|
136
|
+
const isUpdateEnabled = isActionEnabled('update');
|
|
137
|
+
const isDeleteEnabled = isActionEnabled('delete');
|
|
138
|
+
|
|
139
|
+
if (isCreateEnabled) {
|
|
140
|
+
registerAuthConfig(createMutationName, { scope: [`${contentType.uid}.create`] });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (isUpdateEnabled) {
|
|
144
|
+
registerAuthConfig(updateMutationName, { scope: [`${contentType.uid}.update`] });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (isDeleteEnabled) {
|
|
148
|
+
registerAuthConfig(deleteMutationName, { scope: [`${contentType.uid}.delete`] });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return extendType({
|
|
152
|
+
type: 'Mutation',
|
|
153
|
+
|
|
154
|
+
definition(t) {
|
|
155
|
+
if (isCreateEnabled) {
|
|
156
|
+
addCreateMutation(t, contentType);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (isUpdateEnabled) {
|
|
160
|
+
addUpdateMutation(t, contentType);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (isDeleteEnabled) {
|
|
164
|
+
addDeleteMutation(t, contentType);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const createCollectionTypeMutationsBuilder = require('./collection-type');
|
|
4
|
+
const createSingleTypeMutationsBuilder = require('./single-type');
|
|
5
|
+
|
|
6
|
+
module.exports = context => ({
|
|
7
|
+
...createCollectionTypeMutationsBuilder(context),
|
|
8
|
+
...createSingleTypeMutationsBuilder(context),
|
|
9
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { extendType, nonNull } = require('nexus');
|
|
4
|
+
const { omit, isNil } = require('lodash/fp');
|
|
5
|
+
const { getNonWritableAttributes } = require('@strapi/utils').contentTypes;
|
|
6
|
+
|
|
7
|
+
const sanitizeInput = (contentType, data) => omit(getNonWritableAttributes(contentType), data);
|
|
8
|
+
|
|
9
|
+
module.exports = ({ strapi }) => {
|
|
10
|
+
const { service: getService } = strapi.plugin('graphql');
|
|
11
|
+
|
|
12
|
+
const { naming } = getService('utils');
|
|
13
|
+
const { transformArgs } = getService('builders').utils;
|
|
14
|
+
const { toEntityResponse } = getService('format').returnTypes;
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
getUpdateMutationTypeName,
|
|
18
|
+
getEntityResponseName,
|
|
19
|
+
getContentTypeInputName,
|
|
20
|
+
getDeleteMutationTypeName,
|
|
21
|
+
} = naming;
|
|
22
|
+
|
|
23
|
+
const addUpdateMutation = (t, contentType) => {
|
|
24
|
+
const { uid } = contentType;
|
|
25
|
+
|
|
26
|
+
const updateMutationName = getUpdateMutationTypeName(contentType);
|
|
27
|
+
const responseTypeName = getEntityResponseName(contentType);
|
|
28
|
+
|
|
29
|
+
t.field(updateMutationName, {
|
|
30
|
+
type: responseTypeName,
|
|
31
|
+
|
|
32
|
+
args: {
|
|
33
|
+
// Update payload
|
|
34
|
+
data: nonNull(getContentTypeInputName(contentType)),
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async resolve(parent, args) {
|
|
38
|
+
const transformedArgs = transformArgs(args, { contentType });
|
|
39
|
+
|
|
40
|
+
// Sanitize input data
|
|
41
|
+
Object.assign(transformedArgs, { data: sanitizeInput(contentType, transformedArgs.data) });
|
|
42
|
+
|
|
43
|
+
const { create, update } = getService('builders')
|
|
44
|
+
.get('content-api')
|
|
45
|
+
.buildMutationsResolvers({ contentType });
|
|
46
|
+
|
|
47
|
+
const findParams = omit(['data', 'files'], transformedArgs);
|
|
48
|
+
const entity = await strapi.entityService.findMany(uid, { params: findParams });
|
|
49
|
+
|
|
50
|
+
// Create or update
|
|
51
|
+
const value = isNil(entity)
|
|
52
|
+
? create(parent, transformedArgs)
|
|
53
|
+
: update(uid, { id: entity.id, data: transformedArgs.data });
|
|
54
|
+
|
|
55
|
+
return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const addDeleteMutation = (t, contentType) => {
|
|
61
|
+
const { uid } = contentType;
|
|
62
|
+
|
|
63
|
+
const deleteMutationName = getDeleteMutationTypeName(contentType);
|
|
64
|
+
const responseTypeName = getEntityResponseName(contentType);
|
|
65
|
+
|
|
66
|
+
t.field(deleteMutationName, {
|
|
67
|
+
type: responseTypeName,
|
|
68
|
+
|
|
69
|
+
args: {},
|
|
70
|
+
|
|
71
|
+
async resolve(parent, args) {
|
|
72
|
+
const transformedArgs = transformArgs(args, { contentType });
|
|
73
|
+
|
|
74
|
+
Object.assign(transformedArgs, { data: sanitizeInput(contentType, transformedArgs.data) });
|
|
75
|
+
|
|
76
|
+
const { delete: deleteResolver } = getService('builders')
|
|
77
|
+
.get('content-api')
|
|
78
|
+
.buildMutationsResolvers({ contentType });
|
|
79
|
+
|
|
80
|
+
const params = omit(['data', 'files'], transformedArgs);
|
|
81
|
+
const entity = await strapi.entityService.findMany(uid, { params });
|
|
82
|
+
|
|
83
|
+
if (!entity) {
|
|
84
|
+
throw new Error('Entity not found');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const value = await deleteResolver(parent, { id: entity.id, params });
|
|
88
|
+
|
|
89
|
+
return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
buildSingleTypeMutations(contentType) {
|
|
96
|
+
const updateMutationName = `Mutation.${getUpdateMutationTypeName(contentType)}`;
|
|
97
|
+
const deleteMutationName = `Mutation.${getDeleteMutationTypeName(contentType)}`;
|
|
98
|
+
|
|
99
|
+
const extension = getService('extension');
|
|
100
|
+
|
|
101
|
+
const registerAuthConfig = (action, auth) => {
|
|
102
|
+
return extension.use({ resolversConfig: { [action]: { auth } } });
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const isActionEnabled = action => {
|
|
106
|
+
return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const isUpdateEnabled = isActionEnabled('update');
|
|
110
|
+
const isDeleteEnabled = isActionEnabled('delete');
|
|
111
|
+
|
|
112
|
+
if (isUpdateEnabled) {
|
|
113
|
+
registerAuthConfig(updateMutationName, { scope: [`${contentType.uid}.update`] });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (isDeleteEnabled) {
|
|
117
|
+
registerAuthConfig(deleteMutationName, { scope: [`${contentType.uid}.delete`] });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return extendType({
|
|
121
|
+
type: 'Mutation',
|
|
122
|
+
|
|
123
|
+
definition(t) {
|
|
124
|
+
if (isUpdateEnabled) {
|
|
125
|
+
addUpdateMutation(t, contentType);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isDeleteEnabled) {
|
|
129
|
+
addDeleteMutation(t, contentType);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
};
|