Skip to content

Instantly share code, notes, and snippets.

@christiaanwesterbeek
Forked from alexkubica/dataProvider.ts
Last active August 26, 2019 03:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christiaanwesterbeek/05161dbc12baba91da7aa31e7e4eb465 to your computer and use it in GitHub Desktop.
Save christiaanwesterbeek/05161dbc12baba91da7aa31e7e4eb465 to your computer and use it in GitHub Desktop.
dataProvider adaptation for postgraphile flavor
// from https://gist.github.com/ak-il/e91bfbd2c7c83086c7ded41512335795
import gql from 'graphql-tag';
import {
GET_LIST,
GET_ONE,
GET_MANY,
CREATE,
UPDATE,
DELETE,
} from 'react-admin';
import buildGraphQLProvider from 'ra-data-graphql';
import { plural, singular } from 'pluralize';
import * as _ from 'lodash';
function isScalar(field) {
return (
(_.has(field, 'type.kind') && field.type.kind === 'SCALAR') ||
(_.has(field, 'type.ofType.kind') &&
field.type.ofType.kind === 'SCALAR')
);
}
function isInt(field) {
return (
(_.has(field, 'type.kind') && field.type.kind === 'Int') ||
(_.has(field, 'type.ofType.kind') && field.type.ofType.kind === 'Int')
);
}
function isString(field) {
return (
(_.has(field, 'type.kind') && field.type.kind === 'String') ||
(_.has(field, 'type.ofType.kind') &&
field.type.ofType.kind === 'String')
);
}
function buildFieldList(resource) {
// Get scalar fields only
// TODO use graphql-ast-types helpers
return resource.type.fields
.filter(isScalar)
.map(field => {
return field.name;
})
.join('\r\n');
}
function buildObjectInput(resource, object) {
return Object.keys(object)
.map(key => {
const field = resource.type.fields.find(
field => field.name === key
);
if (!field) {
return undefined;
}
if (isString(field)) {
return `${key}: "${object[key]}"`;
}
if (isInt(field)) {
return `${key}: ${object[key]}`;
}
return undefined;
})
.join('\r\n');
}
function buildGetManyQuery(ids, resource, getOneQueryName) {
const fieldList = buildFieldList(resource);
const query = `query {
${ids
.map(id => {
return `id${id}: ${getOneQueryName} (
${`id: ${id}`}
) {
${fieldList}
}`;
})
.join('\r\n')}
}`;
return gql(query);
}
function builderFactory(introspectionResults) {
return (raFetchType, resourceName, params) => {
// TODO use ra-data-graphql-simple's buildQuery function as a default
const resource = introspectionResults.resources.find(
r =>
r.type.name ===
singular(_.upperFirst(_.camelCase(resourceName)))
);
switch (raFetchType) {
case GET_LIST:
return {
query: gql`query {
pageResults: ${resource[raFetchType].name}(
first: ${params.pagination.perPage}
offset: ${Math.floor(
(params.pagination.page - 1) *
params.pagination.perPage
)}
orderBy: ${_.snakeCase(
params.sort.field
).toUpperCase()}_${params.sort.order}
) {
nodes {
${buildFieldList(resource)}
}
}
total: ${resource[raFetchType].name} {
totalCount
}
}`,
variables: params,
parseResponse: ((raFetchType, resourceName, params) => {
return response => {
return {
data: response.data.pageResults.nodes,
total: response.data.total.totalCount,
};
};
})(raFetchType, resourceName, params),
};
case GET_MANY:
return {
query: buildGetManyQuery(
params.ids,
resource,
resource[GET_ONE].name
),
variables: params,
parseResponse: ((raFetchType, resourceName, params) => {
return response => {
return {
data: Object.keys(response.data).map(key => {
return response.data[key];
}),
};
};
})(raFetchType, resourceName, params),
};
case GET_ONE:
return {
query: gql`query {
${resource[raFetchType].name} (
${`id: ${params.id}`}
) {
${buildFieldList(resource)}
}
}`,
variables: params,
parseResponse: ((raFetchType, resourceName, params) => {
const queryName = resource[raFetchType].name;
return response => {
return {
data: response.data[queryName],
};
};
})(raFetchType, resourceName, params),
};
case CREATE:
return {
query: gql`mutation {
${resource[raFetchType].name}(input: {
${singular(_.camelCase(resourceName))}: {
${buildObjectInput(resource, params.data)}
}
}) {
${singular(_.camelCase(resourceName))} {
${buildFieldList(resource)}
}
}
}`,
variables: params,
parseResponse: ((raFetchType, resourceName, params) => {
const queryName = resource[raFetchType].name;
return response => {
return {
data:
response.data[queryName][
singular(_.camelCase(resourceName))
],
};
};
})(raFetchType, resourceName, params),
};
case UPDATE:
return {
query: gql`mutation {
${resource[raFetchType].name}(input: {
id: ${params.id}
${singular(_.camelCase(resourceName))}Patch: {
${buildObjectInput(resource, params.data)}
}
}) {
${singular(_.camelCase(resourceName))} {
${buildFieldList(resource)}
}
}
}`,
variables: params,
parseResponse: ((raFetchType, resourceName, params) => {
const queryName = resource[raFetchType].name;
return response => {
return {
data:
response.data[queryName][
singular(_.camelCase(resourceName))
],
};
};
})(raFetchType, resourceName, params),
};
case DELETE:
return {
query: gql`mutation {
${resource[raFetchType].name}(input: {
id: ${params.id}
}) {
${singular(_.camelCase(resourceName))} {
${buildFieldList(resource)}
}
}
}`,
variables: params,
parseResponse: ((raFetchType, resourceName, params) => {
const queryName = resource[raFetchType].name;
return response => {
return {
data:
response.data[queryName][
singular(_.camelCase(resourceName))
],
};
};
})(raFetchType, resourceName, params),
};
default:
throw new Error(
`${raFetchType} was not implemented yet in dataProvider.builderFactory`
);
}
};
}
export const buildQuery = introspectionResults =>
builderFactory(introspectionResults);
export default (client): Promise<any> => {
const dataProvider = buildGraphQLProvider({
client,
buildQuery,
introspection: {
operationNames: {
[GET_ONE]: type => {
return `${singular(_.camelCase(type.name))}ById`;
},
[GET_LIST]: type => {
return `all${plural(_.upperFirst(_.camelCase(type.name)))}`;
},
[UPDATE]: type => {
return `update${singular(
_.upperFirst(_.camelCase(type.name))
)}`;
},
[DELETE]: type => {
return `delete${singular(
_.upperFirst(_.camelCase(type.name))
)}`;
},
},
},
});
return dataProvider;
};
@christiaanwesterbeek
Copy link
Author

christiaanwesterbeek commented May 16, 2019

CHANGELOG.md

Fixed

  • syntax errors
  • fieldList undefined

Changed

  • export buildQuery
  • prettier

@gholzmacher
Copy link

gholzmacher commented Jul 22, 2019

Sorry for the stupid question - but where do you put this in the default ra-admin and what to include in the App.js? I thought I should make a node_module directory for it then just import dataProvider from ra-data-postgraphile but am getting the following errors. Any help would be greatly appreciated, just looking to get started!

Module parse failed: Unexpected token (256:28)
You may need an appropriate loader to handle this file type.
| builderFactory(introspectionResults);
|

export default (client): Promise => {
| const dataProvider = buildGraphQLProvider({
| client,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment