UNPKG

ra-data-graphql-simple

Version:

A GraphQL simple data provider for react-admin

366 lines (319 loc) 10.9 kB
import { IntrospectionField, IntrospectionInputObjectType, IntrospectionNamedTypeRef, IntrospectionNonNullTypeRef, IntrospectionType, } from 'graphql'; import { GET_LIST, GET_ONE, GET_MANY, GET_MANY_REFERENCE, CREATE, UPDATE, DELETE, DELETE_MANY, UPDATE_MANY, } from 'ra-core'; import { IntrospectionResult, IntrospectedResource } from 'ra-data-graphql'; import getFinalType from './getFinalType'; import isList from './isList'; export default (introspectionResults: IntrospectionResult) => ( resource: IntrospectedResource, raFetchMethod: string, params: any, queryType: IntrospectionField ) => { const preparedParams = prepareParams( params, queryType, introspectionResults ); switch (raFetchMethod) { case GET_LIST: { return buildGetListVariables(introspectionResults)( resource, raFetchMethod, preparedParams ); } case GET_MANY: return { filter: { ids: preparedParams.ids }, ...(preparedParams.meta ? { meta: preparedParams.meta } : {}), }; case GET_MANY_REFERENCE: { const variables = buildGetListVariables(introspectionResults)( resource, raFetchMethod, preparedParams ); variables.filter = { ...variables.filter, [preparedParams.target]: preparedParams.id, }; return variables; } case GET_ONE: case DELETE: return { id: preparedParams.id, ...(preparedParams.meta ? { meta: preparedParams.meta } : {}), }; case DELETE_MANY: return preparedParams; case CREATE: case UPDATE: { return buildCreateUpdateVariables( resource, raFetchMethod, preparedParams, queryType ); } case UPDATE_MANY: { const { ids, data: resourceData } = preparedParams; const { id, ...data } = buildCreateUpdateVariables( resource, raFetchMethod, { data: resourceData }, queryType ); return { ids, data, }; } } }; const sanitizeValue = (type: IntrospectionType, value: any) => { if (type.name === 'Int') { return parseInt(value, 10); } if (type.name === 'Float') { return parseFloat(value); } return value; }; const castType = ( value: any, type: IntrospectionType | IntrospectionNonNullTypeRef ) => { const realType = type.kind === 'NON_NULL' ? type.ofType : type; switch ( `${realType.kind}:${(realType as IntrospectionNamedTypeRef).name}` ) { case 'SCALAR:Int': return Number(value); case 'SCALAR:String': return String(value); case 'SCALAR:Boolean': return Boolean(value); default: return value; } }; const prepareParams = ( params: any, queryType: Partial<IntrospectionField>, introspectionResults: IntrospectionResult ) => { const result = {}; if (!params) { return params; } Object.keys(params).forEach(key => { const param = params[key]; let arg = null; if (!param) { result[key] = param; return; } if (queryType && Array.isArray(queryType.args)) { arg = queryType.args.find(item => item.name === key); } if (param instanceof File) { result[key] = param; return; } if (param instanceof Date) { result[key] = param.toISOString(); return; } if ( param instanceof Object && !Array.isArray(param) && arg && arg.type.kind === 'INPUT_OBJECT' ) { const args = ( introspectionResults.types.find( item => item.kind === arg.type.kind && item.name === arg.type.name ) as IntrospectionInputObjectType ).inputFields; result[key] = prepareParams(param, { args }, introspectionResults); return; } if ( param instanceof Object && !(param instanceof Date) && !Array.isArray(param) ) { result[key] = prepareParams(param, queryType, introspectionResults); return; } if (!arg) { result[key] = param; return; } result[key] = castType(param, arg.type); }); return result; }; const buildGetListVariables = (introspectionResults: IntrospectionResult) => (resource: IntrospectedResource, raFetchMethod: string, params: any) => { let variables: Partial<{ filter: { [key: string]: any }; page: number; perPage: number; sortField: string; sortOrder: string; meta?: object; }> = { filter: {} }; if (params.filter) { variables.filter = Object.keys(params.filter).reduce((acc, key) => { if (key === 'ids') { return { ...acc, ids: params.filter[key] }; } if (typeof params.filter[key] === 'object') { const type = introspectionResults.types.find( t => t.name === `${resource.type.name}Filter` ); const filterSome = ( type as IntrospectionInputObjectType )?.inputFields?.find(t => t.name === `${key}_some`); if (filterSome) { const filter = Object.keys(params.filter[key]).reduce( (acc, k) => ({ ...acc, [`${k}_in`]: params.filter[key][k], }), {} ); return { ...acc, [`${key}_some`]: filter }; } } const parts = key.split('.'); if (parts.length > 1) { if (parts[1] === 'id') { const type = introspectionResults.types.find( t => t.name === `${resource.type.name}Filter` ); const filterSome = ( type as IntrospectionInputObjectType )?.inputFields?.find( t => t.name === `${parts[0]}_some` ); if (filterSome) { return { ...acc, [`${parts[0]}_some`]: { id: params.filter[key], }, }; } return { ...acc, [parts[0]]: { id: params.filter[key] }, }; } const resourceField = resource.type.fields.find( f => f.name === parts[0] ); const type = getFinalType( resourceField.type ) as IntrospectionType; return { ...acc, [key]: sanitizeValue(type, params.filter[key]), }; } const resourceField = resource.type.fields.find( f => f.name === key ); if (resourceField) { const type = getFinalType( resourceField.type ) as IntrospectionType; const isAList = isList(resourceField.type); if (isAList) { return { ...acc, [key]: Array.isArray(params.filter[key]) ? params.filter[key].map(value => sanitizeValue(type, value) ) : sanitizeValue(type, [params.filter[key]]), }; } return { ...acc, [key]: sanitizeValue(type, params.filter[key]), }; } return { ...acc, [key]: params.filter[key] }; }, {}); } if (params.pagination) { variables.page = parseInt(params.pagination.page, 10) - 1; variables.perPage = parseInt(params.pagination.perPage, 10); } if (params.sort) { variables.sortField = params.sort.field; variables.sortOrder = params.sort.order; } if (params.meta) variables = { ...variables, meta: params.meta }; return variables; }; const buildCreateUpdateVariables = ( resource: IntrospectedResource, raFetchMethod, { id, data, meta }: any, queryType: IntrospectionField ) => Object.keys(data).reduce( (acc, key) => { if (Array.isArray(data[key])) { const arg = queryType.args.find(a => a.name === `${key}Ids`); if (arg) { return { ...acc, [`${key}Ids`]: data[key].map(({ id }) => id), }; } } if (typeof data[key] === 'object') { const arg = queryType.args.find(a => a.name === `${key}Id`); if (arg) { return { ...acc, [`${key}Id`]: data[key].id, }; } } return { ...acc, [key]: data[key], }; }, { id, meta } );