Git Product home page Git Product logo

osprey's Introduction

Osprey

NPM version NPM Downloads Build status Test coverage Greenkeeper badge

Generate API middleware from a RAML definition, which can be used locally or globally for validating API requests and responses.

Features

  • Automatic Request Validations
    • Bodies
      • Form data
      • Url Encoded bodies
      • JSON schemas
      • XML schemas
    • Headers
    • Query parameters
    • RAML 1.0 types
  • Automatic Request Parameters
    • Default Headers
    • Default Parameters
  • RAML Router
  • Integrates with Express-format middleware servers
    • Simple req/res/next middleware format that works with Connect, Express and even http
  • API documentation Currently disabled
    • Optionally mount API documentation generated from your RAML definition
  • Built-in Error Handling Middleware
    • I18n support
    • Map validation paths to readable strings (with i18n support)
  • Built-in Response Handling Coming soon
    • Validate response bodies against status code definition
    • Automatically fill default response headers
  • Authentication
    • OAuth 1.0 Coming Soon
    • OAuth 2.0
    • Basic Authentication
    • Digest Authentication
    • Custom Security Schemes
  • RAML Mock Service

Osprey is built to enforce a documentation-first approach to APIs. It achieves this by:

Server

  1. 404ing on undocumented resources
  2. Rejecting invalid requests bodies, headers and query parameters
  3. Populating default headers and query parameters
  4. Filtering undocumented headers and query parameters
  5. Validating API responses Coming soon
  6. Fill default response headers Coming soon

Security

  1. Setting up authentication endpoints and methods for you
  2. Authenticating endpoints as defined in RAML

Installation

Global

npm install osprey -g

Osprey can be used as a validation proxy with any other API server. Just install the module globally and use the CLI to set up the application endpoint(s) to proxy, as well as the RAML definition to use. Invalid API requests will be blocked before they reach your application server.

# Proxy to a running application (with optional documentation)
osprey -f api.raml -p 3000 -a localhost:8080

Options

  • -a Application endpoint address (can be fully qualified URLs) and specify multiple, comma-separated addresses
  • -f Path to the root RAML definition (E.g. /path/to/api.raml)
  • -p Port number to bind the proxy locally

Locally

npm install osprey --save

Usage

Osprey is normally used as a local node module and is compatible with any library supporting HTTP middleware, including Express and Connect. Just require the module locally and generate the middleware from a RAML definition file.

const osprey = require('osprey')
const express = require('express')
const join = require('path').join
const app = express()

const path = join(__dirname, 'assets', 'api.raml')

// Be careful, this uses all middleware functions by default. You might just
// want to use each one separately instead - `osprey.server`, etc.
osprey.loadFile(path)
  .then(function (middleware) {
    app.use(middleware)

    app.use(function (err, req, res, next) {
      // Handle errors.
    })

    app.listen(3000)
  })
   .catch(function(e) { console.error("Error: %s", e.message); });

Please note: The middleware function does not use the RAML baseUri. Make sure you mount the application under the correct path. E.g. app.use('/v1', middleware).

Server (Resource Handling)

const wap = require('webapi-parser').WebApiParser

// webapi-parser.WebApiDocument
const model = wap.raml10.parse('/some/api.raml')
const handler = osprey.server(model, options)

console.log(handler) //=> function (req, res, next) {}

console.log(handler.ramlUriParameters) //=> {} // A merged object of used URI parameters.

Undefined API requests will always be rejected with a 404.

Options

These are also passed along to osprey-method-handler).

  • cors Enable CORS by setting to true or an object from cors (default: false)
  • compression Enable response compression using compression (default: false)
  • notFoundHandler Use a 404 error in middleware to skip over invalid/undefined routes from RAML (default: true)

From Osprey Method Handler:

  • discardUnknownBodies Discard undefined request bodies (default: true)
  • discardUnknownQueryParameters Discard undefined query parameters (default: true)
  • discardUnknownHeaders Discard undefined header parameters (always includes known headers) (default: true)
  • parseBodiesOnWildcard Toggle parsing bodies on wildcard body support (default: false)
  • reviver The reviver passed to JSON.parse for JSON endpoints
  • limit The maximum bytes for XML, JSON and URL-encoded endpoints (default: '100kb')
  • parameterLimit The maximum number of URL-encoded parameters (default: 1000)
  • busboyLimits The limits for Busboy multipart form parsing

If you disable the default "not found" handler, it should be mounted later using osprey.server.notFoundHandler. For example, app.use(osprey.server.notFoundHandler).

Invalid Headers and Query Parameters

Invalid headers and query parameters are removed from the request. To read them they need to be documented in the RAML definition.

Request Bodies

Request bodies are parsed and validated for you, when you define the schema.

For application/json and application/x-www-form-urlencoded, the data will be an object under req.body. For text/xml, the body is stored as a string under req.body while the parsed XML document is under req.xml (uses LibXMLJS, not included). For multipart/form-data, you will need to attach field and file listeners to the request form (uses Busboy):

app.post('/users/{userId}', function (req, res, next) {
  req.form.on('field', function (name, value) {
    console.log(name + '=' + value)
  })

  req.form.on('file', function (name, stream, filename) {
    stream.pipe(fs.createWriteStream(join(os.tmpDir(), filename)))
  })

  req.form.on('error', next)

  req.pipe(req.form)
})

Headers, Parameters and Query Parameters

All parameters are automatically validated and parsed to the correct types according to the RAML document using webapi-parser and raml-sanitize. URL parameter validation comes with Osprey Router, available using osprey.Router.

// Similar to `express.Router`, but uses RAML paths.
const Router = require('osprey').Router
const utils = require('./utils')

// Array<webapi-parser.Parameter>
const parameters = utils.getUriParameters()

const app = new Router()

app.use(...)

app.get('/{slug}', parameters, function (req, res) {
  res.send('success')
})

module.exports = app

You can initialize a Router with ramlUriParameters. This is helpful, since every router collects an object with merged URI parameters. For example, you can combine it with the server middleware to generate a router with your RAML URI parameters:

const handler = osprey.server(model)
const router = osprey.Router({ ramlUriParameters: handler.ramlUriParameters })

// Uses an existing `userId` URI parameter, if it exists.
router.get('/{userId}', function (req, res, next) {})

Handling Errors

Osprey returns a middleware router instance, so you can mount this within any compatible application and handle errors with the framework. For example, using HTTP with finalhandler (the same module Express uses):

const http = require('http')
const osprey = require('osprey')
const finalhandler = require('finalhandler')
const join = require('path').join

osprey.loadFile(join(__dirname, 'api.raml'))
  .then(function (middleware) {
    http.createServer(function (req, res) {
      middleware(req, res, finalhandler(req, res))
    }).listen(process.env.PORT || 3000)
  })
   .catch(function(e) { console.error("Error: %s", e.message); });

Error Types

  • error.ramlAuthorization = true An unauthorized error containing an array of errors that occured is set on error.authorizationErrors
  • error.ramlValidation = true A request failed validation and an array of validation data is set on error.requestErrors (beware, different types contain different information)
  • error.ramlNotFound = true A request 404'd because it was not specified in the RAML definition for the API

Add JSON Schemas

JSON schemas can be added to the application for when external JSON references are needed. From osprey-method-handler.

osprey.addJsonSchema(schema, key)

Error Handler

Osprey comes with support for a built-in error handler middleware that formats request errors for APIs. It comes with built-in i18n with some languages already included for certain formats (help us add more!). The default fallback language is en and the default responder renders JSON, XML, HTML and plain text - all options are overridable.

const osprey = require('osprey')
const app = require('express')()

// It's best to use the default responder, but it's overridable if you need it.
app.use(osprey.errorHandler(function (req, res, errors, stack) { /* Override */ }, 'en'))

You can override the i18n messages or provide your own by passing a nested object that conforms to the following interface:

interface CustomMessages {
  [type: string]: {
    [keyword: string]: {
      [language: string]: (error: RequestError) => string
    }
  }
}

The request error interface is as follows:

interface RequestError {
  type: 'json' | 'form' | 'headers' | 'query' | 'xml' | string
  message: string /* Merged with i18n when available */
  keyword: string /* Keyword that failed validation */
  id?: string /* A unique identifier for the instance of this error */
  dataPath?: string /* Natural path to the error message (E.g. JSON Pointers when using JSON) */
  data?: any /* The data that failed validation */
  schema?: any /* The schema value that failed validation */
  detail?: string /* Additional details about this specific error instance */
  meta?: { [name: string]: string } /* Meta data from the error (XML validation provides a code, column, etc.) */
}

Want to format your own request errors? If you emit an error with a .status property of "client error" (400 - 499) and an array of requestErrors, it will automatically be rendered as the API response (using status as the response status code).

Security

// model is an instance of webapi-parser WebApiDocument
osprey.security(model, options)

Osprey accepts an options object that maps object keys to the security scheme name in the RAML definition.

OAuth 2.0

Provided by OAuth2orize and Passport.

securitySchemes:
  - oauth_2_0:
      type: OAuth 2.0
      settings:
        authorizationUri: https://example.com/oauth/authorize
        accessTokenUri: https://example.com/oauth/token
        authorizationGrants: [ code, token, owner, credentials ]
        scopes:
          - profile
          - history
          - history_lite
          - request
          - request_receipt

OAuth 2.0 can be fairly tricky to enforce on your own. With Osprey, any endpoint with securedBy will automatically be enforced.

Required Options (by grant type)

  • All

    • authenticateClient
    • exchange.refresh When refresh tokens are used
  • Code and Token

    • serializeClient
    • deserializeClient
    • authorizeClient
    • sessionKeys
    • ensureLoggedIn Has access to req.session
    • serveAuthorizationPage Has access to req.session
  • Code

    • grant.code
    • exchange.code
  • Token

    • grant.token
  • Credentials

    • exchange.credentials
  • Owner

    • exchange.owner

The authorization page must submit a POST request to the same URL with the transaction_id and scope properties set (from req.oauth2). If the dialog was denied, submit cancel=true with the POST body. If you wish to enable the ability to skip the authorization page (E.g. user already authorized or first-class client), use the immediateAuthorization option.

// model is an instance of webapi-parser WebApiDocument
osprey.security(model, {
  oauth_2_0: {
    // Optionally override `accessTokenUri` and `authorizationUri` when needed.
    // They need to match the suffix defined in the security scheme.
    accessTokenUri: '/oauth/token',
    authorizationUri: '/oauth/authorize',
    // Serialize the client object into the session.
    serializeClient: function (application, done) {
      return done(null, application.id)
    },
    // Deserialize client objects out of the session.
    deserializeClient: function (id, done) {
      Client.findById(id, function (err, client) {
        done(err, client)
      })
    },
    authorizeClient: function (clientId, redirectUri, scope, type, done) {
      Clients.findOne(clientId, function (err, client) {
        if (err) { return done(err) }
        if (!client) { return done(null, false) }
        if (!client.redirectUri != redirectUri) { return done(null, false) }
        return done(null, client, client.redirectUri)
      })
    },
    authenticateClient: function (clientId, clientSecret, done) {
      Clients.findOne({ clientId: clientId }, function (err, client) {
        if (err) { return done(err) }
        if (!client) { return done(null, false) }
        if (client.clientSecret != clientSecret) { return done(null, false) }
        return done(null, client)
      })
    },
    findUserByToken: function (token, done) {
      User.findOne({ token: token }, function (err, user) {
        if (err) { return done(err) }
        if (!user) { return done(null, false) }
        return done(null, user, { scope: 'all' })
      })
    },
    // An array of unique session keys to sign and verify cookies.
    sessionKeys: ['a', 'b', 'c', ...],
    ensureLoggedIn: function (req, res, next) {
      // For example: https://github.com/jaredhanson/connect-ensure-login
    },
    immediateAuthorization: function (client, user, scope, done) {
      return done(null, false)
    },
    serveAuthorizationPage: function (req, res) {
      res.render('dialog', {
        transactionId: req.oauth2.transactionID,
        user: req.user,
        client: req.oauth2.client
      })
    },
    grant: {
      code: function (client, redirectUri, user, ares, done) {
        AuthorizationCode.create(client.id, redirectUri, user.id, ares.scope, function (err, code) {
          if (err) { return done(err) }
          done(null, code)
        })
      },
      token: function (client, user, ares, done) {
        AccessToken.create(client, user, ares.scope, function (err, accessToken) {
          if (err) { return done(err) }
          done(null, accessToken /*, params */)
        })
      }
    },
    exchange: {
      code: function (client, code, redirectUri, done) {
        AccessToken.create(client, code, redirectUri, function (err, accessToken) {
          if (err) { return done(err) }
          done(null, accessToken /*, refreshToken, params */)
        })
      },
      credentials: function (client, scope, done) {
        AccessToken.create(client, scope, function (err, accessToken) {
          if (err) { return done(err) }
          done(null, accessToken /*, refreshToken, params */)
        })
      },
      owner: function (client, username, password, scope, done) {
        AccessToken.create(client, username, password, scope, function (err, accessToken) {
          if (err) { return done(err) }
          done(null, accessToken /*, refreshToken, params */)
        })
      },
      refresh: function (client, refreshToken, scope, done) {
        AccessToken.create(client, refreshToken, scope, function (err, accessToken) {
          if (err) { return done(err) }
          done(null, accessToken /*, refreshToken, params */)
        })
      }
    }
  }
})

Osprey will automatically block requests with invalid scopes, when defined in RAML using the inline option syntax.

/example:
  securedBy: [oauth_2_0: { scopes: [ ADMINISTRATOR ] } ]

To implement scope validation in your own application, without RAML, use osprey.security.scope('example') and users without the required scope will be rejected.

app.get('/foo/bar', osprey.security.scope('example'), function (req, res) {
  res.send('hello, world')
})

Please note: OAuth 2.0 does not (currently) take into account security scheme describedBy of specification.

OAuth 1.0

Coming soon...

Basic Authentication

Provided by Passport-HTTP.

securitySchemes:
  - basic_auth:
      type: Basic Authentication
// model is an instance of webapi-parser WebApiDocument
osprey.security(model, {
  basic_auth: {
    realm: 'Users', // Optional.
    passReqToCallback: false, // Optional. Default value: false. If true "req" is added as the first callback argument.
    validateUser: function (username, password, done) {
      User.findOne({ username: username }, function (err, user) {
        if (err) { return done(err) }
        if (!user) { return done(null, false) }
        if (!user.verifyPassword(password)) { return done(null, false) }
        return done(null, user)
      })
    }
  }
})

Digest Authentication

Provided by Passport-HTTP.

securitySchemes:
  - digest_auth:
      type: Digest Authentication
// model is an instance of webapi-parser WebApiDocument
osprey.security(model, {
  digest_auth: {
    realm: 'Users', // Optional.
    domain: 'example.com', // Optional.
    findUserByUsername: function (username, done) {
      User.findOne({ username: username }, function (err, user) {
        if (err) { return done(err) }
        if (!user) { return done(null, false) }
        return done(null, user, user.password)
      })
    }
  }
})

Custom Security Schemes

To register a custom security scheme, you can pass in your own function.

securitySchemes:
  - custom_auth:
      type: x-custom

The function must return an object with a handler and, optionally, a router. The router will be mounted immediately and the handler will be called on every secured route with the secured by options and the RAML path.

// model is an instance of webapi-parser WebApiDocument
osprey.security(model, {
  custom_auth: function (scheme, name) {
    return {
      handler: function (options, path) {
        return function (req, res, next) {
          return next()
        }
      },
      router: function (req, res, next) {
        return next()
      }
    }
  }
})

Proxy

osprey.proxy(middleware, addresses)

Pass in an Osprey middleware function with an array of addresses to proxy to and you have a fully-functioning validation and/or security proxy.

License

Apache 2.0

osprey's People

Contributors

blakeembrey avatar brevity avatar carlos avatar cmd-johnson avatar danielwpz avatar der-on avatar dploeger avatar forsakenharmony avatar greenkeeper[bot] avatar greenkeeperio-bot avatar jcenturion avatar jmesters avatar jstoiko avatar lucasfo avatar muka avatar nachoesmite avatar postatum avatar sichvoge avatar smulesoft avatar usarid 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  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

osprey's Issues

Can't use console with other middleware

Registering the console triggers express to mount the router middleware, which is occurring before any Osprey middleware is registered since it's async. The best fix for this would be to make Osprey mount "placeholder" middlewares that don't do anything until the async raml is loaded.

Mock data module roadmap?

Hi!

I was wondering if there was some code somewhere I could hack on with the beginnings of the mock data cli module for osprey 0.2.0-alpha.

I'm starting to use that app I have and could REALLY REALLY use the ability to serve the example json responses as mock data based on the baseUrl and defined routes in the raml.

If there isn't anything hacked out yet, any pointers would be most helpful.

Thanks for all of your help. I'll try not to be too impatient. :)

Not possible to install

Not possible to install via npm install osprey -g (Linux Mint 16 Petra)

../vendor/libxml/xpath.c: In function ‘xmlXPathNodeCollectAndTest’:
../vendor/libxml/xpath.c:12401:28: warning: comparison between ‘xmlElementType’ and ‘xmlXPathTypeVal’ [-Wenum-compare]
       } else if (cur->type == type) {
                            ^
../vendor/libxml/xpath.c:12402:13: warning: comparison between ‘xmlXPathTypeVal’ and ‘enum <anonymous>’ [-Wenum-compare]
    if (type == XML_NAMESPACE_DECL)
             ^
In file included from ../vendor/libxml/xpath.c:185:0:
../vendor/libxml/xpath.c: At top level:
../vendor/libxml/trionan.c:218:1: warning: ‘trio_is_negative’ defined but not used [-Wunused-function]
 trio_is_negative
 ^
  CC(target) Release/obj.target/libxml/vendor/libxml/xpointer.o
../vendor/libxml/xpointer.c: In function ‘xmlXPtrNewRangeNodePoint’:
../vendor/libxml/xpointer.c:451:21: warning: comparison between ‘xmlElementType’ and ‘enum <anonymous>’ [-Wenum-compare]
     if (start->type != XPATH_POINT)
                     ^
  AR(target) Release/obj.target/vendor/libxml/xml.a
  COPY Release/xml.a
  CXX(target) Release/obj.target/xmljs/src/libxmljs.o
make: g++: Command not found
make: *** [Release/obj.target/xmljs/src/libxmljs.o] Error 127
make: Leaving directory `/home/xxx/dev/osprey/node_modules/osprey-resources/node_modules/osprey-method-handler/node_modules/libxmljs/build'
gyp ERR! build error 
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/usr/share/node-gyp/lib/build.js:267:23)
gyp ERR! stack     at ChildProcess.EventEmitter.emit (events.js:98:17)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (child_process.js:789:12)
gyp ERR! System Linux 3.11.0-12-generic
gyp ERR! command "nodejs" "/usr/bin/node-gyp" "rebuild"
gyp ERR! cwd /home/xxx/dev/osprey/node_modules/osprey-resources/node_modules/osprey-method-handler/node_modules/libxmljs
gyp ERR! node -v v0.10.15
gyp ERR! node-gyp -v v0.10.9
gyp ERR! not ok 
npm WARN This failure might be due to the use of legacy binary "node"
npm WARN For further explanations, please read
/usr/share/doc/nodejs/README.Debian

npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /home/xxx/dev/osprey/npm-debug.log
npm ERR! not ok code 0

osprey does not work with express 4

Given the following code which integrate osprey with express4. It returns Cannot GET /api/xxx for every path.

var express = require('express');
var path = require('path');
var osprey = require('osprey');

var app = module.exports = express();

app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.compress());
app.use(express.logger('dev'));

app.set('port', process.env.PORT || 3000);

api = osprey.create('/1', app, {
  ramlFile: path.join(__dirname, 'api.raml'),
  enableValidations: false,
  logLevel: 'debug'  //  logLevel: off->No logs | info->Show Osprey modules initializations | debug->Show all
});

if (!module.parent) {
  var port = app.get('port');
  app.listen(port);
  console.log('listening on port ' + port);
}

Console doesn't load

Trying to load console with the project generated from example league.raml, I see the following content from http://localhost:3000/api/console:
{"context":"while fetching /api/","context_mark":null,"message":"cannot fetch /api/ (while fetching /api/\ncannot fetch /api/ (NetworkError: A network error occurred.), check that the server is up and that CORS is enabled)","problem_mark":null}

From the command line I see the following output:
local:~/sites/raml-workshop/osprey_test/leagues-app (master)$ node src/app.js
connect.multipart() will be removed in connect 3.0
visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives
connect.limit() will be removed in connect 3.0
Osprey: Osprey::APIConsole has been initialized successfully listening at /api/console
listening on port 3000
GET /api/console 200 19ms - 605b
GET /api/console/styles/app.css 304 9ms
GET /api/console/scripts/vendor.js 304 9ms
GET /api/console/scripts/app.js 304 6ms

/home/gimballock/sites/raml-workshop/osprey_test/leagues-app/node_modules/osprey/dist/osprey.js:78
data = data.toString().replace(/^baseUri:.*$/gmi, "baseUri: " + ap
^
TypeError: Cannot call method 'toString' of undefined
at /home/gimballock/sites/raml-workshop/osprey_test/leagues-app/node_modules/osprey/dist/osprey.js:78:25
at fs.js:207:20
at Object.oncomplete (fs.js:107:15)

Check responses for development

This will warn through logging if the response is correct. Example: if a response header is ok based on RAML or if all the required headers are being send on response.

osprey.create should take a done callback

it appears osprey.create does some async stuff and so a caller should be able to pass a done function and be called when this async process completes -- i.e. setting up routes, etc. without the callback, we have to put waits in our code

API returns 404 for everything

It doesn't matter what I do, trying any of the API calls returns 404 and even calling the api directly from the browser returns 404. I even tried your example on github and it return 404 as well... any ideas?

Should generate an express app

If I were using osprey, I would expect that it would create a express application which I could then use however I see fit. This means I could mount it to my normal application or use it as the base of the application. There are also a lot of benefits to this approach:

  • Osprey applications will be self contained and don't affect parts of the users application
  • The osprey app can be mounted any number of times on any number of routes
  • You don't need to impose parsing logic onto the users application which can actually cause security concerns
  • You can use the xml middleware in osprey without forcing it onto users (same with form encoding, json, etc.)

Basic usage:

app.use('/api', osprey.create({
  ramlFile: path.join(__dirname, '/assets/raml/api.raml')
}));

Example:

var api = osprey.create({
  ramlFile: path.join(__dirname, '/assets/raml/api.raml')
});

api.get('/resource', function(req, res) {
  //// Your business logic here!
});

app.use('/api', api);

Not overwriting resources when osprey gets created multiple times

Running my integration tests involves having to initialize osprey multiple times, but it fails to overwrite the resources on subsequent test runs (I’m not seeing the ‘RAML successfully loaded’ message and the ‘Overwritten resource’ messages anymore after the first test). This causes the tests to fail since supertest start getting mocked results. It looks like the file is being cached based on the code, so is there a way to turn off the cache or have osprey overwrite the endpoints every time?

Strict Mode

  • Turn off mocks for production
  • Turn off debug logging for production
  • Warn about not overwritten resources
  • Block unknown routes using 404

Osprey hiding app exceptions

If I throw an exception* (ie: call null.a.b) in a method of one of my controllers that gets called on a route defined inside osprey api wrapper, that exception isn't shown on the terminal and a 404 response is returned to the client.

One probable cause is that exception are not re-thrown inside .catch handler on osprey promise chain.

Nested URI parameter validation

Osprey does not provide validation for nested resources that have URI validation. For example, the below resource will validate apiVersionId if the path is /abc/123, while apiId will only validate when the path is /123.

/{apiId}:
  uriParameters:
    apiId:
      type: integer
      minimum: 1

  /{apiVersionId}:
    uriParameters:
      apiVersionId:
        type: integer
        minimum: 1

API Console broken when using !include

When using !include (relative paths, included files are in the same folder as the RAML-file), the API Console gets the paths wrong, displaying:

{"context":"while fetching /user.schema.json", [...],
 "message":"cannot fetch /user.schema.json (Not Found)" [...]

This has been working until this commit. So if I add the trailing slash in line 14 in dist/assets/console/index.html back, so that it says

    <raml-console src="apiPath/" with-root-documentation></raml-console>

then the console is working again.

To reproduce you can setup a minimal project using osprey-cli utilizing any RAML-file containing an !include directive with a relative path, like e.g.

schemas:
 - user: !include user.schema.json

Would adding the slash back break anything?

Allow using middlewares when defining a new api.

I need to define authenticated apis using passport authentication method and a custom object that handle permissions.

If I define this api example against express app, it work as well. But with the object that osprey.create returns, it always gives me 404:

var api = osprey.create('/api', app, {
  ramlFile: path.join(__dirname, '../raml_spec/api_spec.raml'),
  logLevel: app.get('env') !== 'test' ? 'debug' : 'off'
});

api.describe(function (api) {
  api.put(
    '/templates/:id',
    accountController.ensureAuthenticated, 
    permissions.can('edit'),                           
    templatesController.edit);                     
})
.then(function() {
  app.listen(app.get('port'), function() {
    debug('Express server listening on port ' + server.address().port);
  });
})

Thanks!

Raml file is served with incorrect content type

Osprey serves the raml file with
Content-type: application/octet-stream
this causes the file to be downloaded instead of being viewed in the browser.

How about using the following?
Content-type: text/yaml

raml file does not have baseUri replaced in it

the baseUri property of the raml file should be updated with the servers actual full path (protocol, host, [port,] path).

This does not happen at all for the raml served at //api.raml.
The incorrect url is substituted for the raml served at / (It always puts http://localhost:3000/). The protocol and port are ignored and probably the host as well.

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.