graphql-genie
Advanced tools
Comparing version 0.2.21 to 0.2.23
@@ -0,1 +1,5 @@ | ||
- [Connections](#connections) | ||
- [Connection output type](#connection-output-type) | ||
- [Examples](#examples) | ||
# Connections | ||
@@ -44,2 +48,4 @@ | ||
### Connection output type | ||
Lets look at a the CityConnection returned by `citiesConnection` query | ||
@@ -46,0 +52,0 @@ |
@@ -0,1 +1,21 @@ | ||
- [GraphQLGenie API](#graphqlgenie-api) | ||
- [**constructor**](#constructor) | ||
- [**use**](#use) | ||
- [**getSchema**](#getschema) | ||
- [**printSchema**](#printschema) | ||
- [**getFragmentTypes**](#getfragmenttypes) | ||
- [**getRawData**](#getrawdata) | ||
- [**importRawData**](#importrawdata) | ||
- [**data**](#data) | ||
- [**merge**](#merge) | ||
- [**defaultTypename**](#defaulttypename) | ||
- [**getDataResolver**](#getdataresolver) | ||
- [**getSchemaBuilder**](#getschemabuilder) | ||
- [GraphQLSchemaBuilder API](#graphqlschemabuilder-api) | ||
- [**printSchemaWithDirectives**](#printschemawithdirectives) | ||
- [**addTypeDefsToSchema**](#addtypedefstoschema) | ||
- [**setResolvers**](#setresolvers) | ||
- [**setIResolvers**](#setiresolvers) | ||
- [**isUserType**](#isusertype) | ||
### GraphQLGenie API | ||
@@ -5,3 +25,3 @@ | ||
**constructor** | ||
#### **constructor** | ||
@@ -35,3 +55,3 @@ ```typescript | ||
**use** | ||
#### **use** | ||
@@ -45,11 +65,9 @@ ```ts | ||
> See info about the GeniePlugin interface in [GraphQLGenieInterfaces.ts](https://github.com/genie-team/graphql-genie/blob/master/src/GraphQLGenieInterfaces.ts) | ||
> | ||
> --- | ||
> | ||
> **getSchema** | ||
> | ||
> ```typescript | ||
> getSchema(): GraphQLSchema | ||
> ``` | ||
#### **getSchema** | ||
```typescript | ||
getSchema(): GraphQLSchema | ||
``` | ||
Get the schema | ||
@@ -59,3 +77,3 @@ | ||
**printSchema** | ||
#### **printSchema** | ||
@@ -70,3 +88,3 @@ ```ts | ||
**getFragmentTypes** | ||
#### **getFragmentTypes** | ||
@@ -89,3 +107,3 @@ ```typescript | ||
**getRawData** | ||
#### **getRawData** | ||
@@ -133,3 +151,3 @@ ``` | ||
**importRawData** | ||
#### **importRawData** | ||
@@ -142,3 +160,3 @@ ``` | ||
**data** | ||
#### **data** | ||
@@ -149,3 +167,3 @@ an array of objects to import. It can be either in the format of raw data (as exported from `getRawData` ) or in the format returned from a graphql query. Note that if it is in the format of the graphql query and __typename fields are not added the defaultTypename must be provided | ||
**merge** | ||
#### **merge** | ||
@@ -156,3 +174,3 @@ If false every object will create a new object, the id won't be preserved from the current data but relationships will still be built as they were in the provided data. | ||
**defaultTypename** | ||
#### **defaultTypename** | ||
@@ -163,3 +181,3 @@ Must be provided if every object in data does not have a `__typename` property | ||
**getDataResolver** | ||
#### **getDataResolver** | ||
@@ -188,15 +206,12 @@ ```typescript | ||
> See info about the DataResolver interface in [GraphQLGenieInterfaces.ts](https://github.com/genie-team/graphql-genie/blob/master/src/GraphQLGenieInterfaces.ts) | ||
> | ||
> --- | ||
> | ||
> **getSchemaBuilder** | ||
> | ||
> ```ts | ||
> getSchemaBuilder(): GraphQLSchemaBuilder | ||
> ``` | ||
> | ||
#### **getSchemaBuilder** | ||
```ts | ||
getSchemaBuilder(): GraphQLSchemaBuilder | ||
``` | ||
> GraphQLSchemaBuilder has some additional helpers to add types and resolvers to a graphql schema | ||
> See the [GraphQLSchemaBuilder API documentation](#graphqlschemabuilder-api) | ||
See the [GraphQLSchemaBuilder API documentation](#graphqlschemabuilder-api) | ||
--- | ||
@@ -208,3 +223,3 @@ | ||
**printSchemaWithDirectives** | ||
#### **printSchemaWithDirectives** | ||
@@ -219,3 +234,3 @@ ```typescript | ||
**addTypeDefsToSchema** | ||
#### **addTypeDefsToSchema** | ||
@@ -226,7 +241,7 @@ ```typescript | ||
Completely rebuilds the schema with the new typeDefs. You need to use this if we want any of the custom directives to work on your new typeDefs. Other wise you can use the schema stitching tools from | ||
Completely rebuilds the schema with the new typeDefs. You need to use this if we want any of the custom directives to work on your new typeDefs. Otherwise you can use the schema stitching tools from | ||
--- | ||
**setResolvers** | ||
#### **setResolvers** | ||
@@ -241,3 +256,3 @@ ```typescript | ||
**setIResolvers** | ||
#### **setIResolvers** | ||
@@ -252,3 +267,3 @@ ```ts | ||
**isUserType** | ||
#### **isUserType** | ||
@@ -255,0 +270,0 @@ ```ts |
@@ -0,1 +1,19 @@ | ||
- [Mutations](#mutations) | ||
- [Create](#create) | ||
- [Example](#example) | ||
- [Update](#update) | ||
- [Examples](#examples) | ||
- [Update the city name and push onto neighborhoods](#update-the-city-name-and-push-onto-neighborhoods) | ||
- [Update the user connected to the city](#update-the-user-connected-to-the-city) | ||
- [Upsert](#upsert) | ||
- [Examples](#examples-1) | ||
- [Upsert a user resulting in a create](#upsert-a-user-resulting-in-a-create) | ||
- [Upsert a user resulting in an update](#upsert-a-user-resulting-in-an-update) | ||
- [Delete](#delete) | ||
- [Examples](#examples-2) | ||
- [Delete a user](#delete-a-user) | ||
- [UpdateMany and DeleteMany](#updatemany-and-deletemany) | ||
- [Examples](#examples-3) | ||
- [UpdateManyUsers](#updatemanyusers) | ||
# Mutations | ||
@@ -32,24 +50,24 @@ | ||
type City { | ||
id: ID! @unique | ||
name: String! | ||
neighborhoods: [String] | ||
user: [User] | ||
founded: Date | ||
population: Int | ||
id: ID! @unique | ||
name: String! | ||
neighborhoods: [String] | ||
user: [User] | ||
founded: Date | ||
population: Int | ||
} | ||
type User { | ||
id: ID! @unique | ||
displayname: String @unique | ||
email: String! @unique | ||
address: City | ||
id: ID! @unique | ||
displayname: String @unique | ||
email: String! @unique | ||
address: City | ||
} | ||
` | ||
const genie = new GraphQLGenie({ | ||
typeDefs, | ||
generatorOptions: { | ||
generateCreate: true, | ||
generateUpdate: true, | ||
generateDelete: true, | ||
generateUpsert: true | ||
} | ||
typeDefs, | ||
generatorOptions: { | ||
generateCreate: true, | ||
generateUpdate: true, | ||
generateDelete: true, | ||
generateUpsert: true | ||
} | ||
}); | ||
@@ -66,28 +84,28 @@ ``` | ||
input CreateCityMutationInput { | ||
data: CityCreateInput! | ||
# clientMutationID is optional, but if provided the same clientMutationId will be returned | ||
clientMutationId: String | ||
data: CityCreateInput! | ||
# clientMutationID is optional, but if provided the same clientMutationId will be returned | ||
clientMutationId: String | ||
} | ||
input CityCreateInput { | ||
name: String! | ||
neighborhoods: [String] | ||
# nested create, create or connect User objects. | ||
# Relation will be built automatically to the newly created City | ||
user: UserCreateManyWithoutAddressInput | ||
founded: Date | ||
population: Int | ||
name: String! | ||
neighborhoods: [String] | ||
# nested create, create or connect User objects. | ||
# Relation will be built automatically to the newly created City | ||
user: UserCreateManyWithoutAddressInput | ||
founded: Date | ||
population: Int | ||
} | ||
input UserCreateManyWithoutAddressInput { | ||
# create one or more new User objects | ||
create: [UserCreateWithoutAddressInput!] | ||
# setup a relation to one or more existing User options, you can connect based on any unique field | ||
connect: [UserWhereUniqueInput!] | ||
# create one or more new User objects | ||
create: [UserCreateWithoutAddressInput!] | ||
# setup a relation to one or more existing User options, you can connect based on any unique field | ||
connect: [UserWhereUniqueInput!] | ||
} | ||
input UserWhereUniqueInput { | ||
id: ID | ||
displayname: String | ||
email: String | ||
id: ID | ||
displayname: String | ||
email: String | ||
} | ||
@@ -134,3 +152,3 @@ ``` | ||
"data": { | ||
"id": "TkEwTHc1QjJJaVdySzFCOkNpdHk=", | ||
"id": "ID1", | ||
"name": "NY", | ||
@@ -155,3 +173,3 @@ "neighborhoods": [ | ||
Updates also have a single argument, input, and a data and clientMutationId like create. They also have a where field. The where field type is the where unique input, same that we saw in the create if we were connecting a User (UserWhereUniqueInput). You can update based on id or any other field that has the @unique directive. | ||
Updates also have a single argument, input. input has data and clientMutationId arguments like create. They also have a where field. The where field type is the where unique input, same that we saw in the create if we were connecting a User (UserWhereUniqueInput). You can update based on id or any other field that has the @unique directive. | ||
@@ -162,6 +180,6 @@ ```graphql | ||
input UpdateCityMutationInput { | ||
data: CityUpdateInput! | ||
# In the case of city this will just be id, but any field with @unique would be allowed | ||
where: CityWhereUniqueInput! | ||
clientMutationId: String | ||
data: CityUpdateInput! | ||
# In the case of city this will just be id, but any field with @unique would be allowed | ||
where: CityWhereUniqueInput! | ||
clientMutationId: String | ||
} | ||
@@ -174,4 +192,33 @@ | ||
### Example | ||
A major difference between the UpdateInput and the CreateInput is on related types. While creating a City you can only create/connect a user. But while updating a City you can create, connect, disconnect, delete, update and upsert. | ||
```graphql | ||
type CityUpdateInput { | ||
name: String | ||
# neighborhoods is a list field, so we have the option of push, pull or set when updating | ||
neighborhoods: StringScalarListInput | ||
user: UserUpdateManyWithoutAddressInput | ||
founded: Date | ||
population: Int | ||
} | ||
type UserUpdateManyWithoutAddressInput { | ||
# create a new user that is related to this city | ||
create: [UserCreateWithoutAddressInput!] | ||
# relate an existing user to this city | ||
connect: [UserWhereUniqueInput!] | ||
# un-relate a user matching the where unique input | ||
disconnect: [UserWhereUniqueInput!] | ||
# delete a user matching the where unique input | ||
delete: [UserWhereUniqueInput!] | ||
# update a user matching the where unique input with the supplied data | ||
update: [UserUpdateWithWhereUniqueWithoutAddressInput!] | ||
# upsert a user matching the where unique input with the supplied data | ||
upsert: [UserUpsertWithWhereUniqueWithoutAddressInput!] | ||
} | ||
``` | ||
### Examples | ||
#### Update the city name and push onto neighborhoods | ||
Say we wanted to update the created city above, we could use the update resolver. | ||
@@ -183,7 +230,5 @@ | ||
where: {id: "ID1"} | ||
data: { | ||
data: { | ||
name: "New York", | ||
""" | ||
neighborhoods is a list field, so we have the option of push, pull or set when updating | ||
""" | ||
# neighborhoods is a list field, so we have the option of push, pull or set when updating | ||
neighborhoods: { | ||
@@ -194,3 +239,3 @@ push: ["east side"] | ||
clientMutationId: "updateNY" | ||
}) { | ||
}) { | ||
data { | ||
@@ -207,2 +252,268 @@ id | ||
} | ||
``` | ||
Which will return | ||
```graphql | ||
{ | ||
"data": { | ||
"updateCity": { | ||
"data": { | ||
"id": "ID1", | ||
"name": "New York", | ||
"neighborhoods": [ | ||
"queens", | ||
"manhattan", | ||
"east side" | ||
], | ||
"user": [ | ||
{ | ||
"displayname": "steve" | ||
} | ||
] | ||
}, | ||
"clientMutationId": "updateNY" | ||
} | ||
} | ||
} | ||
``` | ||
#### Update the user connected to the city | ||
```graphql | ||
mutation { | ||
updateCity(input:{ | ||
where: {id: "ID1"} | ||
data: { | ||
name: "New York", | ||
user: { | ||
update: { | ||
where: { | ||
displayname: "steve" | ||
} | ||
data: { | ||
email: "steven@example.com" | ||
} | ||
} | ||
} | ||
}, | ||
}) { | ||
data { | ||
name | ||
user { | ||
displayname | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
will return | ||
```graphql | ||
{ | ||
"data": { | ||
"updateCity": { | ||
"data": { | ||
"name": "New York", | ||
"user": [ | ||
{ | ||
"displayname": "steve", | ||
"email": "steven@example.com" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
## Upsert | ||
Upserts also have a single argument, input. input has where and clientMutationId arguments like update. But instead of a data field it has a create and an update field. If the where argument finds something the update data will be used and the record will be updated, otherwise a new record will be created with the create data | ||
```graphql | ||
type UpsertUserMutationInput { | ||
create: UserCreateInput! | ||
update: UserUpdateInput! | ||
where: UserWhereUniqueInput! | ||
clientMutationId: String | ||
} | ||
``` | ||
### Examples | ||
#### Upsert a user resulting in a create | ||
```graphql | ||
mutation { | ||
upsertUser(input:{ | ||
where: {email: "zeus@example.com"}, | ||
create: { | ||
email: "zeus@example.com" | ||
}, | ||
update: { | ||
displayname: "zeus" | ||
} | ||
}) { | ||
data { | ||
displayname | ||
} | ||
} | ||
} | ||
``` | ||
will return | ||
```graphql | ||
{ | ||
"data": { | ||
"upsertUser": { | ||
"data": { | ||
"displayname": null, | ||
"email": "zeus@example.com" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
#### Upsert a user resulting in an update | ||
```graphql | ||
mutation { | ||
upsertUser(input:{ | ||
where: {email: "zeus@example.com"}, | ||
create: { | ||
email: "zeus@example.com" | ||
}, | ||
update: { | ||
displayname: "zeus" | ||
} | ||
}) { | ||
data { | ||
displayname | ||
} | ||
} | ||
} | ||
``` | ||
will return | ||
```graphql | ||
{ | ||
"data": { | ||
"upsertUser": { | ||
"data": { | ||
"displayname": "zeus", | ||
"email": "zeus@example.com" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
## Delete | ||
Deletes also have a single argument, input. input has where and clientMutationId arguments. The data returned will be of the record that used to exist | ||
### Examples | ||
#### Delete a user | ||
```graphql | ||
mutation { | ||
deleteUser(input:{ | ||
where: {email: "zeus@example.com"}, | ||
clientMutationId: "deleteUser" | ||
}) { | ||
data { | ||
displayname | ||
} | ||
clientMutationId | ||
} | ||
} | ||
``` | ||
will return | ||
```graphql | ||
{ | ||
"data": { | ||
"deleteUser": { | ||
"data": { | ||
"displayname": "zeus", | ||
"email": "zeus@example.com" | ||
}, | ||
"clientMutationId": "deleteUser" | ||
} | ||
} | ||
} | ||
``` | ||
## UpdateMany and DeleteMany | ||
UpdateMany and DeleteMany have a single argument input. input has the same arguments (where, clientMutationId and data if an update). The difference is the where argument contains all the arguments used in [queries](https://github.com/genie-team/graphql-genie/blob/master/docs/queries.md), not just the unique fields. Many mutations return a BatchPayload which has the number of fields updated | ||
```graphql | ||
input UpdateManyCitiesMutationInput { | ||
data: CityUpdateInput! | ||
where: CityWhereInput! | ||
clientMutationId: String | ||
}: BatchPayload | ||
# see type queries documentation for more info on these arguments | ||
input CityWhereInput { | ||
user: UserWhereInput | ||
exists: CityExistsInput | ||
match: CityMatchInput | ||
range: CityRangeInput | ||
and: [CityWhereInput!] | ||
or: [CityWhereInput!] | ||
not: CityWhereInput | ||
} | ||
type BatchPayload { | ||
# The number of nodes that have been affected by the Batch operation. | ||
count: Int! | ||
clientMutationId: String | ||
} | ||
``` | ||
### Examples | ||
#### UpdateManyUsers | ||
```graphql | ||
mutation { | ||
updateManyCities(input: { | ||
where: { | ||
exists: { | ||
name: true | ||
} | ||
}, | ||
data: { | ||
name: "Everywhere is Earth" | ||
} | ||
}) { | ||
count | ||
} | ||
} | ||
``` | ||
will return | ||
```graphql | ||
{ | ||
"data": { | ||
"updateManyCities": { | ||
"count": 2 | ||
} | ||
} | ||
} | ||
``` |
@@ -0,1 +1,11 @@ | ||
- [Queries](#queries) | ||
- [Query docs](#query-docs) | ||
- [where argument](#where-argument) | ||
- [orderBy argument](#orderby-argument) | ||
- [Examples](#examples) | ||
- [get all the cities](#get-all-the-cities) | ||
- [get a single city matching an id](#get-a-single-city-matching-an-id) | ||
- [get cities using a filter and skip](#get-cities-using-a-filter-and-skip) | ||
- [Get cities and filter the output](#get-cities-and-filter-the-output) | ||
# Queries | ||
@@ -36,2 +46,5 @@ | ||
#### Query docs | ||
Let's take a close look at what a generated query looks like | ||
@@ -68,2 +81,4 @@ | ||
#### where argument | ||
The where argument will filter the returned results. | ||
@@ -95,2 +110,4 @@ | ||
#### orderBy argument | ||
The orderBy argument will sort the results. Scalar fields will simply take the ORDER_BY_OPTIONS enum which looks like. | ||
@@ -128,3 +145,3 @@ | ||
get all the cities | ||
#### get all the cities | ||
@@ -140,3 +157,3 @@ ```graphql | ||
get a single city with an id | ||
#### get a single city matching an id | ||
@@ -152,3 +169,3 @@ ```graphql | ||
range, orderBy, skip | ||
#### get cities using a filter and skip | ||
@@ -169,4 +186,5 @@ ```graphql | ||
``` | ||
#### Get cities and filter the output | ||
Note when requesting objects you can filter as part of the query, this is different than filtering on that type in the arguments of the query in that it won't filter out cities that don't match the input, it will just filter out the results of the user | ||
When requesting objects you can filter as part of the query, this is different than filtering on that type in the arguments of the query in that it won't filter out cities that don't match the input, it will just filter out the results of the user | ||
@@ -173,0 +191,0 @@ ```graphql |
- [Custom directives you can use.](#custom-directives-you-can-use) | ||
- [Scalar Types](#scalar-types) | ||
**GraphQLGenie works off regular [GraphQL type defintions](https://graphql.org/learn/schema/) with some additional features** | ||
@@ -3,0 +6,0 @@ |
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
}; | ||
import { GraphQLInputObjectType, GraphQLNonNull, GraphQLString } from 'graphql'; | ||
import { GraphQLError, GraphQLInputObjectType, GraphQLNonNull, GraphQLString } from 'graphql'; | ||
import pluralize from 'pluralize'; | ||
import { InputGenerator } from './InputGenerator'; | ||
import { getPayloadTypeDef, getPayloadTypeName, parseFilter, updateResolver } from './TypeGeneratorUtilities'; | ||
import { find, get } from 'lodash'; | ||
export class GenerateUpdate { | ||
@@ -83,2 +84,13 @@ constructor(dataResolver, objectName, types, $config, currInputObjectTypes, currOutputObjectTypeDefs, schemaInfo, schema, $relations) { | ||
count = fortuneReturn.length; | ||
if (count > 1) { | ||
Object.keys(updateArgs).forEach(fieldName => { | ||
const fields = get(this.schemaInfo, [type.name, 'fields']); | ||
if (fields) { | ||
const field = find(fields, { name: fieldName }); | ||
if (get(field, ['metadata', 'unique']) === true) { | ||
throw new GraphQLError('Can\'t update multiple values on unique field ' + fieldName); | ||
} | ||
} | ||
}); | ||
} | ||
yield Promise.all(fortuneReturn.map((fortuneRecord) => __awaiter(this, void 0, void 0, function* () { | ||
@@ -85,0 +97,0 @@ return yield updateResolver(this.dataResolver)(fortuneRecord, { update: updateArgs, where: true }, _context, _info, null, null, schemaType); |
@@ -407,3 +407,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
else { | ||
reject(new FindByUniqueError('tried to connect using unique value that does not exist ', 'connect', { arg: connectArg, typename: returnTypeName })); | ||
reject(new FindByUniqueError(`connect: ${returnTypeName} does not exist with where args ${JSON.stringify(connectArg)}`, 'disconnect', { arg: connectArg, typename: returnTypeName })); | ||
} | ||
@@ -434,3 +434,3 @@ }).catch(reason => { | ||
else { | ||
reject(new FindByUniqueError('tried to disconnect using unique value that does not exist ', 'disconnect', { arg: disconnectArg, typename: returnTypeName })); | ||
reject(new FindByUniqueError(`disconnect: ${returnTypeName} does not exist with where args ${JSON.stringify(disconnectArg)}`, 'disconnect', { arg: disconnectArg, typename: returnTypeName })); | ||
} | ||
@@ -437,0 +437,0 @@ }).catch(reason => { |
{ | ||
"name": "graphql-genie", | ||
"version": "0.2.21", | ||
"version": "0.2.23", | ||
"description": "GraphQL Genie", | ||
@@ -5,0 +5,0 @@ "browser": "./lib/browser.umd.js", |
@@ -5,2 +5,15 @@ <h1 align="center"> | ||
- [GraphQL Genie](#graphql-genie) | ||
- [Demo](#demo) | ||
- [Installation](#installation) | ||
- [Getting started](#getting-started) | ||
- [Data Store](#data-store) | ||
- [GraphQL Genie Schema API](#graphql-genie-schema-api) | ||
- [GraphQLGenie API](#graphqlgenie-api) | ||
- [Subscriptions](#subscriptions) | ||
- [Authentication](#authentication) | ||
- [How do I do/add [thing]](#how-do-i-doadd-thing) | ||
- [Features/Advantages/Differences](#featuresadvantagesdifferences) | ||
- [Thanks/Credit](#thankscredit) | ||
# GraphQL Genie | ||
@@ -19,3 +32,3 @@ | ||
## Demo | ||
### Demo | ||
@@ -76,12 +89,9 @@ [See the fully featured demo](https://genie-team.github.io/graphql-genie-client/). Create a schema (or use the default provided) and a fully featured api is created. Click the search icon to use GraphiQL to view docs and create or mock data. See [graphql genie client](https://github.com/genie-team/graphql-genie-client) on github for more info on the demo. | ||
### Subscriptions | ||
[GraphQL Genie](https://github.com/genie-team/graphql-genie) also supports subscriptions with the [subscriptions plugin](https://github.com/genie-team/graphql-genie/tree/master/plugins/subscriptions). | ||
### GraphQL Genie Schema API | ||
* **Queries** | ||
* [Type Queries](https://github.com/genie-team/graphql-genie/blob/master/docs/queries.md) | ||
* [Type Queries](https://github.com/genie-team/graphql-genie/blob/master/docs/queries.md) | ||
* [Connection Queries](https://github.com/genie-team/graphql-genie/blob/master/docs/connections.md) | ||
* [Mutations](https://github.com/genie-team/graphql-genie/blob/master/docs/mutations.md) | ||
* Also see the [subscriptions plugin](https://github.com/genie-team/graphql-genie/tree/master/plugins/subscriptions) | ||
WIP | ||
### GraphQLGenie API | ||
@@ -91,2 +101,7 @@ | ||
### Subscriptions | ||
[GraphQL Genie](https://github.com/genie-team/graphql-genie) also supports subscriptions with the [subscriptions plugin](https://github.com/genie-team/graphql-genie/tree/master/plugins/subscriptions). | ||
### Authentication | ||
@@ -127,8 +142,4 @@ | ||
### TODO | ||
## Thanks/Credit | ||
- [ ] API Documentation | ||
#### Thanks/Credit | ||
[Prisma GraphQL / Graphcool](https://github.com/prismagraphql/prisma) for inspiration | ||
@@ -135,0 +146,0 @@ |
import { GraphQLFieldResolver, GraphQLInputObjectType, GraphQLInputType, GraphQLNonNull, GraphQLOutputType, GraphQLSchema, GraphQLString, IntrospectionObjectType, IntrospectionType } from 'graphql'; | ||
import { GraphQLError, GraphQLFieldResolver, GraphQLInputObjectType, GraphQLInputType, GraphQLNonNull, GraphQLOutputType, GraphQLSchema, GraphQLString, IntrospectionObjectType, IntrospectionType } from 'graphql'; | ||
import pluralize from 'pluralize'; | ||
@@ -7,3 +7,3 @@ import { DataResolver, GenerateConfig, TypeGenerator } from './GraphQLGenieInterfaces'; | ||
import { Relations, getPayloadTypeDef, getPayloadTypeName, parseFilter, updateResolver } from './TypeGeneratorUtilities'; | ||
import { find, get } from 'lodash'; | ||
export class GenerateUpdate implements TypeGenerator { | ||
@@ -103,5 +103,16 @@ private objectName: string; | ||
const options = parseFilter(filter, schemaType); | ||
let fortuneReturn: Array<any> = await this.dataResolver.find(type.name, null, options, {context: _context, info: _info}); | ||
let fortuneReturn: Array<any> = await this.dataResolver.find(type.name, null, options, { context: _context, info: _info }); | ||
fortuneReturn = fortuneReturn.filter(element => element !== null && element !== undefined); | ||
count = fortuneReturn.length; | ||
if (count > 1) { | ||
Object.keys(updateArgs).forEach(fieldName => { | ||
const fields = get(this.schemaInfo, [type.name, 'fields']); | ||
if (fields) { | ||
const field = find(fields, { name: fieldName }); | ||
if (get(field, ['metadata', 'unique']) === true) { | ||
throw new GraphQLError('Can\'t update multiple values on unique field ' + fieldName); | ||
} | ||
} | ||
}); | ||
} | ||
await Promise.all(fortuneReturn.map(async (fortuneRecord) => { | ||
@@ -108,0 +119,0 @@ return await updateResolver(this.dataResolver)(fortuneRecord, { update: updateArgs, where: true }, _context, _info, null, null, <GraphQLOutputType>schemaType); |
@@ -798,2 +798,33 @@ import { ApolloClient } from 'apollo-client'; | ||
test('update - update many users, try to set unique field', async () => { | ||
const updateManyUsers = gql` | ||
mutation { | ||
updateManyUsers(input: { | ||
where: { | ||
exists: { | ||
age: true | ||
} | ||
} | ||
data: { | ||
email: "this update should fail" | ||
} | ||
}) { | ||
count | ||
} | ||
} | ||
`; | ||
expect.assertions(2); | ||
try { | ||
const result = await client.mutate({ | ||
mutation: updateManyUsers | ||
}); | ||
} catch (e) { | ||
expect(e).not.toBeNull(); | ||
expect(e['message']).toContain('multiple'); | ||
} | ||
}); | ||
test('create - unique field check', async () => { | ||
@@ -825,2 +856,3 @@ | ||
`; | ||
expect.assertions(2); | ||
try { | ||
@@ -827,0 +859,0 @@ const result = await client.mutate({ |
@@ -444,3 +444,3 @@ import { GraphQLArgument, GraphQLInputObjectType, GraphQLList, GraphQLNamedType, GraphQLObjectType, GraphQLOutputType, GraphQLResolveInfo, GraphQLSchema, GraphQLType, IntrospectionObjectType, IntrospectionType, defaultFieldResolver, getNamedType, isEnumType, isInterfaceType, isObjectType, isScalarType, isUnionType } from 'graphql'; | ||
} else { | ||
reject(new FindByUniqueError('tried to connect using unique value that does not exist ', 'connect', {arg: connectArg, typename: returnTypeName})); | ||
reject(new FindByUniqueError(`connect: ${returnTypeName} does not exist with where args ${JSON.stringify(connectArg)}`, 'disconnect', {arg: connectArg, typename: returnTypeName})); | ||
} | ||
@@ -470,3 +470,3 @@ }).catch(reason => { | ||
} else { | ||
reject(new FindByUniqueError('tried to disconnect using unique value that does not exist ', 'disconnect', {arg: disconnectArg, typename: returnTypeName})); | ||
reject(new FindByUniqueError(`disconnect: ${returnTypeName} does not exist with where args ${JSON.stringify(disconnectArg)}`, 'disconnect', {arg: disconnectArg, typename: returnTypeName})); | ||
} | ||
@@ -473,0 +473,0 @@ }).catch(reason => { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
989833
21891
145