diff --git a/docs/sdl.md b/docs/sdl.md index 647cb1b..325c598 100644 --- a/docs/sdl.md +++ b/docs/sdl.md @@ -4,7 +4,7 @@ - [Scalar Types](#scalar-types) - [Example](#example) - [Altering your schema and Migrations](#altering-your-schema-and-migrations) - - [Special cases and scripting migrations](#special-cases-and-scription-migrations) + - [Special cases and scripting migrations](#special-cases-and-scripting-migrations) ## Type Definitions @@ -29,12 +29,14 @@ GraphQL Genie supports interfaces and unions! You may want to look into using th * The directive @connection can be put on a list field to turn it into a type following the [Relay Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm) rather than just returning a normal list. * **@model** * By default any object type will be part of the CRUD model, using the @model directive will limit to just types that use this directive -* **@createdTimestamp** +* **@createdTimestamp (allowManual: Boolean = false)** * The field will be automatically set when the record is created * Must be of type DateTime -* **@updatedTimestamp** + * If you want to also be able to allow the field to be set manually in mutations set allowManual to true +* **@updatedTimestamp (allowManual: Boolean = false)** * The field will automatically be updated when the record is updated * Must be of type DateTime + * If you want to also be able to allow the field to be set manually in mutations set allowManual to true * **@storeName(value: String!)** * If you want the actual name of the type in your backend store to be something other than based off the type name. Using this you can effectively rename your type without actually migrating any data * Interfaces and Unions diff --git a/package.json b/package.json index 4958826..286b815 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphql-genie", - "version": "0.4.9", + "version": "0.4.10", "description": "GraphQL Genie", "browser": "./lib/browser.umd.js", "jsnext:main": "./lib/module.js", @@ -40,7 +40,7 @@ "lint": "tslint -c tslint.json -p linttsconfig.json --fix", "lint-no-fix": "tslint -c tslint.json -p linttsconfig.json", "postpublish": "npm run tag", - "tag": "git tag v`npm v graphql-genie version` && git push origin --tags" + "tag": "git tag -a v`npm v graphql-genie version` && git push origin --tags" }, "jest": { "testURL": "http://localhost", diff --git a/src/FortuneGraph.ts b/src/FortuneGraph.ts index d1743c2..1fb5194 100644 --- a/src/FortuneGraph.ts +++ b/src/FortuneGraph.ts @@ -387,7 +387,7 @@ export default class FortuneGraph implements DataResolver { return this.fortuneTypeNames; } - private getStoreName = (type, currStoreName: string, currTypeName: string): {storeName: string, typeName: string} => { + private getStoreName = (type, currStoreName: string, currTypeName: string): { storeName: string, typeName: string } => { let storeName = get(type, 'metadata.storeName', null); let typeName = type.name; if (!isEmpty(storeName) && currStoreName && currStoreName !== storeName) { @@ -442,7 +442,9 @@ export default class FortuneGraph implements DataResolver { this.addInputHook(name, (context, record) => { switch (context.request.method) { case 'create': - record[field.name] = new Date(); + if ((isArray && isEmpty(record[field.name])) || !record[field.name]) { + record[field.name] = new Date(); + } return record; } }); @@ -452,7 +454,9 @@ export default class FortuneGraph implements DataResolver { switch (context.request.method) { case 'update': if (!('replace' in update)) update.replace = {}; - update.replace[field.name] = new Date(); + if ((isArray && isEmpty(update.replace[field.name])) || !update.replace[field.name]) { + update.replace[field.name] = new Date(); + } return update; } }); diff --git a/src/GenerateMigrations.ts b/src/GenerateMigrations.ts index 9c24954..dc2313b 100644 --- a/src/GenerateMigrations.ts +++ b/src/GenerateMigrations.ts @@ -71,7 +71,7 @@ export class GenerateMigrations implements TypeGenerator { Note when merging list fields by default the array in the provided data will replace the existing data array. If you don't want to do that instead of providing an array you can provide an object with fields for push and pull or set. ` }, defaultTypename: { - type: GraphQLBoolean, + type: GraphQLString, descriptions: 'Must be provided if every object in data does not have a `__typename` property or ids with the typename encoded' }, conditions: { diff --git a/src/GraphQLSchemaBuilder.ts b/src/GraphQLSchemaBuilder.ts index d691289..05784dc 100644 --- a/src/GraphQLSchemaBuilder.ts +++ b/src/GraphQLSchemaBuilder.ts @@ -36,11 +36,9 @@ export class GraphQLSchemaBuilder { directive @unique on FIELD_DEFINITION - directive @updatedTimestamp on FIELD_DEFINITION + directive @updatedTimestamp(allowManual: Boolean = false) on FIELD_DEFINITION - directive @createdTimestamp on FIELD_DEFINITION - - directive @createdTimestamp on FIELD_DEFINITION + directive @createdTimestamp(allowManual: Boolean = false) on FIELD_DEFINITION """ An object with an ID @@ -408,6 +406,9 @@ class UpdatedTimestampDirective extends SchemaDirectiveVisitor { const type = field.type; if (type.name === 'DateTime') { field['updatedTimestamp'] = true; + if (this.args && this.args.allowManual) { + field['updatedTimestampAllowManual'] = true; + } } } } @@ -416,6 +417,9 @@ class CreatedTimestampDirective extends SchemaDirectiveVisitor { const type = field.type; if (type.name === 'DateTime') { field.createdTimestamp = true; + if (this.args && this.args.allowManual) { + field['createdTimestampAllowManual'] = true; + } } } } diff --git a/src/InputGenerator.ts b/src/InputGenerator.ts index 371b87e..7de1ca8 100644 --- a/src/InputGenerator.ts +++ b/src/InputGenerator.ts @@ -61,9 +61,9 @@ export class InputGenerator { if (field) { if (field.name === 'id') { isAutoField = true; - } else if (get(field, 'metadata.updatedTimestamp') === true) { + } else if (get(field, 'metadata.updatedTimestamp') === true && !get(field, 'metadata.updatedTimestampAllowManual', false) ) { isAutoField = true; - } else if (get(field, 'metadata.createdTimestamp') === true) { + } else if (get(field, 'metadata.createdTimestamp') === true && !get(field, 'metadata.createdTimestampAllowManual', false)) { isAutoField = true; } } diff --git a/src/tests/__tests__/mutationTests.ts b/src/tests/__tests__/mutationTests.ts index ed167e1..15e11c0 100644 --- a/src/tests/__tests__/mutationTests.ts +++ b/src/tests/__tests__/mutationTests.ts @@ -87,6 +87,7 @@ describe('mutationTests', () => { }); test('create - create post connect author', async () => { + const date = new Date(1988, 2, 23); const post = await client.mutate({ mutation: gql`mutation { createPost( @@ -100,6 +101,8 @@ describe('mutationTests', () => { email: "zeus@example.com" } } + created: "${date.toISOString()}" + createdManual: "${date.toISOString()}" } } ) { @@ -111,17 +114,23 @@ describe('mutationTests', () => { author { email } + created + createdManual } } } ` }); + console.log('post :', post); testData.posts.push(post.data.createPost.data); expect(post.data.createPost.data.title).toBe('Genie is great'); expect(post.data.createPost.data.text).toBe('Look how fast I can create an executable schema'); expect(post.data.createPost.data.tags).toEqual(['genie', 'graphql', 'database']); expect(post.data.createPost.data.author.email).toBe('zeus@example.com'); - + const created = new Date(post.data.createPost.data.created); + expect(created > date).toBe(true); + const createdManual = new Date(post.data.createPost.data.createdManual); + expect(createdManual.getTime()).toBe(date.getTime()); }); test('update - push onto tags', async () => { @@ -258,6 +267,36 @@ describe('mutationTests', () => { }); + test('update - updated set to passed in value', async () => { + const date = new Date(1988, 2, 23); + const post = await client.mutate({ + mutation: gql`mutation { + updatePost( + input: { + data: { + updated: "${date.toISOString()}" + updatedManual: "${date.toISOString()}" + } + where: { + id: "${testData.posts[0].id}" + } + } + ) { + data { + id + updated + updatedManual + } + } + } + ` + }); + const updated = new Date(post.data.updatePost.data.updated); + expect(updated > date).toBe(true); + const updatedManual = new Date(post.data.updatePost.data.updatedManual); + expect(updatedManual.getTime()).toBe(date.getTime()); + }); + test('update - pull from tags', async () => { const post = await client.mutate({ mutation: gql`mutation { diff --git a/src/tests/setupTests.ts b/src/tests/setupTests.ts index 5357694..bc2627b 100644 --- a/src/tests/setupTests.ts +++ b/src/tests/setupTests.ts @@ -24,6 +24,8 @@ type Post implements Submission { published: Boolean @default(value: "true") created: DateTime @createdTimestamp updated: DateTime @updatedTimestamp + createdManual: DateTime @createdTimestamp(allowManual: true) + updatedManual: DateTime @updatedTimestamp(allowManual: true) } type Comment implements Submission {