mirror of
https://github.com/strapi/strapi.git
synced 2025-12-12 15:32:42 +00:00
Merge branch 'develop' of github.com:strapi/strapi into fix/single-types-issues
Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
commit
7a4a87fa68
@ -263,6 +263,10 @@ query {
|
||||
|
||||
To simplify and automate the build of the GraphQL schema, we introduced the Shadow CRUD feature. It automatically generates the type definition, queries, mutations and resolvers based on your models. The feature also lets you make complex query with many arguments such as `limit`, `sort`, `start` and `where`.
|
||||
|
||||
::: tip NOTE
|
||||
If you use a local plugin, the controller methods of your plugin are not created by default. In order for the Shadow CRUD to work you have to define them in your controllers for each of your models. You can find examples of controllers `findOne`, `find`, `create`, `update` and `delete` there : [Core controllers](../concepts/controllers.html#core-controllers).
|
||||
:::
|
||||
|
||||
### Example
|
||||
|
||||
If you've generated an API called `Restaurant` using the CLI `strapi generate:api restaurant` or the administration panel, your model looks like this:
|
||||
@ -517,7 +521,7 @@ module.exports = {
|
||||
`,
|
||||
mutation: `
|
||||
attachRestaurantToChef(id: ID, chefID: ID): Restaurant!
|
||||
`
|
||||
`,
|
||||
resolver: {
|
||||
Query: {
|
||||
restaurant: {
|
||||
|
||||
@ -14,7 +14,8 @@
|
||||
},
|
||||
"slug": {
|
||||
"type": "uid",
|
||||
"targetField": "title"
|
||||
"targetField": "title",
|
||||
"required": true
|
||||
},
|
||||
"single": {
|
||||
"model": "file",
|
||||
|
||||
@ -3,6 +3,9 @@ import styled from 'styled-components';
|
||||
const SearchButton = styled.button`
|
||||
padding: 0 10px;
|
||||
line-height: normal;
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default SearchButton;
|
||||
|
||||
@ -3,6 +3,7 @@ import styled from 'styled-components';
|
||||
const LeftMenuListLink = styled.div`
|
||||
max-height: 180px;
|
||||
margin-bottom: 19px;
|
||||
margin-right: 28px;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ exports[`Admin | containers | EditView should match the snapshot 1`] = `
|
||||
|
||||
.c5 {
|
||||
height: 30px;
|
||||
padding: 0 15px;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
font-size: 1.3rem;
|
||||
line-height: normal;
|
||||
@ -96,7 +96,7 @@ exports[`Admin | containers | EditView should match the snapshot 1`] = `
|
||||
|
||||
.c7 {
|
||||
height: 30px;
|
||||
padding: 0 15px;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
font-size: 1.3rem;
|
||||
line-height: normal;
|
||||
@ -140,7 +140,7 @@ exports[`Admin | containers | EditView should match the snapshot 1`] = `
|
||||
|
||||
.c8 {
|
||||
height: 30px;
|
||||
padding: 0 15px;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
font-size: 1.3rem;
|
||||
line-height: normal;
|
||||
|
||||
@ -36,7 +36,7 @@ exports[`Admin | containers | ListView should match the snapshot 1`] = `
|
||||
|
||||
.c4 {
|
||||
height: 30px;
|
||||
padding: 0 15px;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
font-size: 1.3rem;
|
||||
line-height: normal;
|
||||
|
||||
@ -22,12 +22,12 @@
|
||||
"@babel/preset-env": "^7.4.3",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/runtime": "^7.4.3",
|
||||
"@buffetjs/core": "3.0.5",
|
||||
"@buffetjs/custom": "3.0.5",
|
||||
"@buffetjs/hooks": "3.0.5",
|
||||
"@buffetjs/icons": "3.0.5",
|
||||
"@buffetjs/styles": "3.0.5",
|
||||
"@buffetjs/utils": "3.0.5",
|
||||
"@buffetjs/core": "3.0.6",
|
||||
"@buffetjs/custom": "3.0.6",
|
||||
"@buffetjs/hooks": "3.0.6",
|
||||
"@buffetjs/icons": "3.0.6",
|
||||
"@buffetjs/styles": "3.0.6",
|
||||
"@buffetjs/utils": "3.0.6",
|
||||
"@fortawesome/fontawesome-free": "^5.11.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.11.2",
|
||||
@ -116,4 +116,4 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"gitHead": "c85658a19b8fef0f3164c19693a45db305dc07a9"
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,17 @@ const List = styled.ul`
|
||||
margin-bottom: 0;
|
||||
padding-left: 0;
|
||||
max-height: 178px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
::-webkit-scrollbar {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
margin-bottom: 2px;
|
||||
|
||||
@ -7,7 +7,7 @@ import { useDebounce, useClickAwayListener } from '@buffetjs/hooks';
|
||||
import styled from 'styled-components';
|
||||
import { request, LoadingIndicator } from 'strapi-helper-plugin';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import pluginId from '../../pluginId';
|
||||
import getRequestUrl from '../../utils/getRequestUrl';
|
||||
@ -42,13 +42,12 @@ const InputUID = ({
|
||||
error: inputError,
|
||||
name,
|
||||
onChange,
|
||||
required,
|
||||
validations,
|
||||
value,
|
||||
editable,
|
||||
...inputProps
|
||||
}) => {
|
||||
const { modifiedData, initialData } = useDataManager();
|
||||
const { modifiedData, initialData, layout } = useDataManager();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [availability, setAvailability] = useState(null);
|
||||
const [isSuggestionOpen, setIsSuggestionOpen] = useState(true);
|
||||
@ -59,9 +58,10 @@ const InputUID = ({
|
||||
const wrapperRef = useRef(null);
|
||||
const generateUid = useRef();
|
||||
const initialValue = initialData[name];
|
||||
const isCreation = isEmpty(initialData);
|
||||
const createdAtName = get(layout, ['schema', 'options', 'timestamps'], ['created_at'])[0];
|
||||
const isCreation = !initialData[createdAtName];
|
||||
|
||||
generateUid.current = async (changeInitialData = false) => {
|
||||
generateUid.current = async (shouldSetInitialValue = false) => {
|
||||
setIsLoading(true);
|
||||
const requestURL = getRequestUrl('explorer/uid/generate');
|
||||
try {
|
||||
@ -74,7 +74,7 @@ const InputUID = ({
|
||||
},
|
||||
});
|
||||
|
||||
onChange({ target: { name, value: data, type: 'text' } }, changeInitialData);
|
||||
onChange({ target: { name, value: data, type: 'text' } }, shouldSetInitialValue);
|
||||
setIsLoading(false);
|
||||
} catch (err) {
|
||||
console.error({ err });
|
||||
@ -107,7 +107,7 @@ const InputUID = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!value && required) {
|
||||
if (!value && validations.required) {
|
||||
generateUid.current(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@ -144,9 +144,15 @@ const InputUID = ({
|
||||
}, [availability]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCustomized && isCreation && debouncedTargetFieldValue) {
|
||||
generateUid.current();
|
||||
if (
|
||||
!isCustomized &&
|
||||
isCreation &&
|
||||
debouncedTargetFieldValue &&
|
||||
modifiedData[attribute.targetField]
|
||||
) {
|
||||
generateUid.current(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedTargetFieldValue, isCustomized, isCreation]);
|
||||
|
||||
useClickAwayListener(wrapperRef, () => setIsSuggestionOpen(false));
|
||||
@ -221,7 +227,7 @@ const InputUID = ({
|
||||
<RegenerateButton
|
||||
onMouseEnter={handleGenerateMouseEnter}
|
||||
onMouseLeave={handleGenerateMouseLeave}
|
||||
onClick={generateUid.current}
|
||||
onClick={() => generateUid.current()}
|
||||
>
|
||||
{isLoading ? (
|
||||
<LoadingIndicator small />
|
||||
|
||||
@ -184,7 +184,6 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
validations={validations}
|
||||
value={inputValue}
|
||||
withDefaultValue={false}
|
||||
required={isRequired}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
@ -3,7 +3,6 @@ import styled from 'styled-components';
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
margin-bottom: 27px;
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
@ -98,7 +98,7 @@ const Header = () => {
|
||||
title: {
|
||||
label: headerTitle && headerTitle.toString(),
|
||||
},
|
||||
content: isSingleType ? `${formatMessage({ id: `${pluginId}.api.id` })} : ${layout.apiID}` : '',
|
||||
content: `${formatMessage({ id: `${pluginId}.api.id` })} : ${layout.apiID}`,
|
||||
actions: getHeaderActions(),
|
||||
};
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ const EditViewDataManagerProvider = ({ allLayoutData, children, redirectToPrevio
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = ({ target: { name, value, type } }, initialValue = false) => {
|
||||
const handleChange = ({ target: { name, value, type } }, shouldSetInitialValue = false) => {
|
||||
let inputValue = value;
|
||||
|
||||
// Empty string is not a valid date,
|
||||
@ -214,7 +214,7 @@ const EditViewDataManagerProvider = ({ allLayoutData, children, redirectToPrevio
|
||||
type: 'ON_CHANGE',
|
||||
keys: name.split('.'),
|
||||
value: inputValue,
|
||||
initialValue,
|
||||
shouldSetInitialValue,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -139,7 +139,7 @@ const reducer = (state, action) => {
|
||||
// that needs an asynchronous initial value like the UID field
|
||||
// This is just a temporary patch.
|
||||
// TODO : Refactor the default form creation (workflow) to accept async default values.
|
||||
if (action.initialValue) {
|
||||
if (action.shouldSetInitialValue) {
|
||||
newState = state.updateIn(['initialData', ...action.keys], () => {
|
||||
return action.value;
|
||||
});
|
||||
|
||||
@ -7,8 +7,15 @@
|
||||
const _ = require('lodash');
|
||||
const compose = require('koa-compose');
|
||||
|
||||
const { convertToParams, convertToQuery, amountLimiting } = require('./utils');
|
||||
const { policy: policyUtils } = require('strapi-utils');
|
||||
const {
|
||||
convertToParams,
|
||||
convertToQuery,
|
||||
amountLimiting,
|
||||
getAction,
|
||||
getActionDetails,
|
||||
isResolvablePath,
|
||||
} = require('./utils');
|
||||
|
||||
const buildMutation = (mutationName, config) => {
|
||||
const { resolver, resolverOf, transformOutput = _.identity } = config;
|
||||
@ -147,70 +154,9 @@ const buildQueryContext = ({ options, graphqlContext }) => {
|
||||
return { ctx, opts };
|
||||
};
|
||||
|
||||
const getAction = resolver => {
|
||||
if (!_.isString(resolver)) {
|
||||
throw new Error(`Error building query. Expected a string, got ${resolver}`);
|
||||
}
|
||||
|
||||
const actionDetails = getActionDetails(resolver);
|
||||
const actionFn = getActionFn(actionDetails);
|
||||
|
||||
if (!actionFn) {
|
||||
throw new Error(
|
||||
`[GraphQL] Cannot find action "${resolver}". Check your graphql configurations.`
|
||||
);
|
||||
}
|
||||
|
||||
return actionFn;
|
||||
};
|
||||
|
||||
const getActionFn = details => {
|
||||
const { controller, action, plugin, api } = details;
|
||||
|
||||
if (plugin) {
|
||||
return _.get(strapi.plugins, [_.toLower(plugin), 'controllers', _.toLower(controller), action]);
|
||||
}
|
||||
|
||||
return _.get(strapi.api, [_.toLower(api), 'controllers', _.toLower(controller), action]);
|
||||
};
|
||||
|
||||
const getActionDetails = resolver => {
|
||||
if (resolver.startsWith('plugins::')) {
|
||||
const [, path] = resolver.split('::');
|
||||
const [plugin, controller, action] = path.split('.');
|
||||
|
||||
return { plugin, controller, action };
|
||||
}
|
||||
|
||||
if (resolver.startsWith('application::')) {
|
||||
const [, path] = resolver.split('::');
|
||||
const [api, controller, action] = path.split('.');
|
||||
|
||||
return { api, controller, action };
|
||||
}
|
||||
|
||||
const args = resolver.split('.');
|
||||
|
||||
if (args.length === 3) {
|
||||
const [api, controller, action] = args;
|
||||
return { api, controller, action };
|
||||
}
|
||||
|
||||
// if direct api access
|
||||
if (args.length === 2) {
|
||||
const [controller, action] = args;
|
||||
return { api: controller, controller, action };
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`[GraphQL] Could not find action for resolver "${resolver}". Check your graphql configurations.`
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a resolverPath (resolver or resovlerOf) might be resolved
|
||||
*/
|
||||
const isResolvablePath = path => _.isString(path) && !_.isEmpty(path);
|
||||
|
||||
const getPolicies = config => {
|
||||
const { resolver, policies = [], resolverOf } = config;
|
||||
|
||||
@ -16,13 +16,14 @@ const { mergeSchemas, convertToParams, convertToQuery, amountLimiting } = requir
|
||||
const { toSDL, getTypeDescription } = require('./schema-definitions');
|
||||
const { toSingular, toPlural } = require('./naming');
|
||||
const { buildQuery, buildMutation } = require('./resolvers-builder');
|
||||
const { actionExists } = require('./utils');
|
||||
|
||||
const isQueryEnabled = (schema, name) => {
|
||||
return _.get(schema, ['resolver', 'Query', name]) !== false;
|
||||
return _.get(schema, `resolver.Query.${name}`) !== false;
|
||||
};
|
||||
|
||||
const isMutationEnabled = (schema, name) => {
|
||||
return _.get(schema, ['resolver', 'Mutation', name]) !== false;
|
||||
return _.get(schema, `resolver.Mutation.${name}`) !== false;
|
||||
};
|
||||
|
||||
const buildTypeDefObj = model => {
|
||||
@ -172,7 +173,7 @@ const buildAssocResolvers = model => {
|
||||
if (association.type === 'model') {
|
||||
params[targetModel.primaryKey] = _.get(
|
||||
obj,
|
||||
[association.alias, targetModel.primaryKey],
|
||||
`${association.alias}.${targetModel.primaryKey}`,
|
||||
obj[association.alias]
|
||||
);
|
||||
} else {
|
||||
@ -289,7 +290,7 @@ const buildSingleType = model => {
|
||||
|
||||
const _schema = _.cloneDeep(_.get(strapi.plugins, 'graphql.config._schema.graphql', {}));
|
||||
|
||||
const globalType = _.get(_schema, ['type', model.globalId], {});
|
||||
const globalType = _.get(_schema, `type.${model.globalId}`, {});
|
||||
|
||||
const localSchema = buildModelDefinition(model, globalType);
|
||||
|
||||
@ -308,7 +309,7 @@ const buildSingleType = model => {
|
||||
Query: {
|
||||
[singularName]: buildQuery(singularName, {
|
||||
resolver: `${uid}.find`,
|
||||
..._.get(_schema, ['resolver', 'Query', singularName], {}),
|
||||
..._.get(_schema, `resolver.Query.${singularName}`, {}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -320,9 +321,9 @@ const buildSingleType = model => {
|
||||
|
||||
// build every mutation
|
||||
['update', 'delete'].forEach(action => {
|
||||
const mutationScheam = buildMutationTypeDef({ model, action }, { _schema });
|
||||
const mutationSchema = buildMutationTypeDef({ model, action }, { _schema });
|
||||
|
||||
mergeSchemas(localSchema, mutationScheam);
|
||||
mergeSchemas(localSchema, mutationSchema);
|
||||
});
|
||||
|
||||
return localSchema;
|
||||
@ -336,7 +337,7 @@ const buildCollectionType = model => {
|
||||
|
||||
const _schema = _.cloneDeep(_.get(strapi.plugins, 'graphql.config._schema.graphql', {}));
|
||||
|
||||
const globalType = _.get(_schema, ['type', model.globalId], {});
|
||||
const globalType = _.get(_schema, `type.${model.globalId}`, {});
|
||||
|
||||
const localSchema = {
|
||||
definition: '',
|
||||
@ -370,50 +371,52 @@ const buildCollectionType = model => {
|
||||
}
|
||||
|
||||
if (isQueryEnabled(_schema, singularName)) {
|
||||
_.merge(localSchema, {
|
||||
query: {
|
||||
[`${singularName}(id: ID!)`]: model.globalId,
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
[singularName]: buildQuery(singularName, {
|
||||
resolver: `${uid}.findOne`,
|
||||
..._.get(_schema, ['resolver', 'Query', singularName], {}),
|
||||
}),
|
||||
const resolverOpts = {
|
||||
resolver: `${uid}.findOne`,
|
||||
..._.get(_schema, `resolver.Query.${pluralName}`, {}),
|
||||
};
|
||||
if (actionExists(resolverOpts)) {
|
||||
_.merge(localSchema, {
|
||||
query: {
|
||||
[`${singularName}(id: ID!)`]: model.globalId,
|
||||
},
|
||||
},
|
||||
});
|
||||
resolvers: {
|
||||
Query: {
|
||||
[singularName]: buildQuery(singularName, resolverOpts),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isQueryEnabled(_schema, pluralName)) {
|
||||
const resolverOpts = {
|
||||
resolver: `${uid}.find`,
|
||||
..._.get(_schema, ['resolver', 'Query', pluralName], {}),
|
||||
..._.get(_schema, `resolver.Query.${pluralName}`, {}),
|
||||
};
|
||||
|
||||
const resolverFn = buildQuery(pluralName, resolverOpts);
|
||||
|
||||
_.merge(localSchema, {
|
||||
query: {
|
||||
[`${pluralName}(sort: String, limit: Int, start: Int, where: JSON)`]: `[${model.globalId}]`,
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
[pluralName]: resolverFn,
|
||||
if (actionExists(resolverOpts)) {
|
||||
_.merge(localSchema, {
|
||||
query: {
|
||||
[`${pluralName}(sort: String, limit: Int, start: Int, where: JSON)`]: `[${model.globalId}]`,
|
||||
},
|
||||
},
|
||||
});
|
||||
resolvers: {
|
||||
Query: {
|
||||
[pluralName]: buildQuery(pluralName, resolverOpts),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Generation the aggregation for the given model
|
||||
const aggregationSchema = formatModelConnectionsGQL({
|
||||
fields: typeDefObj,
|
||||
model,
|
||||
name: modelName,
|
||||
resolver: resolverOpts,
|
||||
plugin,
|
||||
});
|
||||
// Generate the aggregation for the given model
|
||||
const aggregationSchema = formatModelConnectionsGQL({
|
||||
fields: typeDefObj,
|
||||
model,
|
||||
name: modelName,
|
||||
resolver: resolverOpts,
|
||||
plugin,
|
||||
});
|
||||
|
||||
mergeSchemas(localSchema, aggregationSchema);
|
||||
mergeSchemas(localSchema, aggregationSchema);
|
||||
}
|
||||
}
|
||||
|
||||
// Add model Input definition.
|
||||
@ -421,9 +424,8 @@ const buildCollectionType = model => {
|
||||
|
||||
// build every mutation
|
||||
['create', 'update', 'delete'].forEach(action => {
|
||||
const mutationScheam = buildMutationTypeDef({ model, action }, { _schema });
|
||||
|
||||
mergeSchemas(localSchema, mutationScheam);
|
||||
const mutationSchema = buildMutationTypeDef({ model, action }, { _schema });
|
||||
mergeSchemas(localSchema, mutationSchema);
|
||||
});
|
||||
|
||||
return localSchema;
|
||||
@ -433,10 +435,19 @@ const buildCollectionType = model => {
|
||||
// - Implement batch methods (need to update the content-manager as well).
|
||||
// - Implement nested transactional methods (create/update).
|
||||
const buildMutationTypeDef = ({ model, action }, { _schema }) => {
|
||||
const { uid } = model;
|
||||
const capitalizedName = _.upperFirst(toSingular(model.modelName));
|
||||
const mutationName = `${action}${capitalizedName}`;
|
||||
|
||||
const resolverOpts = {
|
||||
resolver: `${model.uid}.${action}`,
|
||||
transformOutput: result => ({ [toSingular(model.modelName)]: result }),
|
||||
..._.get(_schema, `resolver.Mutation.${mutationName}`, {}),
|
||||
};
|
||||
|
||||
if (!actionExists(resolverOpts)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const definition = types.generateInputPayloadArguments({
|
||||
model,
|
||||
name: model.modelName,
|
||||
@ -465,13 +476,7 @@ const buildMutationTypeDef = ({ model, action }, { _schema }) => {
|
||||
},
|
||||
resolvers: {
|
||||
Mutation: {
|
||||
[mutationName]: buildMutation(mutationName, {
|
||||
resolver: `${uid}.${action}`,
|
||||
transformOutput: result => ({
|
||||
[toSingular(model.modelName)]: result,
|
||||
}),
|
||||
..._.get(_schema, ['resolver', 'Mutation', mutationName], {}),
|
||||
}),
|
||||
[mutationName]: buildMutation(mutationName, resolverOpts),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -87,6 +87,85 @@ const amountLimiting = (params = {}) => {
|
||||
|
||||
const nonRequired = type => type.replace('!', '');
|
||||
|
||||
const actionExists = ({ resolver, resolverOf }) => {
|
||||
if (isResolvablePath(resolverOf)) {
|
||||
return true;
|
||||
} else if (_.isFunction(resolver)) {
|
||||
return true;
|
||||
} else if (_.isString(resolver)) {
|
||||
return _.isFunction(getActionFn(getActionDetails(resolver)));
|
||||
} else {
|
||||
throw new Error(
|
||||
`Error building query. Expected \`resolver\` as string or a function, or \`resolverOf\` as a string. got ${{
|
||||
resolver,
|
||||
resolverOf,
|
||||
}}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const getAction = resolver => {
|
||||
if (!_.isString(resolver)) {
|
||||
throw new Error(`Error building query. Expected a string, got ${resolver}`);
|
||||
}
|
||||
|
||||
const actionDetails = getActionDetails(resolver);
|
||||
const actionFn = getActionFn(actionDetails);
|
||||
|
||||
if (!actionFn) {
|
||||
throw new Error(
|
||||
`[GraphQL] Cannot find action "${resolver}". Check your graphql configurations.`
|
||||
);
|
||||
}
|
||||
|
||||
return actionFn;
|
||||
};
|
||||
|
||||
const getActionFn = details => {
|
||||
const { controller, action, plugin, api } = details;
|
||||
|
||||
if (plugin) {
|
||||
return _.get(strapi.plugins, [_.toLower(plugin), 'controllers', _.toLower(controller), action]);
|
||||
}
|
||||
|
||||
return _.get(strapi.api, [_.toLower(api), 'controllers', _.toLower(controller), action]);
|
||||
};
|
||||
|
||||
const getActionDetails = resolver => {
|
||||
if (resolver.startsWith('plugins::')) {
|
||||
const [, path] = resolver.split('::');
|
||||
const [plugin, controller, action] = path.split('.');
|
||||
|
||||
return { plugin, controller, action };
|
||||
}
|
||||
|
||||
if (resolver.startsWith('application::')) {
|
||||
const [, path] = resolver.split('::');
|
||||
const [api, controller, action] = path.split('.');
|
||||
|
||||
return { api, controller, action };
|
||||
}
|
||||
|
||||
const args = resolver.split('.');
|
||||
|
||||
if (args.length === 3) {
|
||||
const [api, controller, action] = args;
|
||||
return { api, controller, action };
|
||||
}
|
||||
|
||||
// if direct api access
|
||||
if (args.length === 2) {
|
||||
const [controller, action] = args;
|
||||
return { api: controller, controller, action };
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`[GraphQL] Could not find action for resolver "${resolver}". Check your graphql configurations.`
|
||||
);
|
||||
};
|
||||
|
||||
const isResolvablePath = path => _.isString(path) && !_.isEmpty(path);
|
||||
|
||||
module.exports = {
|
||||
diffResolvers,
|
||||
mergeSchemas,
|
||||
@ -95,4 +174,9 @@ module.exports = {
|
||||
convertToQuery,
|
||||
amountLimiting,
|
||||
nonRequired,
|
||||
actionExists,
|
||||
getAction,
|
||||
getActionDetails,
|
||||
getActionFn,
|
||||
isResolvablePath,
|
||||
};
|
||||
|
||||
@ -104,9 +104,11 @@ module.exports = {
|
||||
description: 'Update an existing role',
|
||||
resolverOf: 'plugins::users-permissions.userspermissions.updateRole',
|
||||
resolver: async (obj, options, { context }) => {
|
||||
context.params = { ...context.params, ...options.input };
|
||||
context.params.role = context.params.id;
|
||||
|
||||
await strapi.plugins['users-permissions'].controllers.userspermissions.updateRole(
|
||||
context.params,
|
||||
context.body
|
||||
context
|
||||
);
|
||||
|
||||
return { ok: true };
|
||||
@ -116,6 +118,9 @@ module.exports = {
|
||||
description: 'Delete an existing role',
|
||||
resolverOf: 'plugins::users-permissions.userspermissions.deleteRole',
|
||||
resolver: async (obj, options, { context }) => {
|
||||
context.params = { ...context.params, ...options.input };
|
||||
context.params.role = context.params.id;
|
||||
|
||||
await strapi.plugins['users-permissions'].controllers.userspermissions.deleteRole(
|
||||
context
|
||||
);
|
||||
|
||||
62
yarn.lock
62
yarn.lock
@ -844,15 +844,15 @@
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@buffetjs/core@3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/core/-/core-3.0.5.tgz#52b7f38603814c761bf3382159b769590d8306e6"
|
||||
integrity sha512-xY5xIi8b6QRFdYjGoPjWr6LmTV35wlsG6o3LeopkDViBVKtiVkmifv7mq64TcrHfuR8haolG8UsdE46Gy8HPoQ==
|
||||
"@buffetjs/core@3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/core/-/core-3.0.6.tgz#1a95540d76ba24bfa7881bbdfb673ca2925e717d"
|
||||
integrity sha512-tT+SuKnnli9L7G2Bkyj79X6AERfT+tAjhCUMQcu2pTxKQTVDFYL/cqbtxWh43sikqfjnPxi+MjKBMrVo0Yp38g==
|
||||
dependencies:
|
||||
"@buffetjs/hooks" "3.0.5"
|
||||
"@buffetjs/icons" "3.0.5"
|
||||
"@buffetjs/styles" "3.0.5"
|
||||
"@buffetjs/utils" "3.0.5"
|
||||
"@buffetjs/hooks" "3.0.6"
|
||||
"@buffetjs/icons" "3.0.6"
|
||||
"@buffetjs/styles" "3.0.6"
|
||||
"@buffetjs/utils" "3.0.6"
|
||||
"@fortawesome/fontawesome-svg-core" "^1.2.25"
|
||||
"@fortawesome/free-regular-svg-icons" "^5.11.2"
|
||||
"@fortawesome/free-solid-svg-icons" "^5.11.2"
|
||||
@ -863,31 +863,31 @@
|
||||
react-dates "^21.5.1"
|
||||
react-moment-proptypes "^1.7.0"
|
||||
|
||||
"@buffetjs/custom@3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/custom/-/custom-3.0.5.tgz#ead4e52220b6254737717f9cb549e73ae46d17bc"
|
||||
integrity sha512-VvzZtmngFVBxWj+3cd4nZ6ccWzis8SEb8ClzYV5n+EhMRLBiYDRJ8sjIIYSbDtu4HQv87p7Hh1MkFKy1FMbpjg==
|
||||
"@buffetjs/custom@3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/custom/-/custom-3.0.6.tgz#8d29fc69088ef11b82308142e8ca606de24f13c3"
|
||||
integrity sha512-skzUI9jhC+9oy9i2LOqOUpigAabA81nhONz8AjV2TRzwMo0Mpav8jWGWVmILasziNCJwHIX7BxisgNWC8/C36Q==
|
||||
dependencies:
|
||||
"@buffetjs/core" "3.0.5"
|
||||
"@buffetjs/styles" "3.0.5"
|
||||
"@buffetjs/utils" "3.0.5"
|
||||
"@buffetjs/core" "3.0.6"
|
||||
"@buffetjs/styles" "3.0.6"
|
||||
"@buffetjs/utils" "3.0.6"
|
||||
moment "^2.24.0"
|
||||
react-moment-proptypes "^1.7.0"
|
||||
|
||||
"@buffetjs/hooks@3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/hooks/-/hooks-3.0.5.tgz#b48301102ec69c0ebc10d83eb8ace70adb0a2a58"
|
||||
integrity sha512-6NSETsJ3EfPzLXRGHAcsRAImCTrzm6oAc3V8ijqSTajf0e70UPhntfCbcN+l0vpDVlqut3YArLQmfVznva2xsw==
|
||||
"@buffetjs/hooks@3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/hooks/-/hooks-3.0.6.tgz#98b3780fa005193a06f32af2b23e0782358ce5a6"
|
||||
integrity sha512-boxLYVT/4RauTQmgLkdZWrwdMPS624/91rk4TqWARRmnXeP4+99MErHwqz5z3Bk8aBa3yNWpayu1p25fdoti6A==
|
||||
|
||||
"@buffetjs/icons@3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/icons/-/icons-3.0.5.tgz#6784c60026e4ed1b7633c5d59dd0193e7c9467c8"
|
||||
integrity sha512-GZgYLMJ7nkDhAiczK3vRj9OEkjw2pLMaHNVfQxeBgZk2JdWrMXFSPxeJrw4BLGExO7m1L6F9aEuLsw2R/OBR5w==
|
||||
"@buffetjs/icons@3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/icons/-/icons-3.0.6.tgz#162a103b0ee02a10de5d1eceb21ce72dd383e228"
|
||||
integrity sha512-lvFJfyhbTnXJmnf6QBm7ZimkLpSZ7Uo5vJYmUJY2FpJbVlVgmWrsyEgwe78MglDjz8ONrdZDLw8lPvUfXsTRzg==
|
||||
|
||||
"@buffetjs/styles@3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/styles/-/styles-3.0.5.tgz#102b3aa25bad017657dd3e87bf59c7cc979892f0"
|
||||
integrity sha512-c4OFX/SBnevMZNofjiboxU/Ixx3eHS2Fb4dqVrKvHYAdTQhHVzcoECCqz2RZcRfUNpaLuJKhE9EeRGj2lmgHVA==
|
||||
"@buffetjs/styles@3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/styles/-/styles-3.0.6.tgz#bfa8d128fdd6e4bfb3a1317c609833c82aa2be49"
|
||||
integrity sha512-4/FMUNUCmGPTJmbe0t9TaO26SiOGbvDuxZdefAtrHWwkvdFvjaofNquONBeSEag5gxlSZN/UKlTbiAq29v0b3Q==
|
||||
dependencies:
|
||||
"@fortawesome/fontawesome-free" "^5.12.0"
|
||||
"@fortawesome/fontawesome-svg-core" "^1.2.22"
|
||||
@ -896,10 +896,10 @@
|
||||
"@fortawesome/react-fontawesome" "^0.1.4"
|
||||
react-dates "^21.1.0"
|
||||
|
||||
"@buffetjs/utils@3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/utils/-/utils-3.0.5.tgz#24bd3f3c6f30a9f7d0851a7741dae5c70f8272ea"
|
||||
integrity sha512-HFhqBBi8N8MTOkTa1/AuET26gET2tI8El1d10aCscLFx5mT7O94LUbspL6RwqTd62SkEli528aV/gBA5fHdTjA==
|
||||
"@buffetjs/utils@3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@buffetjs/utils/-/utils-3.0.6.tgz#87f7e0822d907397ae62b4764edcddc516e3ad27"
|
||||
integrity sha512-DfYrHtmta2KXTc8iy892uGzbs1ygZ37hiXLAYmSWZfJNNk8pamAqnRLcfjVDA/SgTQIVO8unmaPxi3141xfe/Q==
|
||||
dependencies:
|
||||
yup "^0.27.0"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user