Skip to content

Instantly share code, notes, and snippets.

@alexkubica
Last active October 4, 2019 11:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alexkubica/e91bfbd2c7c83086c7ded41512335795 to your computer and use it in GitHub Desktop.
Save alexkubica/e91bfbd2c7c83086c7ded41512335795 to your computer and use it in GitHub Desktop.
dataProvider adaptation for postgraphile flavor, UPDATE: see https://github.com/des-des/ra-postgraphile-client
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 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);
};
break;
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);
};
break;
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);
};
break;
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);
};
break;
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);
};
break;
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);
};
break;
default:
throw new Error(`${raFetchType} was not implemented yet in dataProvider.builderFactory`);
}
};
}
export default (client): Promise<any> => {
const dataProvider = buildGraphQLProvider({
client,
buildQuery: (introspectionResults) => {
return builderFactory(introspectionResults);
},
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

Thanks. I'm going to be using this one as is it seems to be the only published postgraphile dataProvider for react-admin out there.

@christiaanwesterbeek
Copy link

christiaanwesterbeek commented May 16, 2019

I see some syntax errors that I fixed locally. Do you have some time to make this dataProvider right together in a Github repo?

Check out my fork.

I suggest repo name: ra-data-postgraphile

@anwajler
Copy link

@alexkubica
Copy link
Author

alexkubica commented Oct 4, 2019

https://github.com/des-des/ra-postgraphile-client

@anwajler
That's awesome, I added it to the title of the gist :)

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