From 22fa30669af90c27230079f3acb1e0615328605e Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Tue, 24 Oct 2017 03:49:55 +0530 Subject: [PATCH 1/8] use integer ids Signed-off-by: Arnav Gupta --- .gitignore | 1 + lib/graphQl/index.js | 2 +- lib/graphQl/joiConverter.js | 2 +- lib/graphQl/readTypes.js | 2 +- lib/graphQl/writeArgs.js | 2 +- lib/jsonApi.js | 2 +- lib/ourJoi.js | 2 +- lib/routes/create.js | 2 +- lib/swagger/paths.js | 2 +- lib/swagger/resources.js | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 9dab88aa..e4573598 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules npm-debug.log /.nyc_output/ /coverage/ +.idea/ diff --git a/lib/graphQl/index.js b/lib/graphQl/index.js index a408d4af..d24a6d61 100644 --- a/lib/graphQl/index.js +++ b/lib/graphQl/index.js @@ -87,7 +87,7 @@ jsonApiGraphQL.generateWriteSchema = (allReadTypes, allResourceConfig, allWriteT result[`delete${uName}`] = { description: `Delete a ${resourceConfig.resource} resource`, args: { - id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLString) } + id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt) } }, type: allReadTypes[resource], resolve: resolvers.delete.bind(resolvers, resourceConfig) diff --git a/lib/graphQl/joiConverter.js b/lib/graphQl/joiConverter.js index 8448e674..ae93fdd8 100644 --- a/lib/graphQl/joiConverter.js +++ b/lib/graphQl/joiConverter.js @@ -81,7 +81,7 @@ const oneRelationship = new graphQl.GraphQLInputObjectType({ name: 'oneRelationship', fields: { id: { - type: new graphQl.GraphQLNonNull(graphQl.GraphQLString), + type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt), description: 'The UUID of another resource' } } diff --git a/lib/graphQl/readTypes.js b/lib/graphQl/readTypes.js index 085062c4..1dc79098 100644 --- a/lib/graphQl/readTypes.js +++ b/lib/graphQl/readTypes.js @@ -28,7 +28,7 @@ readTypes.createReadType = (resourceConfig, otherTypes, allUnionTypes) => { fields () { const fields = { id: { - type: new graphQl.GraphQLNonNull(graphQl.GraphQLString), + type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt), description: 'The UUID of the resource' } } diff --git a/lib/graphQl/writeArgs.js b/lib/graphQl/writeArgs.js index 99c287f8..7b432e4a 100644 --- a/lib/graphQl/writeArgs.js +++ b/lib/graphQl/writeArgs.js @@ -7,7 +7,7 @@ const joiConverter = require('./joiConverter.js') writeArgs.generate = (resource, allWriteTypes) => { const args = { - id: { type: graphQl.GraphQLString } + id: { type: graphQl.GraphQLInt } } const resourceConfig = jsonApi._resources[resource] Object.keys(resourceConfig.attributes).forEach(attribute => { diff --git a/lib/jsonApi.js b/lib/jsonApi.js index 258ae523..ca15cb3e 100644 --- a/lib/jsonApi.js +++ b/lib/jsonApi.js @@ -93,7 +93,7 @@ jsonApi.define = resourceConfig => { }, resourceConfig.searchParams, pagination.joiPageDefinition) resourceConfig.attributes = _.assign({ - id: ourJoi.Joi.string().required() + id: ourJoi.Joi.number().integer().required() .description('Unique resource identifier') .example('1234'), type: ourJoi.Joi.string().required().valid(resourceConfig.resource) diff --git a/lib/ourJoi.js b/lib/ourJoi.js index 979752b1..c0f2175f 100644 --- a/lib/ourJoi.js +++ b/lib/ourJoi.js @@ -5,7 +5,7 @@ const Joi = require('joi') ourJoi._joiBase = resourceName => { const relationType = Joi.object().keys({ - id: Joi.string().required(), + id: Joi.number().integer().required(), type: Joi.any().required().valid(resourceName), meta: Joi.object().optional() }) diff --git a/lib/routes/create.js b/lib/routes/create.js index 5bf51f24..869f3978 100644 --- a/lib/routes/create.js +++ b/lib/routes/create.js @@ -34,7 +34,7 @@ createRoute.register = () => { const theirs = request.params.data theirResource = _.assign( { - id: uuid.v4(), + id: 0, type: request.params.type }, theirs.id && { id: theirs.id }, diff --git a/lib/swagger/paths.js b/lib/swagger/paths.js index c1135cff..bdf6572d 100644 --- a/lib/swagger/paths.js +++ b/lib/swagger/paths.js @@ -320,7 +320,7 @@ swaggerPaths._getRelationModel = () => ({ type: 'string' }, id: { - type: 'string' + type: 'number' }, meta: { type: 'object' diff --git a/lib/swagger/resources.js b/lib/swagger/resources.js index 15893fbc..629e959a 100644 --- a/lib/swagger/resources.js +++ b/lib/swagger/resources.js @@ -102,7 +102,7 @@ swaggerPaths._getResourceDefinition = resourceConfig => { type: 'string' }, id: { - type: 'string' + type: 'number' }, meta: { type: 'object' From 0b3363dbabbb2abcc5351d14787b1c74f272bb6f Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Tue, 24 Oct 2017 04:14:09 +0530 Subject: [PATCH 2/8] fix paths Signed-off-by: Arnav Gupta --- lib/swagger/paths.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/swagger/paths.js b/lib/swagger/paths.js index bdf6572d..111c0633 100644 --- a/lib/swagger/paths.js +++ b/lib/swagger/paths.js @@ -270,7 +270,7 @@ swaggerPaths._getPathOperationObject = options => { in: 'path', description: 'id of specific instance to lookup', required: true, - type: 'string' + type: 'number' }) } From fefe9570bc4cc2c4ca95d77223aff5c7c78fef9a Mon Sep 17 00:00:00 2001 From: Wolfgang Faust Date: Thu, 2 Mar 2017 14:13:23 -0500 Subject: [PATCH 3/8] Add resourceConfig.generateId to disable automatic ID generation. --- documentation/resources.md | 3 ++ example/handlers/autoincrementHandler.js | 15 ++++++++ example/resources/autoincrement.js | 26 +++++++++++++ lib/routes/create.js | 6 +-- test/post-resource.js | 47 ++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 example/handlers/autoincrementHandler.js create mode 100644 example/resources/autoincrement.js diff --git a/documentation/resources.md b/documentation/resources.md index 3e32d3a8..ab75a722 100644 --- a/documentation/resources.md +++ b/documentation/resources.md @@ -97,3 +97,6 @@ url: jsonApi.Joi.string().uri().meta("readonly").description("This attribute can ``` If you look through the example json:api resources in the `/example/resources` folder things should become clearer. + +#### generateId +By default, the server autogenerates a UUID for resources which are created without specifying an ID. To disable this behavior (for example, if the database generates an ID by auto-incrementing), set `generateId` to `false`. If the resource's ID is not a UUID, it is also necessary to specify an `id` attribute with the correct type. See `/examples/resorces/autoincrement.js` for an example of such a resource. diff --git a/example/handlers/autoincrementHandler.js b/example/handlers/autoincrementHandler.js new file mode 100644 index 00000000..5fc73bd1 --- /dev/null +++ b/example/handlers/autoincrementHandler.js @@ -0,0 +1,15 @@ +'use strict' + +const jsonApi = require('../..') + +const chainHandler = new jsonApi.ChainHandler() + +let i = 2 // 1 is used by the example in resources/autoincrement.js +chainHandler.beforeCreate = (request, newResource, callback) => { + // Autoincrement the ID. + // In practice this would actually be handled by the underlying database. + newResource.id = (i++).toString() + callback(null, request, newResource) +} + +module.exports = chainHandler.chain(new jsonApi.MemoryHandler()) diff --git a/example/resources/autoincrement.js b/example/resources/autoincrement.js new file mode 100644 index 00000000..81028bc8 --- /dev/null +++ b/example/resources/autoincrement.js @@ -0,0 +1,26 @@ +'use strict' + +const jsonApi = require('../../.') +const autoincrementHandler = require('../handlers/autoincrementHandler.js') + +jsonApi.define({ + namespace: 'json:api', + resource: 'autoincrement', + description: 'Demonstration of a resource with an auto-incrementing ID', + handlers: autoincrementHandler, + searchParams: { }, + generateId: false, + attributes: { + id: jsonApi.Joi.string(), + name: jsonApi.Joi.string() + .description('The name of the item') + .example('Hello') + }, + examples: [ + { + id: '1', + type: 'autoincrement', + name: 'Foo' + } + ] +}) diff --git a/lib/routes/create.js b/lib/routes/create.js index 5bf51f24..c88fa890 100644 --- a/lib/routes/create.js +++ b/lib/routes/create.js @@ -33,10 +33,8 @@ createRoute.register = () => { callback => { const theirs = request.params.data theirResource = _.assign( - { - id: uuid.v4(), - type: request.params.type - }, + { type: request.params.type }, + (request.resourceConfig.generateId !== false) && { id: uuid.v4() }, theirs.id && { id: theirs.id }, theirs.attributes, { meta: theirs.meta } diff --git a/test/post-resource.js b/test/post-resource.js index 5ca18331..f3e8c735 100644 --- a/test/post-resource.js +++ b/test/post-resource.js @@ -201,6 +201,53 @@ describe('Testing jsonapi-server', () => { done() }) }) + describe('creates a resource with non-UUID ID', () => { + let id + + it('works', done => { + const data = { + method: 'post', + url: 'http://localhost:16006/rest/autoincrement', + headers: { + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + 'data': { + 'type': 'autoincrement', + 'attributes': { + 'name': 'bar' + } + } + }) + } + helpers.request(data, (err, res, json) => { + assert.equal(err, null) + json = helpers.validateJson(json) + + assert.equal(json.data.id, '2') + assert.equal(res.headers.location, `http://localhost:16006/rest/autoincrement/${json.data.id}`) + assert.equal(res.statusCode, '201', 'Expecting 201') + assert.equal(json.data.type, 'autoincrement', 'Should be a autoincrement resource') + id = json.data.id + + done() + }) + }) + + it('new resource is retrievable', done => { + const url = `http://localhost:16006/rest/autoincrement/${id}` + helpers.request({ + method: 'GET', + url + }, (err, res, json) => { + assert.equal(err, null) + json = helpers.validateJson(json) + assert.equal(res.statusCode, '200', 'Expecting 200 OK') + assert.equal(json.included.length, 0, 'Should be no included resources') + done() + }) + }) + }) }) }) From 4912310c4d4d29e5e0642a738f5793893d516fc5 Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Thu, 2 Nov 2017 01:10:42 +0530 Subject: [PATCH 4/8] no uuids Signed-off-by: Arnav Gupta --- lib/graphQl/joiConverter.js | 2 +- lib/graphQl/readTypes.js | 2 +- lib/routes/create.js | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/graphQl/joiConverter.js b/lib/graphQl/joiConverter.js index ae93fdd8..951ff6d0 100644 --- a/lib/graphQl/joiConverter.js +++ b/lib/graphQl/joiConverter.js @@ -82,7 +82,7 @@ const oneRelationship = new graphQl.GraphQLInputObjectType({ fields: { id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt), - description: 'The UUID of another resource' + description: 'The id of another resource' } } }) diff --git a/lib/graphQl/readTypes.js b/lib/graphQl/readTypes.js index 1dc79098..0ac95e04 100644 --- a/lib/graphQl/readTypes.js +++ b/lib/graphQl/readTypes.js @@ -29,7 +29,7 @@ readTypes.createReadType = (resourceConfig, otherTypes, allUnionTypes) => { const fields = { id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt), - description: 'The UUID of the resource' + description: 'The id of the resource' } } diff --git a/lib/routes/create.js b/lib/routes/create.js index c88fa890..fd10f60d 100644 --- a/lib/routes/create.js +++ b/lib/routes/create.js @@ -5,7 +5,6 @@ const async = require('async') const _ = { assign: require('lodash.assign') } -const uuid = require('uuid') const helper = require('./helper.js') const router = require('../router.js') const postProcess = require('../postProcess.js') @@ -34,8 +33,6 @@ createRoute.register = () => { const theirs = request.params.data theirResource = _.assign( { type: request.params.type }, - (request.resourceConfig.generateId !== false) && { id: uuid.v4() }, - theirs.id && { id: theirs.id }, theirs.attributes, { meta: theirs.meta } ) From 766310a871e6c5628bdc637beb2c3fa31d4be592 Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Thu, 2 Nov 2017 01:14:16 +0530 Subject: [PATCH 5/8] jsonapi spec says string ids Signed-off-by: Arnav Gupta --- lib/graphQl/index.js | 2 +- lib/graphQl/joiConverter.js | 2 +- lib/graphQl/readTypes.js | 2 +- lib/graphQl/writeArgs.js | 2 +- lib/jsonApi.js | 2 +- lib/ourJoi.js | 2 +- lib/swagger/paths.js | 2 +- lib/swagger/resources.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/graphQl/index.js b/lib/graphQl/index.js index d24a6d61..a408d4af 100644 --- a/lib/graphQl/index.js +++ b/lib/graphQl/index.js @@ -87,7 +87,7 @@ jsonApiGraphQL.generateWriteSchema = (allReadTypes, allResourceConfig, allWriteT result[`delete${uName}`] = { description: `Delete a ${resourceConfig.resource} resource`, args: { - id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt) } + id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLString) } }, type: allReadTypes[resource], resolve: resolvers.delete.bind(resolvers, resourceConfig) diff --git a/lib/graphQl/joiConverter.js b/lib/graphQl/joiConverter.js index 951ff6d0..b5784c6f 100644 --- a/lib/graphQl/joiConverter.js +++ b/lib/graphQl/joiConverter.js @@ -81,7 +81,7 @@ const oneRelationship = new graphQl.GraphQLInputObjectType({ name: 'oneRelationship', fields: { id: { - type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt), + type: new graphQl.GraphQLNonNull(graphQl.GraphQLString), description: 'The id of another resource' } } diff --git a/lib/graphQl/readTypes.js b/lib/graphQl/readTypes.js index 0ac95e04..22bb5945 100644 --- a/lib/graphQl/readTypes.js +++ b/lib/graphQl/readTypes.js @@ -28,7 +28,7 @@ readTypes.createReadType = (resourceConfig, otherTypes, allUnionTypes) => { fields () { const fields = { id: { - type: new graphQl.GraphQLNonNull(graphQl.GraphQLInt), + type: new graphQl.GraphQLNonNull(graphQl.GraphQLString), description: 'The id of the resource' } } diff --git a/lib/graphQl/writeArgs.js b/lib/graphQl/writeArgs.js index 7b432e4a..99c287f8 100644 --- a/lib/graphQl/writeArgs.js +++ b/lib/graphQl/writeArgs.js @@ -7,7 +7,7 @@ const joiConverter = require('./joiConverter.js') writeArgs.generate = (resource, allWriteTypes) => { const args = { - id: { type: graphQl.GraphQLInt } + id: { type: graphQl.GraphQLString } } const resourceConfig = jsonApi._resources[resource] Object.keys(resourceConfig.attributes).forEach(attribute => { diff --git a/lib/jsonApi.js b/lib/jsonApi.js index ca15cb3e..436944e7 100644 --- a/lib/jsonApi.js +++ b/lib/jsonApi.js @@ -93,7 +93,7 @@ jsonApi.define = resourceConfig => { }, resourceConfig.searchParams, pagination.joiPageDefinition) resourceConfig.attributes = _.assign({ - id: ourJoi.Joi.number().integer().required() + id: ourJoi.Joi.string() .description('Unique resource identifier') .example('1234'), type: ourJoi.Joi.string().required().valid(resourceConfig.resource) diff --git a/lib/ourJoi.js b/lib/ourJoi.js index c0f2175f..72ec1055 100644 --- a/lib/ourJoi.js +++ b/lib/ourJoi.js @@ -5,7 +5,7 @@ const Joi = require('joi') ourJoi._joiBase = resourceName => { const relationType = Joi.object().keys({ - id: Joi.number().integer().required(), + id: Joi.string(), type: Joi.any().required().valid(resourceName), meta: Joi.object().optional() }) diff --git a/lib/swagger/paths.js b/lib/swagger/paths.js index 111c0633..2fd8230b 100644 --- a/lib/swagger/paths.js +++ b/lib/swagger/paths.js @@ -320,7 +320,7 @@ swaggerPaths._getRelationModel = () => ({ type: 'string' }, id: { - type: 'number' + type: 'string' }, meta: { type: 'object' diff --git a/lib/swagger/resources.js b/lib/swagger/resources.js index 629e959a..15893fbc 100644 --- a/lib/swagger/resources.js +++ b/lib/swagger/resources.js @@ -102,7 +102,7 @@ swaggerPaths._getResourceDefinition = resourceConfig => { type: 'string' }, id: { - type: 'number' + type: 'string' }, meta: { type: 'object' From b7f47dbefcef9c439d666f35e348099c98659b02 Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Thu, 2 Nov 2017 01:17:54 +0530 Subject: [PATCH 6/8] fix shit Signed-off-by: Arnav Gupta --- lib/graphQl/joiConverter.js | 2 +- lib/graphQl/readTypes.js | 2 +- lib/jsonApi.js | 2 +- lib/ourJoi.js | 2 +- lib/routes/create.js | 3 +++ lib/swagger/paths.js | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/graphQl/joiConverter.js b/lib/graphQl/joiConverter.js index b5784c6f..8448e674 100644 --- a/lib/graphQl/joiConverter.js +++ b/lib/graphQl/joiConverter.js @@ -82,7 +82,7 @@ const oneRelationship = new graphQl.GraphQLInputObjectType({ fields: { id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLString), - description: 'The id of another resource' + description: 'The UUID of another resource' } } }) diff --git a/lib/graphQl/readTypes.js b/lib/graphQl/readTypes.js index 22bb5945..085062c4 100644 --- a/lib/graphQl/readTypes.js +++ b/lib/graphQl/readTypes.js @@ -29,7 +29,7 @@ readTypes.createReadType = (resourceConfig, otherTypes, allUnionTypes) => { const fields = { id: { type: new graphQl.GraphQLNonNull(graphQl.GraphQLString), - description: 'The id of the resource' + description: 'The UUID of the resource' } } diff --git a/lib/jsonApi.js b/lib/jsonApi.js index 436944e7..258ae523 100644 --- a/lib/jsonApi.js +++ b/lib/jsonApi.js @@ -93,7 +93,7 @@ jsonApi.define = resourceConfig => { }, resourceConfig.searchParams, pagination.joiPageDefinition) resourceConfig.attributes = _.assign({ - id: ourJoi.Joi.string() + id: ourJoi.Joi.string().required() .description('Unique resource identifier') .example('1234'), type: ourJoi.Joi.string().required().valid(resourceConfig.resource) diff --git a/lib/ourJoi.js b/lib/ourJoi.js index 72ec1055..979752b1 100644 --- a/lib/ourJoi.js +++ b/lib/ourJoi.js @@ -5,7 +5,7 @@ const Joi = require('joi') ourJoi._joiBase = resourceName => { const relationType = Joi.object().keys({ - id: Joi.string(), + id: Joi.string().required(), type: Joi.any().required().valid(resourceName), meta: Joi.object().optional() }) diff --git a/lib/routes/create.js b/lib/routes/create.js index fd10f60d..c88fa890 100644 --- a/lib/routes/create.js +++ b/lib/routes/create.js @@ -5,6 +5,7 @@ const async = require('async') const _ = { assign: require('lodash.assign') } +const uuid = require('uuid') const helper = require('./helper.js') const router = require('../router.js') const postProcess = require('../postProcess.js') @@ -33,6 +34,8 @@ createRoute.register = () => { const theirs = request.params.data theirResource = _.assign( { type: request.params.type }, + (request.resourceConfig.generateId !== false) && { id: uuid.v4() }, + theirs.id && { id: theirs.id }, theirs.attributes, { meta: theirs.meta } ) diff --git a/lib/swagger/paths.js b/lib/swagger/paths.js index 2fd8230b..c1135cff 100644 --- a/lib/swagger/paths.js +++ b/lib/swagger/paths.js @@ -270,7 +270,7 @@ swaggerPaths._getPathOperationObject = options => { in: 'path', description: 'id of specific instance to lookup', required: true, - type: 'number' + type: 'string' }) } From 29970ebf5f5c5796dc6eb740b72ee94c844ed2c7 Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Thu, 2 Nov 2017 01:20:00 +0530 Subject: [PATCH 7/8] changelog Signed-off-by: Arnav Gupta --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfda203f..5bef6aa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- 2017-11-02 - v3.3.0 +- 2017-11-02 - support generateId type - 2017-10-21 - v3.2.2 - 2017-10-21 - Report coverage with Coveralls. - 2017-10-21 - Modernise script. From 81aaa53ff9e603a0fa9293e415e59772e898f193 Mon Sep 17 00:00:00 2001 From: Arnav Gupta Date: Thu, 2 Nov 2017 01:20:05 +0530 Subject: [PATCH 8/8] 3.3.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ca7ad86..530f5c57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "jsonapi-server", - "version": "3.2.1", + "version": "3.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c1b5053b..dc45a7db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsonapi-graphql-server", - "version": "3.2.2", + "version": "3.3.0", "description": "A config driven NodeJS framework implementing json:api", "keywords": [ "jsonapi",