Skip to content

Commit

Permalink
DELETE_MANY and UPDATE_MANY native support
Browse files Browse the repository at this point in the history
  • Loading branch information
maxschridde1494 committed Oct 26, 2023
1 parent 735edff commit b4628e0
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 55 deletions.
11 changes: 10 additions & 1 deletion packages/ra-data-graphql-simple/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ type Mutation {
views: Int!
user_id: ID!
): Post
updatePosts(
ids: [ID!]
data: PostBulkUpdatePayload
) : { ids: [ID!]}
deletePost(id: ID!): Post
deletePosts(ids: [ID!]) : { ids: [ID!]}
}

type Post {
Expand All @@ -106,6 +111,10 @@ input PostFilter {
user_id: ID
}

input PostBulkUpdatePayload {
title: String
}

type ListMetadata {
count: Int!
}
Expand Down Expand Up @@ -211,7 +220,7 @@ buildApolloProvider({ introspection: introspectionOptions });

## `DELETE_MANY` and `UPDATE_MANY` Optimizations

Your GraphQL backend may not allow multiple deletions or updates in a single query. This provider simply makes multiple requests to handle those. This is obviously not ideal but can be alleviated by supplying your own `ApolloClient` which could use the [apollo-link-batch-http](https://www.apollographql.com/docs/link/links/batch-http.html) link if your GraphQL backend support query batching.
Your GraphQL backend may not allow multiple deletions or updates in a single query. This provider defaults to simply making multiple requests to handle those. This is obviously not ideal but can be alleviated by supplying your own `ApolloClient` which could use the [apollo-link-batch-http](https://www.apollographql.com/docs/link/links/batch-http.html) link if your GraphQL backend support query batching.

## Contributing

Expand Down
88 changes: 88 additions & 0 deletions packages/ra-data-graphql-simple/src/buildGqlQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
UPDATE,
CREATE,
DELETE,
DELETE_MANY,
UPDATE_MANY,
} from 'ra-core';
import buildGqlQuery, {
buildApolloArgs,
Expand Down Expand Up @@ -330,6 +332,49 @@ describe('buildGqlQuery', () => {
{ name: 'bar' },
],
};

const queryTypeDeleteMany = {
name: 'deleteCommands',
args: [
{
name: 'ids',
type: {
kind: TypeKind.LIST,
ofType: {
kind: TypeKind.NON_NULL,
ofType: {
kind: TypeKind.SCALAR,
name: 'ID',
},
},
},
},
],
};

const queryTypeUpdateMany = {
name: 'updateCommands',
args: [
{
name: 'ids',
type: {
kind: TypeKind.LIST,
ofType: {
kind: TypeKind.NON_NULL,
ofType: {
kind: TypeKind.SCALAR,
name: 'ID',
},
},
},
},
{
name: 'data',
type: { kind: TypeKind.OBJECT, name: 'CommandType' },
},
],
};

const params = { foo: 'foo_value' };

it('returns the correct query for GET_LIST', () => {
Expand Down Expand Up @@ -513,6 +558,49 @@ describe('buildGqlQuery', () => {
}
}
}
`
);
});

it('returns the correct query for DELETE_MANY', () => {
expect(
print(
buildGqlQuery(introspectionResults)(
resource,
DELETE_MANY,
queryTypeDeleteMany,
{ ids: [1, 2, 3] }
)
)
).toEqual(
`mutation deleteCommands($ids: [ID!]) {
data: deleteCommands(ids: $ids) {
ids
}
}
`
);
});

it('returns the correct query for UPDATE_MANY', () => {
expect(
print(
buildGqlQuery(introspectionResults)(
resource,
UPDATE_MANY,
queryTypeUpdateMany,
{
ids: [1, 2, 3],
data: params,
}
)
)
).toEqual(
`mutation updateCommands($ids: [ID!], $data: CommandType) {
data: updateCommands(ids: $ids, data: $data) {
ids
}
}
`
);
});
Expand Down
30 changes: 29 additions & 1 deletion packages/ra-data-graphql-simple/src/buildGqlQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { GET_LIST, GET_MANY, GET_MANY_REFERENCE, DELETE } from 'ra-core';
import {
GET_LIST,
GET_MANY,
GET_MANY_REFERENCE,
DELETE,
DELETE_MANY,
UPDATE_MANY,
} from 'ra-core';
import {
QUERY_TYPES,
IntrospectionResult,
Expand Down Expand Up @@ -84,6 +91,27 @@ export default (introspectionResults: IntrospectionResult) => (
]);
}

if (raFetchMethod === DELETE_MANY || raFetchMethod === UPDATE_MANY) {
return gqlTypes.document([
gqlTypes.operationDefinition(
'mutation',
gqlTypes.selectionSet([
gqlTypes.field(
gqlTypes.name(queryType.name),
gqlTypes.name('data'),
args,
null,
gqlTypes.selectionSet([
gqlTypes.field(gqlTypes.name('ids')),
])
),
]),
gqlTypes.name(queryType.name),
apolloArgs
),
]);
}

return gqlTypes.document([
gqlTypes.operationDefinition(
QUERY_TYPES.includes(raFetchMethod) ? 'query' : 'mutation',
Expand Down
44 changes: 44 additions & 0 deletions packages/ra-data-graphql-simple/src/buildVariables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
CREATE,
UPDATE,
DELETE,
DELETE_MANY,
UPDATE_MANY,
} from 'ra-core';
import buildVariables from './buildVariables';

Expand Down Expand Up @@ -172,4 +174,46 @@ describe('buildVariables', () => {
});
});
});

describe('DELETE_MANY', () => {
it('returns correct variables', () => {
const params = {
ids: ['post1'],
};
expect(
buildVariables(introspectionResult)(
{ type: { name: 'Post', inputFields: [] } },
DELETE_MANY,
params,
{}
)
).toEqual({
ids: ['post1'],
});
});
});

describe('UPDATE_MANY', () => {
it('returns correct variables', () => {
const params = {
ids: ['post1', 'post2'],
data: {
title: 'New Title',
},
};
expect(
buildVariables(introspectionResult)(
{ type: { name: 'Post', inputFields: [] } },
UPDATE_MANY,
params,
{}
)
).toEqual({
ids: ['post1', 'post2'],
data: {
title: 'New Title',
},
});
});
});
});
17 changes: 17 additions & 0 deletions packages/ra-data-graphql-simple/src/buildVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
CREATE,
UPDATE,
DELETE,
DELETE_MANY,
UPDATE_MANY,
} from 'ra-core';
import { IntrospectionResult, IntrospectedResource } from 'ra-data-graphql';

Expand Down Expand Up @@ -63,6 +65,8 @@ export default (introspectionResults: IntrospectionResult) => (
return {
id: preparedParams.id,
};
case DELETE_MANY:
return preparedParams;
case CREATE:
case UPDATE: {
return buildCreateUpdateVariables(
Expand All @@ -72,6 +76,19 @@ export default (introspectionResults: IntrospectionResult) => (
queryType
);
}
case UPDATE_MANY: {
const { ids, data: resourceData } = preparedParams;
const { id, ...data } = buildCreateUpdateVariables(
resource,
raFetchMethod,
{ data: resourceData },
queryType
);
return {
ids,
data,
};
}
}
};

Expand Down
76 changes: 76 additions & 0 deletions packages/ra-data-graphql-simple/src/getResponseParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
CREATE,
UPDATE,
DELETE,
DELETE_MANY,
UPDATE_MANY,
} from 'ra-core';
import getResponseParser from './getResponseParser';

Expand Down Expand Up @@ -394,4 +396,78 @@ describe('getResponseParser', () => {
});
});
});

it('returns the response expected for DELETE_MANY', () => {
const introspectionResults = {
resources: [
{
type: {
name: 'User',
fields: [
{ name: 'id', type: { kind: TypeKind.SCALAR } },
{
name: 'firstName',
type: { kind: TypeKind.SCALAR },
},
],
},
},
],
types: [{ name: 'User' }],
};
const response = {
data: {
data: {
ids: [1, 2, 3, 4],
},
},
};

expect(
getResponseParser(introspectionResults)(
DELETE_MANY,
undefined,
undefined
)(response)
).toEqual({
data: [1, 2, 3, 4],
});
});

it('returns the response expected for UPDATE_MANY', () => {
const introspectionResults = {
resources: [
{
type: {
name: 'User',
fields: [
{ name: 'id', type: { kind: TypeKind.SCALAR } },
{
name: 'firstName',
type: { kind: TypeKind.SCALAR },
},
],
},
},
],
types: [{ name: 'User' }],
};
const response = {
data: {
data: {
ids: [1, 2, 3, 4],
},
},
};

expect(
getResponseParser(introspectionResults)(
UPDATE_MANY,
undefined,
undefined
)(response)
).toEqual({
data: [1, 2, 3, 4],
});
});
});
10 changes: 9 additions & 1 deletion packages/ra-data-graphql-simple/src/getResponseParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { GET_LIST, GET_MANY, GET_MANY_REFERENCE } from 'ra-core';
import {
DELETE_MANY,
GET_LIST,
GET_MANY,
GET_MANY_REFERENCE,
UPDATE_MANY,
} from 'ra-core';
import { IntrospectionResult, IntrospectedResource } from 'ra-data-graphql';
import { IntrospectionField } from 'graphql';
import { ApolloQueryResult } from '@apollo/client';
Expand All @@ -19,6 +25,8 @@ export default (_introspectionResults: IntrospectionResult) => (
data: response.data.items.map(sanitizeResource),
total: response.data.total.count,
};
} else if (raFetchMethod === DELETE_MANY || raFetchMethod === UPDATE_MANY) {
return { data: sanitizeResource(data.data).ids };
}

return { data: sanitizeResource(data.data) };
Expand Down
Loading

0 comments on commit b4628e0

Please sign in to comment.