From 82d613037c3f2aac32a8e704fb266e9c940ee9c8 Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Mon, 22 Jun 2020 03:20:12 +0200 Subject: [PATCH 1/7] correct for ESM --- utilities/model-generator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/model-generator.js b/utilities/model-generator.js index e7f993ea..d637d86f 100644 --- a/utilities/model-generator.js +++ b/utilities/model-generator.js @@ -47,7 +47,7 @@ module.exports = function(mongoose, logger, config) { const ext = path.extname(file) if (ext === '.js') { const modelName = path.basename(file, '.js') - const schema = require(modelPath + '/' + modelName)(mongoose) + const schema = require(modelPath + '/' + modelName).default(mongoose) // EXPL: Add text index if enabled if (config.enableTextSearch) { From f88acc4eda73bd45e1d42443c4ac266d39b9fa00 Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Mon, 22 Jun 2020 03:24:49 +0200 Subject: [PATCH 2/7] add allowList to routeOptions --- docs/route-customization.md | 1 + utilities/rest-helper-factory.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/route-customization.md b/docs/route-customization.md index 48e1c7e1..46dc3d33 100644 --- a/docs/route-customization.md +++ b/docs/route-customization.md @@ -65,6 +65,7 @@ You can prevent CRUD endpoints from generating by setting the correct property t Property | Effect when false --- | --- +allowList | omits ``GET /path`` endpoint allowRead | omits ``GET /path`` and ``GET /path/{_id}`` endpoints allowCreate | omits ``POST /path`` endpoint allowUpdate | omits ``PUT /path/{_id}`` endpoint diff --git a/utilities/rest-helper-factory.js b/utilities/rest-helper-factory.js index 6a67f38b..804cc392 100644 --- a/utilities/rest-helper-factory.js +++ b/utilities/rest-helper-factory.js @@ -49,7 +49,9 @@ module.exports = function(logger, mongoose, server) { options = options || {} if (model.routeOptions.allowRead !== false) { - this.generateListEndpoint(server, model, options, Log) + if (model.routeOptions.allowList !== false) { + this.generateListEndpoint(server, model, options, Log) + } this.generateFindEndpoint(server, model, options, Log) } From b27d5c35866f8d572e0ac438ff3b19be8894be8f Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Tue, 30 Jun 2020 12:09:12 +0200 Subject: [PATCH 3/7] Change Result logs to info --- utilities/handler-helper.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utilities/handler-helper.js b/utilities/handler-helper.js index da6ed635..ad2a9a8d 100644 --- a/utilities/handler-helper.js +++ b/utilities/handler-helper.js @@ -159,7 +159,7 @@ async function _listHandler(model, request, Log) { Log ).lean() const result = await mongooseQuery.exec() - Log.log('Result: %s', JSON.stringify(result)) + Log.info('Result: %s', JSON.stringify(result)) return result } @@ -218,7 +218,7 @@ async function _listHandler(model, request, Log) { } } - Log.log('Result: %s', JSON.stringify(result)) + Log.info('Result: %s', JSON.stringify(result)) return result }) @@ -404,7 +404,7 @@ async function _findHandler(model, _id, request, Log) { } } - Log.log('Result: %s', JSON.stringify(result)) + Log.info('Result: %s', JSON.stringify(result)) return result } else { From 4fab4964ab6742cbc0746938f91721a34ef8ca37 Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Thu, 16 Jul 2020 13:15:35 +0200 Subject: [PATCH 4/7] change loglevels --- utilities/handler-helper-factory.js | 20 ++++++++++---------- utilities/handler-helper.js | 6 +++--- utilities/rest-helper-factory.js | 22 +++++++++++----------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/utilities/handler-helper-factory.js b/utilities/handler-helper-factory.js index dd39ece3..8b7054b7 100644 --- a/utilities/handler-helper-factory.js +++ b/utilities/handler-helper-factory.js @@ -138,7 +138,7 @@ function generateListHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -167,7 +167,7 @@ function generateFindHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -200,7 +200,7 @@ function generateCreateHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -228,7 +228,7 @@ function generateUpdateHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -261,7 +261,7 @@ function generateDeleteHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -310,7 +310,7 @@ function generateAssociationAddOneHandler( return async function(request, h) { try { - Log.log( + Log.info( addMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -357,7 +357,7 @@ function generateAssociationRemoveOneHandler( return async function(request, h) { try { - Log.log( + Log.info( removeMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -402,7 +402,7 @@ function generateAssociationAddManyHandler( return async function(request, h) { try { - Log.log( + Log.info( addMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -447,7 +447,7 @@ function generateAssociationRemoveManyHandler( return async function(request, h) { try { - Log.log( + Log.info( removeMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -492,7 +492,7 @@ function generateAssociationGetAllHandler( return async function(request, h) { try { - Log.log( + Log.info( getAllMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), diff --git a/utilities/handler-helper.js b/utilities/handler-helper.js index ad2a9a8d..913754b6 100644 --- a/utilities/handler-helper.js +++ b/utilities/handler-helper.js @@ -159,7 +159,7 @@ async function _listHandler(model, request, Log) { Log ).lean() const result = await mongooseQuery.exec() - Log.info('Result: %s', JSON.stringify(result)) + Log.note('Result: %s', JSON.stringify(result)) return result } @@ -218,7 +218,7 @@ async function _listHandler(model, request, Log) { } } - Log.info('Result: %s', JSON.stringify(result)) + Log.note('Result: %s', JSON.stringify(result)) return result }) @@ -404,7 +404,7 @@ async function _findHandler(model, _id, request, Log) { } } - Log.info('Result: %s', JSON.stringify(result)) + Log.note('Result: %s', JSON.stringify(result)) return result } else { diff --git a/utilities/rest-helper-factory.js b/utilities/rest-helper-factory.js index 804cc392..325408d6 100644 --- a/utilities/rest-helper-factory.js +++ b/utilities/rest-helper-factory.js @@ -153,7 +153,7 @@ module.exports = function(logger, mongoose, server) { options = options || {} if (config.logRoutes) { - Log.note('Generating List endpoint for ' + collectionName) + Log.info('Generating List endpoint for ' + collectionName) } let resourceAliasForRoute @@ -280,7 +280,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Find endpoint for ' + collectionName) + Log.info('Generating Find endpoint for ' + collectionName) } let resourceAliasForRoute @@ -407,7 +407,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Create endpoint for ' + collectionName) + Log.info('Generating Create endpoint for ' + collectionName) } options = options || {} @@ -602,7 +602,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Delete One endpoint for ' + collectionName) + Log.info('Generating Delete One endpoint for ' + collectionName) } options = options || {} @@ -738,7 +738,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Delete Many endpoint for ' + collectionName) + Log.info('Generating Delete Many endpoint for ' + collectionName) } options = options || {} @@ -877,7 +877,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Update endpoint for ' + collectionName) + Log.info('Generating Update endpoint for ' + collectionName) } options = options || {} @@ -1052,7 +1052,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating addOne association endpoint for ' + ownerModelName + ' -> ' + @@ -1263,7 +1263,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating removeOne association endpoint for ' + ownerModelName + ' -> ' + @@ -1456,7 +1456,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating addMany association endpoint for ' + ownerModelName + ' -> ' + @@ -1674,7 +1674,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating removeMany association endpoint for ' + ownerModelName + ' -> ' + @@ -1870,7 +1870,7 @@ module.exports = function(logger, mongoose, server) { ownerModel.collectionDisplayName || ownerModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating list association endpoint for ' + ownerModelName + ' -> ' + From e025e4d39bf53ec8455a8fd33abf41cc47c8c351 Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Thu, 16 Jul 2020 13:25:24 +0200 Subject: [PATCH 5/7] change logLevel for extra-endpoints && .md --- seed/user.model.js | 2 +- .../version-2.2.x/creating-endpoints.md | 214 ++++++++++++++++++ 2 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 website/versioned_docs/version-2.2.x/creating-endpoints.md diff --git a/seed/user.model.js b/seed/user.model.js index 86e59ede..bfb31596 100644 --- a/seed/user.model.js +++ b/seed/user.model.js @@ -59,7 +59,7 @@ module.exports = function(mongoose) { const collectionName = model.collectionDisplayName || model.modelName - Log.note('Generating Password Update endpoint for ' + collectionName) + Log.info('Generating Password Update endpoint for ' + collectionName) const handler = async function(request, h) { try { diff --git a/website/versioned_docs/version-2.2.x/creating-endpoints.md b/website/versioned_docs/version-2.2.x/creating-endpoints.md new file mode 100644 index 00000000..7df73025 --- /dev/null +++ b/website/versioned_docs/version-2.2.x/creating-endpoints.md @@ -0,0 +1,214 @@ +--- +id: version-2.0.x-creating-endpoints +title: Creating Endpoints +sidebar_label: Creating Endpoints +original_id: creating-endpoints +--- + +Creating endpoints with rest-hapi can be accomplished three different ways: + +- generating endpoints based off of [model definitions](#model-endpoints) +- defining [standalone endpoints](#standalone-endpoints) +- [adding endpoints](#additional-endpoints) to a model + +## Model endpoints +Restful endpoints are automatically generated based off of any mongoose models that you add to your ``models`` directory with the file structure of ``{model name}.model.js``. These models must adhere to the following format: + +```javascript +module.exports = function (mongoose) { + let Schema = new mongoose.Schema({ + /*fill in schema fields*/ + }); + + Schema.statics = { + collectionName: /*your model name*/, + routeOptions: {} + }; + + return Schema; +}; +``` + +As a concrete example, here is a ``user`` model: + +```javascript +// models/user.model.js +module.exports = function (mongoose) { + let modelName = "user"; + let Types = mongoose.Schema.Types; + let Schema = new mongoose.Schema({ + email: { + type: Types.String, + required: true, + unique: true + }, + password: { + type: Types.String, + required: true, + exclude: true, + allowOnUpdate: false + } + }); + + Schema.statics = { + collectionName: modelName, + routeOptions: {} + }; + + return Schema; +}; +``` + +This will generate the following CRUD endpoints: + +``` +DELETE /user Delete multiple users +POST /user Create one or more new users +GET /user Get a list of users +DELETE /user/{_id} Delete a user +GET /user/{_id} Get a specific user +PUT /user/{_id} Update a user +``` + +Association endpoints can also be generated based on model definitions, see the [Associations](associations.md) section. + +> **NOTE:** If your ``models`` directory is not in your projects root directory, you will need to +> specify the path (relative to your projects root directory) by assigning the path to the +> [`config.modelPath`](configuration.md#modelpath) property and you will need to set the +> [`config.absoluteModelPath`](configuration.md#absolutemodelpath) property to ``true``. + +> **NOTE:** For standalone and additional endpoints, make sure to note the validation specifics +> mentioned [here](validation.md#using-joi-in-custom-endpoints) + +## Standalone endpoints +Standalone endpoints can be generated by adding files to your ``api`` directory. The content of these files must adhere to the following format: + +```javascript +module.exports = function (server, mongoose, logger) { + /*register hapi endpoint here*/ +}; +``` + +As a concrete example, here is a ``hello-world`` endpoint that will show in the generated swagger docs: + +```javascript +// api/hello.js +module.exports = function (server, mongoose, logger) { + server.route({ + method: 'GET', + path: '/hello-world', + config: { + handler: function(request, h) { return "Hello World" }, + tags: ['api'], + plugins: { + 'hapi-swagger': {} + } + } + }) +} +``` + +> **NOTE:** If your ``api`` directory is not in your projects root directory, you will need to specify the path (relative to your projects root directory) by assigning the path to the [`config.apiPath`](configuration.md#apipath) property and you will need to set the [`config.absoluteApiPath`](configuration.md#absoluteapipath) property to ``true``. + +## Additional endpoints +If endpoints beyond the generated CRUD endpoints are needed for a model, they can easily be added as an item in the ``routeOptions.extraEndpoints`` array. The endpoint logic should be contained within a function using the footprint: ``function (server, model, options, logger)``. For example, if we wanted to add a ``Password Update`` endpoint to the ``user`` model, it could look like this: + +```javascript +// models/user.model.js +let Joi = require('@hapi/joi') +let bcrypt = require('bcrypt') +let RestHapi = require('rest-hapi') + +module.exports = function (mongoose) { + let modelName = "user" + let Types = mongoose.Schema.Types + let Schema = new mongoose.Schema({ + email: { + type: Types.String, + required: true, + unique: true + }, + password: { + type: Types.String, + required: true, + exclude: true, + allowOnUpdate: false + } + }) + + Schema.statics = { + collectionName:modelName, + routeOptions: { + extraEndpoints: [ + // Password Update Endpoint + function (server, model, options, logger) { + const Log = logger.bind("Password Update") + let Boom = require('@hapi/boom') + + let collectionName = model.collectionDisplayName || model.modelName + + Log.info("Generating Password Update endpoint for " + collectionName) + + let handler = async function (request, h) { + try { + let hashedPassword = model.generatePasswordHash(request.payload.password) + let result = await RestHapi.update(model, request.params._id, {password: hashedPassword}, Log) + if (result) { + return h.response("Password updated.").code(200) + } + else { + throw Boom.notFound("No resource was found with that id.") + } + } catch(err) { + if (!err.isBoom) { + Log.error(err) + throw Boom.badImplementation(err) + } else { + throw err + } + } + } + + server.route({ + method: 'PUT', + path: '/user/{_id}/password', + config: { + handler: handler, + auth: null, + description: 'Update a user\'s password.', + tags: ['api', 'User', 'Password'], + validate: { + params: { + _id: Joi.objectId().required() + }, + payload: { + password: Joi.string().required() + .description('The user\'s new password') + } + }, + plugins: { + 'hapi-swagger': { + responseMessages: [ + {code: 200, message: 'Success'}, + {code: 400, message: 'Bad Request'}, + {code: 404, message: 'Not Found'}, + {code: 500, message: 'Internal Server Error'} + ] + } + } + } + }) + } + ] + }, + + generatePasswordHash: function(password) { + let salt = bcrypt.genSaltSync(10) + let hash = bcrypt.hashSync(password, salt) + return hash + } + } + + return Schema +} +``` From d748cd70824c2c772e96bec5f7de8fbeb48de3e3 Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Thu, 16 Jul 2020 13:26:14 +0200 Subject: [PATCH 6/7] remove ESM changes --- utilities/model-generator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/model-generator.js b/utilities/model-generator.js index d637d86f..e7f993ea 100644 --- a/utilities/model-generator.js +++ b/utilities/model-generator.js @@ -47,7 +47,7 @@ module.exports = function(mongoose, logger, config) { const ext = path.extname(file) if (ext === '.js') { const modelName = path.basename(file, '.js') - const schema = require(modelPath + '/' + modelName).default(mongoose) + const schema = require(modelPath + '/' + modelName)(mongoose) // EXPL: Add text index if enabled if (config.enableTextSearch) { From 6fdb815b64b8406baed5a5561001eb3050c5c90d Mon Sep 17 00:00:00 2001 From: Hugo Menzaghi Date: Thu, 16 Jul 2020 14:37:19 +0200 Subject: [PATCH 7/7] Update docs --- docs/creating-endpoints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/creating-endpoints.md b/docs/creating-endpoints.md index ac8fd36a..f1de12c7 100644 --- a/docs/creating-endpoints.md +++ b/docs/creating-endpoints.md @@ -146,7 +146,7 @@ module.exports = function (mongoose) { let collectionName = model.collectionDisplayName || model.modelName - Log.note("Generating Password Update endpoint for " + collectionName) + Log.info("Generating Password Update endpoint for " + collectionName) let handler = async function (request, h) { try {