| 
									
										
										
										
											2018-09-10 16:05:00 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2020-02-10 19:13:01 +01:00
										 |  |  |  * Build queries and mutation resolvers | 
					
						
							| 
									
										
										
										
											2018-09-10 16:05:00 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 19:13:01 +01:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 16:05:00 +08:00
										 |  |  | const _ = require('lodash'); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  | const compose = require('koa-compose'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 19:13:01 +01:00
										 |  |  | const { policy: policyUtils } = require('strapi-utils'); | 
					
						
							| 
									
										
										
										
											2020-04-22 15:41:40 +02:00
										 |  |  | const { | 
					
						
							|  |  |  |   convertToParams, | 
					
						
							|  |  |  |   convertToQuery, | 
					
						
							|  |  |  |   amountLimiting, | 
					
						
							|  |  |  |   getAction, | 
					
						
							|  |  |  |   getActionDetails, | 
					
						
							|  |  |  |   isResolvablePath, | 
					
						
							|  |  |  | } = require('./utils'); | 
					
						
							| 
									
										
										
										
											2019-03-13 19:27:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  | const buildMutation = (mutationName, config) => { | 
					
						
							|  |  |  |   const { resolver, resolverOf, transformOutput = _.identity } = config; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (_.isFunction(resolver) && !isResolvablePath(resolverOf)) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       `Cannot create mutation "${mutationName}". Missing "resolverOf" option with custom resolver.` | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const policiesMiddleware = compose(getPolicies(config)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // custom resolvers
 | 
					
						
							|  |  |  |   if (_.isFunction(resolver)) { | 
					
						
							| 
									
										
										
										
											2020-06-16 20:57:05 +09:00
										 |  |  |     return async (root, options = {}, graphqlContext, info) => { | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |       const ctx = buildMutationContext({ options, graphqlContext }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await policiesMiddleware(ctx); | 
					
						
							| 
									
										
										
										
											2020-03-20 22:24:14 +09:00
										 |  |  |       graphqlContext.context = ctx; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 20:57:05 +09:00
										 |  |  |       return resolver(root, options, graphqlContext, info); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const action = getAction(resolver); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return async (root, options = {}, graphqlContext) => { | 
					
						
							|  |  |  |     const ctx = buildMutationContext({ options, graphqlContext }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await policiesMiddleware(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 17:35:09 +01:00
										 |  |  |     const values = await action(ctx); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |     const result = ctx.body || values; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_.isError(result)) { | 
					
						
							|  |  |  |       throw result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return transformOutput(result); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const buildMutationContext = ({ options, graphqlContext }) => { | 
					
						
							|  |  |  |   const { context } = graphqlContext; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 14:57:24 +02:00
										 |  |  |   const ctx = cloneKoaContext(context); | 
					
						
							| 
									
										
										
										
											2020-02-11 17:35:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   if (options.input && options.input.where) { | 
					
						
							| 
									
										
										
										
											2020-02-11 17:35:09 +01:00
										 |  |  |     ctx.params = convertToParams(options.input.where || {}); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2020-02-11 17:35:09 +01:00
										 |  |  |     ctx.params = {}; | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (options.input && options.input.data) { | 
					
						
							| 
									
										
										
										
											2020-02-11 17:35:09 +01:00
										 |  |  |     ctx.request.body = options.input.data || {}; | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2020-02-11 17:35:09 +01:00
										 |  |  |     ctx.request.body = options; | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ctx; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const buildQuery = (queryName, config) => { | 
					
						
							| 
									
										
										
										
											2020-02-10 12:50:11 +01:00
										 |  |  |   const { resolver } = config; | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 12:50:11 +01:00
										 |  |  |   try { | 
					
						
							|  |  |  |     validateResolverOption(config); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     throw new Error(`Cannot create query "${queryName}": ${error.message}`); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const policiesMiddleware = compose(getPolicies(config)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // custom resolvers
 | 
					
						
							|  |  |  |   if (_.isFunction(resolver)) { | 
					
						
							| 
									
										
										
										
											2020-06-16 20:57:05 +09:00
										 |  |  |     return async (root, options = {}, graphqlContext, info) => { | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |       const { ctx, opts } = buildQueryContext({ options, graphqlContext }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await policiesMiddleware(ctx); | 
					
						
							| 
									
										
										
										
											2020-03-20 22:24:14 +09:00
										 |  |  |       graphqlContext.context = ctx; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 20:57:05 +09:00
										 |  |  |       return resolver(root, opts, graphqlContext, info); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const action = getAction(resolver); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return async (root, options = {}, graphqlContext) => { | 
					
						
							|  |  |  |     const { ctx } = buildQueryContext({ options, graphqlContext }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // duplicate context
 | 
					
						
							|  |  |  |     await policiesMiddleware(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const values = await action(ctx); | 
					
						
							|  |  |  |     const result = ctx.body || values; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_.isError(result)) { | 
					
						
							|  |  |  |       throw result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 12:50:11 +01:00
										 |  |  | const validateResolverOption = config => { | 
					
						
							|  |  |  |   const { resolver, resolverOf, policies } = config; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (_.isFunction(resolver) && !isResolvablePath(resolverOf)) { | 
					
						
							|  |  |  |     throw new Error(`Missing "resolverOf" option with custom resolver.`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 22:24:14 +09:00
										 |  |  |   if (!_.isUndefined(policies) && (!Array.isArray(policies) || !_.every(policies, _.isString))) { | 
					
						
							| 
									
										
										
										
											2020-02-10 12:50:11 +01:00
										 |  |  |     throw new Error('Policies option must be an array of string.'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 14:57:24 +02:00
										 |  |  | const cloneKoaContext = ctx => { | 
					
						
							|  |  |  |   return Object.assign(ctx.app.createContext(_.clone(ctx.req), _.clone(ctx.res)), { | 
					
						
							|  |  |  |     state: { | 
					
						
							|  |  |  |       ...ctx.state, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  | const buildQueryContext = ({ options, graphqlContext }) => { | 
					
						
							|  |  |  |   const { context } = graphqlContext; | 
					
						
							|  |  |  |   const _options = _.cloneDeep(options); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 14:57:24 +02:00
										 |  |  |   const ctx = cloneKoaContext(context); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Note: we've to used the Object.defineProperties to reset the prototype. It seems that the cloning the context
 | 
					
						
							|  |  |  |   // cause a lost of the Object prototype.
 | 
					
						
							|  |  |  |   const opts = amountLimiting(_options); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ctx.query = { | 
					
						
							|  |  |  |     ...convertToParams(_.omit(opts, 'where')), | 
					
						
							|  |  |  |     ...convertToQuery(opts.where), | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ctx.params = convertToParams(opts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { ctx, opts }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Checks if a resolverPath (resolver or resovlerOf) might be resolved | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-30 10:49:42 +01:00
										 |  |  | const getPolicies = config => { | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   const { resolver, policies = [], resolverOf } = config; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-30 10:49:42 +01:00
										 |  |  |   const { api, plugin } = config['_metadatas'] || {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   const policyFns = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 22:24:14 +09:00
										 |  |  |   const { controller, action, plugin: pathPlugin } = isResolvablePath(resolverOf) | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |     ? getActionDetails(resolverOf) | 
					
						
							|  |  |  |     : getActionDetails(resolver); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const globalPolicy = policyUtils.globalPolicy({ | 
					
						
							|  |  |  |     controller, | 
					
						
							|  |  |  |     action, | 
					
						
							| 
									
										
										
										
											2020-01-30 10:49:42 +01:00
										 |  |  |     plugin: pathPlugin, | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   policyFns.push(globalPolicy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (strapi.plugins['users-permissions']) { | 
					
						
							| 
									
										
										
										
											2020-02-10 12:50:11 +01:00
										 |  |  |     policies.unshift('plugins::users-permissions.permissions'); | 
					
						
							| 
									
										
										
										
											2020-01-29 15:30:53 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   policies.forEach(policy => { | 
					
						
							|  |  |  |     const policyFn = policyUtils.get(policy, plugin, api); | 
					
						
							|  |  |  |     policyFns.push(policyFn); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return policyFns; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-30 10:49:42 +01:00
										 |  |  | module.exports = { | 
					
						
							|  |  |  |   buildQuery, | 
					
						
							| 
									
										
										
										
											2020-01-31 17:40:02 +01:00
										 |  |  |   buildMutation, | 
					
						
							| 
									
										
										
										
											2020-01-30 10:49:42 +01:00
										 |  |  | }; |