import { gql, type OperationResult } from "@urql/core"; import type { BaseRecord, CreateManyParams, DeleteManyParams, GetListParams, GetManyParams, GetOneParams, UpdateManyParams, CustomParams, CreateParams, UpdateParams, DeleteOneParams, } from "@refinedev/core"; import camelcase from "camelcase"; import { singular } from "pluralize"; import { buildFilters, buildPagination, buildSorters, getOperationFields, } from "../utils"; import type { DocumentNode } from "graphql"; export type GraphQLDataProviderOptions = { create?: { dataMapper?: ( response: OperationResult, params: CreateParams, ) => Record; buildVariables?: (params: CreateParams) => Record; }; createMany?: { dataMapper?: ( response: OperationResult, params: CreateManyParams, ) => BaseRecord[]; buildVariables?: (params: CreateManyParams) => Record; }; getOne?: { dataMapper?: ( response: OperationResult, params: GetOneParams, ) => BaseRecord; buildVariables?: (params: GetOneParams) => Record; convertMutationToQuery?: (params: GetOneParams) => DocumentNode; }; getList?: { dataMapper?: ( response: OperationResult, params: GetListParams, ) => BaseRecord[]; getTotalCount?: ( response: OperationResult, params: GetListParams, ) => number; buildVariables?: (params: GetListParams) => Record; }; getMany?: { buildFilter?: (params: GetManyParams) => Record; dataMapper?: ( response: OperationResult, params: GetManyParams, ) => BaseRecord[]; }; update?: { dataMapper?: ( response: OperationResult, params: UpdateParams, ) => BaseRecord; buildVariables?: (params: UpdateParams) => Record; }; updateMany?: { dataMapper?: ( response: OperationResult, params: UpdateManyParams, ) => BaseRecord[]; buildVariables?: (params: UpdateManyParams) => Record; }; deleteOne?: { dataMapper?: ( response: OperationResult, params: DeleteOneParams, ) => BaseRecord; buildVariables?: (params: DeleteOneParams) => Record; }; deleteMany?: { dataMapper?: ( response: OperationResult, params: DeleteManyParams, ) => BaseRecord[]; buildVariables?: (params: DeleteManyParams) => Record; }; custom?: { dataMapper?: ( response: OperationResult, params: CustomParams, ) => Record; buildVariables?: (params: CustomParams) => Record; }; }; export const defaultOptions = { create: { dataMapper: (response: OperationResult, params: CreateParams) => { const key = `createOne${camelcase(singular(params.resource), { pascalCase: true, })}`; return response.data?.[key]; }, buildVariables: (params: CreateParams) => { return { input: { [singular(params.resource)]: params.variables ?? params?.meta?.gqlVariables, }, }; }, }, createMany: { dataMapper: ( response: OperationResult, params: CreateManyParams, ) => { const key = `createMany${camelcase(params.resource, { pascalCase: true, })}`; return response.data?.[key]; }, buildVariables: (params: CreateManyParams) => { return { input: { [camelcase(params.resource)]: params.variables ?? params?.meta?.gqlVariables, }, }; }, }, getOne: { dataMapper: (response: OperationResult, params: GetOneParams) => { const key = camelcase(singular(params.resource)); return response.data?.[key]; }, buildVariables: (params: GetOneParams) => { return { id: params.id, ...params.meta?.gqlVariables, }; }, // Besides useOne hook, getOne hook is also consumed by `useForm`. // useForm hook has an optional gqlQuery field, we may only get `gqlMutation`. // For this reason, we need to convert mutation to query to get initial data on edit. convertMutationToQuery: (params: GetOneParams) => { const { resource, meta } = params; const gqlOperation = meta?.gqlQuery ?? meta?.gqlMutation; if (!gqlOperation) { throw new Error("Operation is required."); } const stringFields = getOperationFields(gqlOperation); const pascalCaseOperation = camelcase(singular(resource), { pascalCase: true, }); const operation = camelcase(singular(resource)); const query = gql` query Get${pascalCaseOperation}($id: ID!) { ${operation}(id: $id) { ${stringFields} } } `; return query; }, }, getList: { dataMapper: (response: OperationResult, params: GetListParams) => { return response.data?.[params.resource].nodes; }, getTotalCount: (response: OperationResult, params: GetListParams) => { return response.data?.[params.resource].totalCount; }, buildVariables: (params: GetListParams) => { return { sorting: buildSorters(params.sorters), filter: buildFilters(params.filters), paging: buildPagination(params.pagination), ...params.meta?.variables, ...params.meta?.gqlVariables, }; }, }, getMany: { buildFilter: (params: GetManyParams) => { return { id: { in: params.ids }, ...params?.meta?.gqlVariables }; }, dataMapper: (response: OperationResult, params: GetManyParams) => { const key = camelcase(params.resource); return response.data?.[key].nodes; }, }, update: { dataMapper: (response: OperationResult, params: UpdateParams) => { const key = `updateOne${camelcase(singular(params.resource), { pascalCase: true, })}`; return response.data?.[key]; }, buildVariables: (params: UpdateParams) => { return { input: { id: params.id, update: params.variables, ...params.meta?.gqlVariables, }, }; }, }, updateMany: { dataMapper: ( _response: OperationResult, params: UpdateManyParams, ): any[] => { return params.ids.map((id) => ({ id })); }, buildVariables: (params: UpdateManyParams) => { const { ids, variables } = params; return { input: { filter: { id: { in: ids } }, update: variables, ...params.meta?.gqlVariables, }, }; }, }, deleteOne: { dataMapper: ( response: OperationResult, params: DeleteOneParams, ) => { const pascalResource = camelcase(singular(params.resource), { pascalCase: true, }); const key = `deleteOne${pascalResource}`; return response.data?.[key]; }, buildVariables: (params: DeleteOneParams) => { return { input: { id: params.id, ...params?.meta?.gqlVariables }, }; }, }, deleteMany: { dataMapper: ( _response: OperationResult, params: DeleteManyParams, ): any[] => { return params.ids.map((id) => ({ id })); }, buildVariables: (params: DeleteManyParams) => { const { ids } = params; return { input: { filter: { id: { in: ids }, }, ...params.meta?.gqlVariables, }, }; }, }, custom: { dataMapper: (response: OperationResult, params: CustomParams) => response.data ?? response.error?.message, buildVariables: (params: CustomParams) => ({ ...params?.meta?.variables, ...params?.meta?.gqlVariables, }), }, };