GithubHelp home page GithubHelp logo

krakenjs / hapi-openapi Goto Github PK

View Code? Open in Web Editor NEW
209.0 23.0 74.0 347 KB

Build design-driven apis with OpenAPI (formerly swagger) 2.0 and hapi.

License: Other

JavaScript 100.00%
hapi openapi swagger api rest

hapi-openapi's Introduction

hapi-openapi

Build Status NPM version

Note: this project was renamed from 'swaggerize-hapi' to 'hapi-openapi'.

hapi-openapi is a design-driven approach to building RESTful services with OpenAPI (Swagger) and Hapi.

hapi-openapi provides the following features:

  • API schema validation.
  • Routes based on the OpenAPI document.
  • API documentation route.
  • Input validation.

Why "Design Driven"

There are already a number of modules that help build RESTful APIs for node with OpenAPI. However, these modules tend to focus on building the documentation or specification as a side effect of writing the application business logic.

hapi-openapi begins with the OpenAPI document first. This facilitates writing APIs that are easier to design, review, and test.

At runtime, hapi-openapi uses the API specification to build routes from previously defined paths. This ensures that everything specified is what is implemented.

Quick Start with a Generator

This guide will let you go from an api.json to a service project in no time flat.

First install generator-swaggerize (and yo if you haven't already):

$ npm install -g yo
$ npm install -g generator-swaggerize

Now run the generator.

$ mkdir petstore && cd $_
$ yo swaggerize

Follow the prompts (note: make sure to choose hapi as your framework choice).

You now have a working api and can use something like SwaggerHub to explore it.

Manual Usage

const Hapi = require('@hapi/hapi');
const Path = require("path");

const server = new Hapi.Server( { port: 3000 } );

async function init () {
  await server.register({
    plugin: require('hapi-openapi'),
    options: {
        api: Path.join(__dirname, './config/pets.json'),
        handlers: Path.join(__dirname, './handlers')
    }
  });
  await server.start();
  console.log( server.info.uri );
}

init();

Hapi Plugin

The plugin will be registered as openapi on server.plugins with the following exposed:

  • getApi() - the resolved Swagger document.
  • setHost(host) - a helper function for setting the host property on the api.

Configuration Options

  • api - a path to a valid OpenAPI 2.0 document, or a valid document in the form of an object.
  • deprecated docspath - the path to expose api docs for swagger-ui, etc. Defaults to /api-docs.
  • docs - an object used to configure the api docs route.
    • path - the path to expose api docs for swagger-ui, etc. Defaults to /api-docs.
    • auth - options auth config for this route.
    • stripExtensions - strip vendor extensions from docs. Defaults to true.
    • prefixBasePath - prefix path of docs with he OpenAPI document's basePath value. Defaults to true.
  • handlers - either a string directory structure for route handlers, object, or not set if using x-hapi-handler.
  • extensions - an array of file extension types to use when scanning for handlers. Defaults to ['js'].
  • vhost - optional domain string (see hapi route options).
  • cors - optional cors setting (see hapi route options).
  • outputvalidation - optional validate response data.

Mount Path

Api path values will be prefixed with the OpenAPI document's basePath value. This behavior can be negated if you set the option docs.prefixBasePath to false.

Handlers Directory

The options.handlers option specifies a directory to scan for handlers. These handlers are bound to the api paths defined in the OpenAPI document.

handlers
  |--foo
  |    |--bar.js
  |--foo.js
  |--baz.js

Will route as:

foo.js => /foo
foo/bar.js => /foo/bar
baz.js => /baz

Path Parameters

The file and directory names in the handlers directory can also represent path parameters.

For example, to represent the path /users/{id}:

handlers
  |--users
  |    |--{id}.js

This works with directory names as well:

handlers
  |--users
  |    |--{id}.js
  |    |--{id}
  |        |--foo.js

To represent /users/{id}/foo.

Handlers File

Each provided javascript file should export an object containing functions with HTTP verbs as keys.

Example:

module.exports = {
    get: function (req, h) { ... },
    put: function (req, h) { ... },
    ...
}

Optionally, pre handlers can be used by providing an array of handlers for a method:

module.exports = {
    get: [
        function p1(req, h) { ... },
        function handler(req, h) { ... }
    ],
}

Handlers Object

The directory generation will yield this object, but it can be provided directly as options.handlers.

Example:

{
    'foo': {
        'get': function (req, h) { ... },
        'bar': {
            'get': function (req, h) { ... },
            'post': function (req, h) { ... }
        }
    }
    ...
}

X-Hapi-Handler

Alternatively the API document can set x-hapi-handler attribute on each defined paths element if handlers is not defined.

Example:

"/pets/{id}": {
    "x-hapi-handler": "./routes/pets-by-id.js",
    .
    .
    .

This will construct a handlers object from the given x-hapi-handler files.

X-Hapi-Options

There is now support at the operations level for x-hapi-options which represent individual Hapi Route Optijons.

This support is limited to configuration supported by the JSON file type.

Example:

"/internal": {
  "post": {
    "x-hapi-options": {
      "isInternal": true
    }
    .
    .
    .

Authentication

Support for OpenAPI security schemes requires that relevant authentication scheme and strategy are registered before the hapi-openapi plugin. See the hapi docs for information about authentication schemes and strategies.

The name of the hapi authentication strategy is expected to match the name field of the OpenAPI security requirement object.

Example:

securityDefinitions:
  api_key:
    type: apiKey
    name: Authorization
    in: header
paths:
  '/users/':
    get:
      security:
        - api_key: []
const server = new Hapi.Server();

await server.register({ plugin: AuthTokenScheme });

server.auth.strategy('api_key', 'auth-token-scheme', {
    validateFunc: async function (token) {
      // Implement validation here, return { credentials, artifacts }.
    }
});

await server.register({
    plugin: require('hapi-openapi'),
    options: {
        api: require('./config/pets.json'),
        handlers: Path.join(__dirname, './handlers')
    }
});

X-Hapi-Auth

Alternatively it may be easier to automatically register a plugin to handle registering the necessary schemes and strategies.

x-hapi-auth-schemes

The root document can contain an x-hapi-auth-schemes object specifying different plugins responsible for registering auth schemes.

Example:

"x-hapi-auth-schemes": {
    "apiKey": "../lib/xauth-scheme.js"
}

This plugin will be passed the following options:

  • name - the auth scheme name, in this example apiKey.

x-hapi-auth-strategy

The securityDefinitions entries can contain an x-hapi-auth-strategy attribute pointing to a plugin responsible for registering auth strategies.

Example:

"securityDefinitions": {
  "api_key": {
    "x-hapi-auth-strategy": "../lib/xauth-strategy.js",
    "type": "apiKey",
    "name": "authorization",
    "in": "header"
  }
}

The plugin will be passed the following options:

  • name - the securityDefinitions entry's key. In this example, api_key. This is typically used as the strategy name.
  • scheme - the securityDefinitions type. In this example, apiKey. This should match a x-hapi-auth-scheme name.
  • where - securityDefinitions entry in attribute. This is search for the lookup value; in this example header.
  • lookup - securityDefinitions entry name attribute. Used as the name to look up against where.

The way you can make these play together is that for every type, a scheme exists that delegates some lookup or evaluation to the appropriate strategy.

Example:

//xauth-scheme.js

const register = function (server, { name  }) {
    server.auth.scheme(name /*apiKey*/, (server, /* options received from the strategy */ { validate }) => {
        return {
            authenticate: async function (request, h) {
                return h.authenticated(await validate(request));
            }
        };
    });
};

module.exports = { register, name: 'x-hapi-auth-scheme' };

and

//xauth-strategy.js

const Boom = require('@hapi/boom');

const register = function (server, { name, scheme, where, lookup }) {
    server.auth.strategy(name, /* the scheme to use this strategy with */ scheme, {
        //Define a validate function for the scheme above to receive
        validate: async function (request) {
            const token = request.headers[lookup];

            //Some arbitrary example
            if (token === '12345') {
                return { credentials: { scope: ['read'] }, artifacts: { token } };
            }

            throw Boom.unauthorized();
        }
    });
};

module.exports = { register, name: 'x-hapi-auth-strategy' };

hapi-openapi's People

Contributors

achannarasappa avatar alferpal avatar brainsiq avatar danielanfelt avatar dcharbonnier avatar dszczyt avatar edward-whittle avatar etiennerd avatar gaboesquivel avatar gabrielcsapo avatar ggrg avatar guybedo avatar jasisk avatar jdarling avatar mattisg avatar nico87lbs avatar pvenkatakrishnan avatar sergioalvz avatar sommestad avatar sprootshift avatar subeeshcbabu avatar tlivings avatar toboid avatar totherik avatar tpburch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hapi-openapi's Issues

How to enable caching?

I read the code but couldn't find a functionality for this (maybe it doesn't exist?). I'd like to set the Cache-Control header using hapijs. For example:

server.route({
    path: '/user/{id}',
    config: {
        cache: {
            expiresIn: 30 * 1000,
            privacy: 'private'
        }
    },
    // ...
});

Is there a way to customize that config.cache element in the created routes?

Route parameters not optional

In OpenAPI 2.0, route parameters are optional however swaggerize-hapi throws an error for routes defined without parameters.

OpenAPI yaml

swagger: '2.0'
info:
  version: '1.0.0'
  title: test
definitions:
  test:
    type: object
    properties:
      is_available:
        type: boolean
paths:
  /test:
    get:
      summary: test
      responses:
        default:
          description: test endpoint
          schema:
            $ref: '#/definitions/test'

Expectation
No error thrown

Result

❯ node app.js
TypeError: operation.parameters is not iterable
    at Object.makeAll (/home/ani/repositories/swaggerize-hapi/lib/validators.js:144:47)
    at Object.create (/home/ani/repositories/swaggerize-hapi/lib/routes.js:71:42)
    at <anonymous>

Array parameter in path

Hello,

I have a problem (bug?) with an array parameter in path.

My swagger spec file looks like:

/foo/{bar}:
    get:
      summary: exemple of array parameter
      parameters:
        - in: path
          name: bar
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            enum: [black, white]

If I make a request to this url;
http://localhost:3000/foo/white
Everything is all right.

But if I request:
http://localhost:3000/foo/white,black
I have a 400 bad request, invalid request params input.

If I add collectionFormat: csv, it changes nothing. And it seems that it is not take into account because if I add collectionFormat: ssv, swagger ui continue to generate the same url with comas!

Furthermore, if I remove the enum line, so my items are only of type string and do the following request:
http://localhost:3000/foo/white,black
In my handlers, if I display request.params, I obtain:
{bar: [ 'white,black' ] }
Instead of:
{bar: ['white', 'black'] }

Could you help me to find the solution?

Thank you

Ref: https://swagger.io/docs/specification/2-0/describing-parameters/#path-parameters

YAML as api SyntaxError: Invalid regular expression: missing /

Hi there,

I'm having an issue with loading my YAML document. Can you help me out here please?

Code:

    {
      plugin: swaggerize,
      options: {
        api: require('./swagger_v2.yaml'),
      },
    },

Error:

swagger_v2.yaml:9
basePath: /v2.1
          ^
SyntaxError: Invalid regular expression: missing /
    at createScript (vm.js:53:10)
    at Object.runInThisContext (vm.js:95:10)
    at Module._compile (module.js:543:28)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)

And when I put apostrophe around basePath: 'v2.1' it continues:

swagger_v2.yaml:14
  /user:
  ^
SyntaxError: Invalid regular expression: missing /

x- custom properties are not allowed at top level

Currently if you try and add x- custom properties at the top level of the document (totally valid according to the OpenAPI specification and works with other generators) you get an error when trying to load the API via the options.

ValidationError: "x-constants" is not allowed
    at Object.exports.process (/home/jdarling/uapi/server/node_modules/enjoi/node_modules/joi/lib/errors.js:140:17)
    at internals.Any.validate (/home/jdarling/uapi/server/node_modules/enjoi/node_modules/joi/lib/any.js:667:25)
    at swaggerize (/home/jdarling/uapi/server/node_modules/swaggerize-routes/lib/index.js:55:35)
    at Object.register (/home/jdarling/uapi/server/node_modules/swaggerize-hapi/lib/index.js:33:18)
    at Object.target [as register] (/home/jdarling/uapi/server/node_modules/joi/lib/types/object/index.js:77:34)
    at each (/home/jdarling/uapi/server/node_modules/hapi/lib/plugin.js:310:14)
    at iterate (/home/jdarling/uapi/server/node_modules/items/lib/index.js:36:13)
    at done (/home/jdarling/uapi/server/node_modules/items/lib/index.js:28:25)
    at Object.exports.register (/home/jdarling/uapi/server/node_modules/vision/lib/index.js:57:12)
    at Object.target [as register] (/home/jdarling/uapi/server/node_modules/joi/lib/types/object/index.js:77:34)

I've worked around this by first loading the schema by hand and then removing the top level x- definitions using the following:

const removeTopLevelX = (schema)=>{
  const reIsX = /^x-/;
  return Object.keys(schema).reduce((s, key)=>{
    if(reIsX.exec(key)){
      return s;
    }
    s[key] = schema[key];
    return s;
  }, {});
};

Then registering as:

  server.register([
      Inert,
      Vision,
      {
          register: Swaggerize,
          options: {
              api: removeTopLevelX(schema),
              handlers
          }
      }
    ], (err) => {

IMHO this shouldn't be necessary and really it means that you have to pass around the "real schema" instead of referencing it in code.

null type in json schema

type null in json schema throw an exception while its a valid type

Could not resolve type: null.

swagger-ui integration

I'm trying to find an example of swagger-ui integration with this module. I have a basic service setup, but the problem I am running into is that the swagger link returns the raw swagger definition, not the swagger-ui. Having read through the code, it appears that this would be expected behavior. Do you have any documentation or examples on how to get the swagger-ui integrated? Thank you.

Add route identifier to generated routes

I have a need to retrieve a generated route from the HAPI server and the suggested solution was to use Server.lookup(). However this doesn't work as swaggerize-hapi doesn't give the generated routes an id.

See HAPI doc's: https://github.com/hapijs/hapi/blob/master/API.md#server.lookup()

My suggestion is to define the routes using some agreed format, e.g.:

get-/resources
get-/resources/{id}
post-/resources

This could easily be added with the following code:
node_modules/swaggerize-hapi/lib/index.js: line: 52

config = {
	pre: [],
	handler: undefined,
	cors: options.cors,
	id: `${route.method}-${route.path}`
};

Thoughts?

Am happy to create a PR with some tests if people think this is a good approach.

Package fails with Hapi 15.

Just tried using the current latest version of Hapi (15.2.0) and server startup failed with the following error.

TypeError: child.schema._getLabel is not a function
    at _base (/fakeproject/node_modules/hapi/node_modules/joi/lib/object.js:157:93)
    at _validate (/fakeproject/node_modules/hapi/node_modules/joi/lib/any.js:540:37)
    at _validateWithOptions (/fakeproject/node_modules/hapi/node_modules/joi/lib/any.js:600:29)
    at root.validate (/fakeproject/node_modules/hapi/node_modules/joi/lib/index.js:105:23)
    at Object.internals.input (/fakeproject/node_modules/hapi/lib/validation.js:137:20)
    at exports.query (/fakeproject/node_modules/hapi/lib/validation.js:30:22)
    at each (/fakeproject/node_modules/hapi/lib/request.js:380:16)
    at iterate (/fakeproject/node_modules/hapi/node_modules/items/lib/index.js:36:13)
    at done (/fakeproject/node_modules/hapi/node_modules/items/lib/index.js:28:25)
    at internals.Auth._authenticate (/fakeproject/node_modules/hapi/lib/auth.js:222:16)
    at internals.Auth.authenticate (/fakeproject/node_modules/hapi/lib/auth.js:197:17)
    at each (/fakeproject/node_modules/hapi/lib/request.js:380:16)
    at iterate (/fakeproject/node_modules/hapi/node_modules/items/lib/index.js:36:13)
    at done (/fakeproject/node_modules/hapi/node_modules/items/lib/index.js:28:25)
    at internals.state (/fakeproject/node_modules/hapi/lib/route.js:362:16)
    at each (/fakeproject/node_modules/hapi/lib/request.js:380:16)

Is this project listed on the main hapi site?

I stumbled upon this looking for something else on the kraken.js github. I did not see it listen on the main hapi site with the other swagger plugins/generators. If not, should it be? Excited to try it out!

Unable to Register Hapi-OpenAPI Multiple Times

I am attempting to register hapi-openapi twice. I was able to do this with previous versions. I am now getting this error. Is it possible to do this post v17 hapi?

{ AssertionError [ERR_ASSERTION]: Plugin openapi already registered
          at internals.Server.register (/app/node_modules/hapi/lib/server.js:403:26)
          at Object.register (/app/modules/openapi2/index.js:10:22)
          at internals.Server.register (/app/node_modules/hapi/lib/server.js:419:35)
          at <anonymous>
     generatedMessage: false,
     generatedMessage: false,name: 'AssertionError [ERR_ASSERTION]',
     generatedMessage: false,code: 'ERR_ASSERTION',
     generatedMessage: false,actual: false,
     generatedMessage: false,expected: true,
     generatedMessage: false,operator: '==' }

server.js

const apiManifest = {
    server: { ... },
    register: {
        plugins: [
            { plugin: require('./modules/openapi1')  },
            { plugin: require('./modules/openapi2') },
        ]
    }
}

(async () => {
    try {
        const server = await Glue.compose(apiManifest)
        await server.start()
    } catch (err) {
        console.log(err)
    }
})();

./modules/openapi1/index.js

exports.plugin = {
    name: 'OpenAPI1',
    register: async (server, options) => {
        const swaggerizeHapi = require('hapi-openapi')
        await server.register({
            plugin: swaggerizeHapi,
            options: { ... }
        })
    }
}

./modules/openapi2/index.js

exports.plugin = {
    name: 'OpenAPI2',
    register: async (server, options) => {
        const swaggerizeHapi = require('hapi-openapi')
        await server.register({
            plugin: swaggerizeHapi,
            options: { ... }
        })
    }
}

"yo swaggerize" and "npm run regenerate" fail under Windows

When "npm yo swaggerize" is executed for HAPI under Windows 8, both initial generation and 'regenerate' fail in similar way:

npm ERR! Windows_NT 6.3.9600
npm ERR! argv "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm
\bin\npm-cli.js" "run" "regenerate"
npm ERR! node v4.2.4
npm ERR! npm v2.14.12
npm ERR! file C:\DEV\CloudFoundry tests\swaggerize\package.json
npm ERR! code EJSONPARSE

npm ERR! Failed to parse json
npm ERR! Unexpected token 'p' at 33:101
npm ERR! erize --only=handlers,models,tests --framework hapi --apiPath config\petstore.j
npm ERR! ^
npm ERR! File: C:\DEV\CloudFoundry tests\swaggerize\package.json
npm ERR! Failed to parse package.json data.
npm ERR! package.json must be actual JSON, not just JavaScript.

Manual fix was quite simple: escape "" with a second "" under "--apiPath config\petstore.json" OR make it into "/".

Probably it would be a good idea to introduce relevant fix here to either always use forward-slash or perform proper escaping.

Authentication interface missing

I was trying to implement Hapi jwt authentication into my existing swagger project and I haven't found any official way in the docs.

In each route generated, I had to add these few lines to every route

    auth: 'token',
    plugins: {
      hapiAuthorization: {
        roles: route.authorization
      }
    }

It would be so much better to provide an interface to customize the route generation! 👍
I'm quite new to these things, if there is any way I can help, tell me. :)

How do I add route tags to generated routes?

How do I add route tags to the generated routes? I'm trying to use a generator to create the swagger doc that requires tags on the routes. My json file has tags specified for each path but it doesn't seem to get translated into the handlers. If this isn't the way to create the swagger doc that goes with the generated endpoints, how is it supposed to work with this generator?

How to generate changes?

Hello!

I'm giving DDD a go but I'm not sure how you're supposed to work with changes in the swagger spec using swaggerize. The first time I generate the code using swaggerize it's superb, but how about when I later on make some changes to my swagger spec to, for example, add another parameter or response to an already existing path? Running the regenerate command will overwrite the whole files which is not what I want (since I've already added business logic), I want the code skeletons generated for the new stuff just as when I used swaggerize the first time.

Am I missing something or is the swaggerize generator only supposed to be used when creating a completely new app or adding new paths (but not when editing existing)?

Thanks

`Too many files open` error in Windows

I am using swagger-hapi(1.0.1) to serve mock files in developing environment. I abstract parts of response to separate definitions for re-usability. I have this folder structure:

|- index.yaml  // $ref to all paths.
|-paths/
| |-eachPath.yaml  // files that start with `get` property, $ref to small definitions
|-defs/
| |-eachDefs.yaml  //reusuable piece of api
|-handlers/

In windows after a certain amount of definitions and paths, when I try to start the server, not while I ping the api, I hit this error:

EMFILE: Too many files open

at random places. Once I remove certain number of files, this error is suppressed. I get this error even if I don't have a handler for that particular path. Just putting the file path in a index.yaml triggers the error. This does not happen in nix systems. I think it has something to do with a limit to number of files open, since windows doesn't have something like a ulimit. I have around 125 definitions (some of them reused) and 40 paths, so it would come around 200 file refs in total.

Any help would be wonderful. Thanks

validation problem on requests with query parameter of array type

the swagger spec allows definition of query parameters of type array but queries generated using swagger-ui are not accepted by swaggerize-hapi.

For example, i have this definition for a get request :

get:
      parameters:
        - name: fields
          in: query
          required: false
          type: array
          items:
            type: string
          enum: [date, title, description]

The server accepts requests with the "fields" parameter encoded in these formats:
fields[]=date,title,description
fields=date&fields=title&fields=description

BUT swagger-ui generates this kind of request : http://test.api.com/request?fields=date,title,description
This request is not accepted by the server and i have a 400 response :'fields must be an array'

So it seems that the implementation does not respect the swagger spec

formData validation for type 'file'

hi!

I can't validate a request with a formData parameter of type 'file'. I use the follow yaml file for the request configuration:

[...]
paths:
  '/content':
    post:
      description: insert new content
      consumes:
        - multipart/form-data
      parameters:
        - name: file
          type: file
          in: formData
          description: content stream
          required: true
        - name: filename
          type: string
          in: formData
          description: the file name
          required: true
      responses:
        '201':
          description: Insert ok
          headers:
             location:
               type: string
          schema:
              $ref: ../models/content-post-response.yaml
        default:
          description: An error occurred
          schema:
            $ref: ../models/errors.yaml

When i perform the request and I post a file the validation process fails comparing a object (the value is a Uint8Array) against a string for the 'file' parameter and swagger get out the response to hapi with errors

child "file" fails because ["value" must be a string]

Response:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "child \"file\" fails because [\"value\" must be a string]",
  "validation": {
    "source": "payload",
    "keys": [
      "file"
    ]
  }
}

Is the right manner to upload a file or maybe some bugs on validation process with file and formdata ?

thanks all for advice!

Validate responses against swagger definition

It'd be really nice if there was a way to validate the API responses against the swagger.json file to ensure that the handlers are returning the documented responses. I guess Hapi's onPreResponse extension point might be the right place to do this.

Something to consider if implementing this is that it could be undesirable for apps or endpoints where high performance is required. In this case there could be an option to disable the check completely, or do something like run the check asynchronously and just log the result for reporting.

Plugin fails with valid OpenAPI spec

Hi there,

first of all, thanks for this amazing project :)

This is more a question than an issue itself but it is not possible to make the PetStore definition to work with this plugin because POST /v2/pet has an empty description '' (which does not look to be a valid value for Hapi's route.options.description). Since the document is a valid OpenAPI spec, perhaps it might be better to skip the description property for route.options in those cases... What do you think?

Cheers,
Sergio

JWT authentication support

How do I set up JWT authentication and authorization in swaggerize-hapi (using e.g. hapi-auth-jwt2)? Does Swagger definition file provide a way to configure authentication and authorization per-route basis?

Add an .editorconfig file

A formatter could also be interesting, it's hard to edit your files if we don't know the specs for formatting the code.

Unexpected token .

I freshly generate a spec from a yaml file, which has been validated and used properly elsewhere. When generating, then running the project with npm server.js I get the following error

❯ node server.js
/project/handlers/addresses/{address_id}.js:14
    get: function api.address.get(req, res, next) {
                     ^
SyntaxError: Unexpected token .
    at createScript (vm.js:53:10)
    at Object.runInThisContext (vm.js:95:10)
    at Module._compile (module.js:543:28)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at /home/shawn/code/src/gitlab.com/forkbomb.co/forkbomb/zbudget/node_modules/swaggerize-routes/lib/readhandlers.js:30:27

That file in question is this:

'use strict';
var dataProvider = require('../../data/addresses/{address_id}.js');
/**
 * Operations on /addresses/{address_id}
 */
module.exports = {
    /**
     * summary: Get a paginated list of addresses.
     * description: This can only be done by the logged in user.
     * parameters: 
     * produces: 
     * responses: 200, 401, 404
     */
    get: function api.address.get(req, res, next) {
        /**
         * Get the data for response 200
         * For response `default` status 200 is used.
         */
        var status = 200;
        var provider = dataProvider['get']['200'];
        provider(req, res, function (err, data) {
            if (err) {
                next(err);
                return;
            }
            res.status(status).send(data && data.responses);
        });
    },
    ...
}

It appears that the method handler singatures are different than what is in the readme, which does not reference an api.* object on the get method. It is like this for all routes generated. There is no api object with the given properties. in this case, no api.address.

How to load multiple Swagger definitions on the same HAPI server

Hi. I have many different files for API definitions that sets many paths in my swagger project.
I want to load them in isolated plugins. I don't know how to do that correctly, actually I'm using a wrapper like this one:

var swaggering = {
  register: function (server, options, next) {
    return Swaggerize.register(server, options, next);
  }
};
swaggering.register.attributes = {
    name: Swaggerize.register.attributes.name+ '-' + API_NAME,
    version: Swaggerize.register.attributes.version
};

Is there any "official" way for that?
Thanks a lot!

Quick Start Fails at {petId}... _.forEach(Object.keys(properties), function (prop) {;

swaggerize-hapi klik npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (swaggerize-hapi)
version: (1.0.1)
keywords:
license: (MIT)
About to write to /Users/klik/github/swaggerize-hapi/package.json:

{
  "name": "swaggerize-hapi",
  "description": "Design-driven apis with swagger 2.0 and hapi.",
  "version": "1.0.1",
  "author": "Trevor Livingston <[email protected]>",
  "repository": {
    "type": "git",
    "url": "git://github.com/krakenjs/swaggerize-hapi.git"
  },
  "bugs": {
    "url": "http://github.com/krakenjs/swaggerize-hapi/issues"
  },
  "publishConfig": {
    "registry": "https://registry.npmjs.org"
  },
  "engines": {
    "node": "~0.10.22"
  },
  "dependencies": {
    "core-util-is": "^1.0.1",
    "hapi": "^8.0.0",
    "hoek": "^2.6.0",
    "joi": "^6.6.1",
    "js-yaml": "^3.2.6",
    "swaggerize-routes": "^1.0.3"
  },
  "devDependencies": {
    "tape": "^3.0.0",
    "jshint": "^2.4.1",
    "istanbul": "~0.2.3",
    "boom": "~2.8.0"
  },
  "scripts": {
    "test": "tape test/*.js",
    "cover": "istanbul cover tape -- test/*.js",
    "lint": "jshint -c .jshintrc lib/*.js"
  },
  "main": "./lib/index",
  "homepage": "https://github.com/krakenjs/swaggerize-hapi#readme",
  "directories": {
    "test": "test"
  },
  "license": "MIT"
}


Is this ok? (yes)
swaggerize-hapi klik npm install
[email protected] /Users/klik/github/swaggerize-hapi
├── [email protected]
├── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├─┬ [email protected]
│ │ │ ├── [email protected]
│ │ │ └─┬ [email protected]
│ │ │   └── [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ └── [email protected]
├── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ └─┬ [email protected]
│ │   └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └─┬ [email protected]
│ │   ├── [email protected]
│ │   └── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ └─┬ [email protected]
│ │   └── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├─┬ [email protected]
│ │ │ └─┬ [email protected]
│ │ │   ├── [email protected]
│ │ │   └── [email protected]
│ │ ├── [email protected]
│ │ └─┬ [email protected]
│ │   ├── [email protected]
│ │   └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └─┬ [email protected]
│ │   ├── [email protected]
│ │   └── [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
└─┬ [email protected]
  ├── [email protected]
  ├── [email protected]
  ├─┬ [email protected]
  │ └── [email protected]
  ├── [email protected]
  ├── [email protected]
  ├── [email protected]
  └── [email protected]

swaggerize-hapi klik mkdir petstore && cd $_
petstore klik yo swaggerize
Swaggerize Generator
? What would you like to call this project: petstore
? Your name: Alain
? Your github user name: kaona
? Your email: [email protected]
? Path (or URL) to swagger document: https://raw.githubusercontent.com/wordnik/s
wagger-spec/master/examples/v2.0/json/petstore.json
? Express, Hapi or Restify: Hapi
   create .jshintrc
   create .gitignore
   create .npmignore
   create server.js
   create package.json
   create README.md
   create config/petstore.json
   create handlers/pets.js
   create handlers/pets/{petId}.js
undefined:11
_.forEach(Object.keys(properties), function (prop) {;
                      ^

ReferenceError: properties is not defined
    at eval (eval at template (/usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/node_modules/lodash/dist/lodash.js:6306:22), <anonymous>:11:23)
    at underscore [as _engine] (/usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/lib/util/engines.js:32:30)
    at engine (/usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/lib/actions/actions.js:303:10)
    at template (/usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/lib/actions/actions.js:281:15)
    at /usr/local/lib/node_modules/generator-swaggerize/app/index.js:287:18
    at Array.forEach (native)
    at yeoman.generators.Base.extend.models (/usr/local/lib/node_modules/generator-swaggerize/app/index.js:276:49)
    at /usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/lib/base.js:341:43
    at /usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/node_modules/async/lib/async.js:551:21
    at /usr/local/lib/node_modules/generator-swaggerize/node_modules/yeoman-generator/node_modules/async/lib/async.js:227:13
petstore klik

New version

Hi,

The last available version (2.0.1) doesn't include the commit (6ca934f).
Could you create a new version ?

Thanks !

How to edit route's config manually?

I need to increase maxBytes of route's payload configuration, but how do I do this when I can't seem to edit route configurations after the routes are initialized by the plugin?

How to consume application/xml

Hello,
when I try to consume application/xml I only get a Unsupported Media Type
Is there a way to set onPostAuth on some routes to convert those XML into JSON to avoid that problem ?

Support empty base path

basePath is optional in OpenAPI 2.0 however swagger-hapi will prefix a / to all routes when when basePath is empty resulting an invalid Hapi route:

OpenAPI yaml

swagger: '2.0'
info:
  version: '1.0.0'
  title: test
definitions:
  test:
    type: object
    properties:
      is_available:
        type: boolean
paths:
  /test:
    get:
      summary: test
      responses:
        default:
          description: test endpoint
          schema:
            $ref: '#/definitions/test'

Expectation
No error thrown

Result

❯ node app.js
{ method: 'get',
  path: '//test',
  options: 
   { cors: true,
     id: undefined,
     description: undefined,
     tags: [ 'api' ],
     handler: [Function: get],
     validate: {} },
  vhost: undefined }
{ AssertionError [ERR_ASSERTION]: Invalid path: //test
    at exports.Router.internals.Router.internals.Router.analyze (/home/ani/repositories/test-api/node_modules/call/lib/index.js:217:10)
    at new module.exports.internals.Route (/home/ani/repositories/test-api/node_modules/hapi/lib/route.js:99:44)
    at internals.Server._addRoute (/home/ani/repositories/test-api/node_modules/hapi/lib/server.js:455:23)
    at internals.Server.route (/home/ani/repositories/test-api/node_modules/hapi/lib/server.js:448:22)
    at Object.register (/home/ani/repositories/swaggerize-hapi/lib/index.js:129:16)
    at <anonymous>
  generatedMessage: false,
  name: 'AssertionError [ERR_ASSERTION]',
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '==' }

validator

type is validated but format is not

{
          'name': 'uuid',
          'in': 'path',
          'description': 'User uuid',
          'required': true,
          'type': 'string',
          'format': '/^[0-9a-fA-F]{24}$/'
        }

does not raise error if uuid is 'XXX'

test fails

After a fresh install npm run test fails with:
(using petstore and Hapi)

/home/vagrant/bizkit/node_modules/enjoi/lib/enjoi.js:22
        if (current.type) {
                   ^

TypeError: Cannot read property 'type' of undefined
    at resolve (/home/vagrant/bizkit/node_modules/enjoi/lib/enjoi.js:22:20)
    at resolve (/home/vagrant/bizkit/node_modules/enjoi/lib/enjoi.js:39:20)

Handle multiple co-resident Swaggerized Hapi plugins

I assemble and deploy Hapi apps like I imagine @hueniverse and @unclebob would have them:

  • A plugin for each responsibility with a small, cohesive API
  • Each plugin in its own module
  • A deployment module with package.json to install the right plugins and a manifest.json to configure the Hapi pack

I'd love to take your design-driven approach and hand my teams the Swagger Editor, but swaggerize-hapi presents an obstacle. The API document has to be embedded in the configuration which, for me, is:

  • In the deployment module, not the plugin module where the code is
  • Committed and published on a different schedule than the plugin module
  • Stored in manifest.json, making dropping in a freshly edited API document harder than I'd like

I'm not sure whether this cuts across your architectural grain too much, but it strikes me you could:

  • Have Swaggerized Hapi plugins take a server.dependency on swaggerize-hapi
  • Have them register their API document via a method you server.expose, leaving it up to them how to load it from code, file, configuration, or external service
  • Merge the APIs
  • Throw an exception and crash deliberately if the APIs clash, either with each other or with your documentation route
  • Register routes with handlers loaded from the plugins' base paths as usual
  • Register the documentation route

The registration method could be as simple as:

server.plugins['swaggerize-hapi'].register(basePath, apiDocument)

I regret I might not be able to contribute code because procedures, but I'm getting good at raising specific and actionable bug reports. Would that be enough help?

Bad version of [email protected]

I created an issue here on enjoi. I was trying to use this in a project using openapi 2/3 with allOf and noticed that it wasn't validating correctly. Appears that [email protected] has a different (presumably old??) version of its library - not the one in its master branch.

`required` property is ignored

During the validation process, if a parameter is specified in the Swagger document as required, a request passes validation even if it does not provide said parameter.

I think the issue is tlivings/enjoi#3 (and I open the issue here both for other users who would waste the same amount of hours as me tracing the errors and for maintainers to remember to publish updates to NPM once the dependency is fixed :) ).

Small change to readme

Now that generator-swaggerize uses hapi-openapi right from the start, this line in readme is no longer needed:

"Note: After running the generator you will need to manually switch from swaggerize-hapi to hapi-openapi."

date-time validated against timestamp rather than ISO8601

I'm trying to post a date-time data type that looks like this: "2018-10-22T00:00:00-0500" which fails validation with message ""ends" must be a valid timestamp or number of milliseconds". According to OpenAPI's specs I think this is what a date-time should look like (https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14), rather than a timestamp.

Not sure if you guys had to kind of re-write the project due to the hapi 17 breaking changes and you're aware of this issue and getting around to it or if it has existed for a while (maybe It's even not a real issue). I'd be open to contributing if this project needs some dev power.

CORS

I need to enable CORS to serve my Swagger API. I don't see how I can do this with swaggerize-hapi at the moment.

Since this seems to me to be a fairly common use-case for a webservice library, I'd like to discuss the different implementation options before opening a PR.

I see two independent choices in the implementation.

Activation: specific or generic

How will the user activate CORS in swaggerize-hapi? I see two options:

  1. Add a generic “pass this to Hapi” config option. Less obvious for what is probably a common use-case, but more future-proof and possibly solving other issues that may arise. This options object would be merged with the generated route definition, probably here.
  2. Add a “cors” config option that is manually added to the route definition. Easier to document.

Default value

  1. true: enable CORS by default for Swagger routes, considering this is a webservice library.
  2. false: let the user opt-in. Keeps current behaviour, but means the user has to understand why the thing he made does not work, and to learn about CORS.

I'm in favour of generic + default to true, as that's the combination that puts the less burden on both user and maintainer.

Utilize x-authorize properly

Take some of the work away by auto configuring auth similar to swaggerize-express and utilizing the x-authorize handler supported by swaggerize-routes.

Get rid of trailing slashes in routes

It seems that currently the API docs are always served from a path with trailing slash. For example, if I have a basepath '/api', and I want API docs to be shown at the basepath (i.e. docspath: '/'), I can access API docs only from '/api/', but not from '/api'. I think this is a bug that needs fixing. I recommend adding a path normalization function; for example like this: nakamorichi/swaggerize-routes@be0fbb4, which can then be used like this: nakamorichi@9e1f97d.

The industry seems to be moving towards no-trailing-slash URLs, so I think it is appropriate to make sure that no generated routes (except of course '/') have trailing slash. Because a URI with a trailing slash does not denote the same resource as an URI without a trailing slash, allowing developers to mix these two schemes creates unnecessary confusion and adds complexity. Therefore, I propose that swaggerize-hapi enforces a policy of no-trailing-slash in routes.

allow for api to be an object

Forcing the api to be a file path is inconvenient, I personally split my api to different files and dynamically generate an api, this force me to write a temporary file.
Please allow for objects or string

Adding a header parameter disallows other unspecified headers

Added a header parameter to one of the paths defined in our API spec:

{
  "name": "accept-language",
  "in": "header",
  "type": "string",
  "description": "Preferred language of the user",
  "enum": [
    "en",
    "sv"
  ]
}

When doing so, the validation from swaggerize-hapi automatically disallows the use of any other headers. I.e. if the client provides an Accept or Connection header, the entire request will fail validation and just return an error to the client.

Is this by design? Should the underlying validation schema take care of this somehow?


Also: Thanks for an awesome library. 👏

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.