diff --git a/app.js b/app.js index b6355b95..2b8fbb86 100644 --- a/app.js +++ b/app.js @@ -1,11 +1,22 @@ -var app = require('express')(); - -var peliasConfig = require( 'pelias-config' ).generate(require('./schema')); +const app = require('express')(); +const swaggerJSDoc = require('swagger-jsdoc'); +const swaggerUi = require('express-swaggerize-ui'); +const peliasConfig = require( 'pelias-config' ).generate(require('./schema')); if( peliasConfig.api.accessLog ){ app.use( require( './middleware/access_log' ).createAccessLogger( peliasConfig.api.accessLog ) ); } +var swaggerSpec = swaggerJSDoc(require( './config/swagger')); + + + +app.get('/api-docs.json', function(req, res) { + res.setHeader('Content-Type', 'application/json'); + res.send(swaggerSpec); +}); + +app.use('/api-docs', swaggerUi()); /** ----------------------- pre-processing-middleware ----------------------- **/ app.use( require('./middleware/headers') ); @@ -16,10 +27,10 @@ app.use( require('./middleware/jsonp') ); /** ----------------------- routes ----------------------- **/ -var defaultRoutes = require('./routes/default'); +const defaultRoutes = require('./routes/default'); defaultRoutes.addRoutes(app); -var v1 = require('./routes/v1'); +const v1 = require('./routes/v1'); v1.addRoutes(app, peliasConfig); /** ----------------------- error middleware ----------------------- **/ @@ -27,4 +38,4 @@ v1.addRoutes(app, peliasConfig); app.use( require('./middleware/404') ); app.use( require('./middleware/500') ); -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/config/swagger.js b/config/swagger.js new file mode 100644 index 00000000..cc35e049 --- /dev/null +++ b/config/swagger.js @@ -0,0 +1,13 @@ +const options = { + 'swaggerDefinition' : { + 'info': { + 'description': 'Swagger documentation for Pelias API', + 'title': 'Pelias API', + 'version': '1.0.0' + } + }, + 'basePath': __dirname, + 'apis': ['./routes/*.js'] +}; + +module.exports = options; \ No newline at end of file diff --git a/package.json b/package.json index d8bee5cb..50301b9b 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "elasticsearch": "^15.0.0", "elasticsearch-exceptions": "0.0.4", "express": "^4.8.8", + "express-swaggerize-ui": "^1.0.2", "geojson": "^0.5.0", "geolib": "^2.0.18", "iso-639-3": "^1.0.0", @@ -64,6 +65,7 @@ "retry": "^0.12.0", "stable": "^0.1.8", "stats-lite": "^2.0.4", + "swagger-jsdoc": "^1.9.7", "through2": "^2.0.3" }, "devDependencies": { diff --git a/routes/v1.js b/routes/v1.js index 6b40f1d6..5c3625e3 100644 --- a/routes/v1.js +++ b/routes/v1.js @@ -402,21 +402,503 @@ function addRoutes(app, peliasConfig) { }; - // static data endpoints - app.get ( base, routers.index ); - app.get ( base + 'attribution', routers.attribution ); - app.get ( '/attribution', routers.attribution ); - app.get ( '/status', routers.status ); +//Models +/** + * @swagger + * definitions: + * standardPeliasReturn: + * properties: + * geocoding: + * type: object + * $ref: '#/definitions/geocodingObject' + * type: + * type: string + * features: + * type: array + * items: + * $ref: '#/definitions/featureObject' + * bbox: + * type: array + * items: number + * standardPeliasErrorReturn: + * properties: + * geocoding: + * type: object + * $ref: '#/definitions/geocodingErrorObject' + * type: + * type: string + * features: + * type: array + * items: + * $ref: '#/definitions/featureObject' + * bbox: + * type: array + * items: number + * geocodingObject: + * properties: + * version: + * type: string + * attribution: + * type: string + * query: + * type: object + * engine: + * type: object + * timestamp: + * type: string + * geocodingErrorObject: + * properties: + * version: + * type: string + * attribution: + * type: string + * query: + * type: object + * errors: + * type: array + * items: string + * timestamp: + * type: string + * featureObject: + * properties: + * type: + * type: string + * geometry: + * type: object + * properties: + * type: object + * bbox: + * type: array + * items: number + * convertReturn: + * properties: + * type: + * type: string + * geometry: + * type: object + * properties: + * type: object + * $ref: '#/definitions/convertPropertiesObject' + * bbox: + * type: array + * items: number + * convertPropertiesObject: + * properties: + * from: + * type: string + * to: + * type: string + * name: + * type: string + * convertErrorReturn: + * properties: + * errors: + * type: string +*/ + + /** + * @swagger + * /v1: + * get: + * tags: + * - v1 + * operationId: v1 + * produces: + * - application/json + * summary: Landing page + * responses: + * 200: + * description: 200 ok + * examples: + * application/json: { "markdown": "# Pelias API\n### Version: [1.0](https://github.com/venicegeo/pelias-api/releases)\n### + * [View our documentation on GitHub](https://github.com/venicegeo/pelias-documentation/blob/master/README.md)\n", "html": "

Pelias API

\n\n

Version: + * 1.0

\n\n

View our documentation + * on GitHub

" } + */ + app.get ( base, routers.index ); + /** + * @swagger + * /v1/attribution: + * get: + * tags: + * - v1 + * operationId: attribution + * produces: + * - application/json + * summary: landing page w/attribution + * responses: + * 200: + * description: 200 ok + * examples: + * application/json: { + * "markdown": "# Pelias API\n### Version: [1.0](https://github.com/venicegeo/pelias-api/releases)\n + * ### [View our documentation on GitHub](https://github.com/venicegeo/pelias-documentation/blob/master/README.md)\n + * ## Attribution\n* Geocoding by [Pelias](https://pelias.io).\n* Data from\n * [OpenStreetMap](http://www.openstreetmap.org/copyright) + * © OpenStreetMap contributors under [ODbL](http://opendatacommons.org/licenses/odbl/). Also see the [OSM Geocoding Guidelines] + * (https://wiki.osmfoundation.org/wiki/Licence/Community_Guidelines/Geocoding_-_Guideline) for acceptable use.\n + * * [OpenAddresses](http://openaddresses.io) under [various public-domain and share-alike licenses](http://results.openaddresses.io/)\n + * * [GeoNames](http://www.geonames.org/) under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/)\n * [WhosOnFirst] + * (https://www.whosonfirst.org/) under [various CC-BY or CC-0 equivalent licenses](https://whosonfirst.org/docs/licenses/)", + * "html": "

Pelias API

\n\n

Version: + * 1.0

\n\n

+ * View our documentation on GitHub

\n\n + *

Attribution

\n\n" +} + */ + app.get ( base + 'attribution', routers.attribution ); + app.get ( '/attribution', routers.attribution ); + /** + * @swagger + * /status: + * get: + * tags: + * - base + * operationId: attribution + * produces: + * - text/plain + * summary: Landing page w/attribution + * responses: + * 200: + * description: 200 ok + * examples: + * text/plain: "status: ok" + */ + app.get ( '/status', routers.status ); // backend dependent endpoints - app.get ( base + 'place', routers.place ); - app.get ( base + 'autocomplete', routers.autocomplete ); - app.get ( base + 'search', routers.search ); - app.post( base + 'search', routers.search ); - app.get ( base + 'search/structured', routers.structured ); - app.get ( base + 'reverse', routers.reverse ); - app.get ( base + 'nearby', routers.nearby ); + + /** + * @swagger + * /v1/place: + * get: + * tags: + * - v1 + * operationId: place + * produces: + * - application/json + * summary: For querying specific place ID(s) + * parameters: + * - name: ids + * description: for details on a place returned from a previous query + * in: query + * required: true + * type: array + * items: {"type":"string", "pattern":"^[A-z]*.:[A-z]*.:[0-9]*$"} + * + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + */ + app.get ( base + 'place', routers.place ); + /** + * @swagger + * /v1/autocomplete: + * get: + * tags: + * - v1 + * operationId: autocomplete + * summary: to give real-time result suggestions without having to type the whole location + * produces: + * - application/json + * parameters: + * - name: text + * description: Text query + * in: query + * required: true + * type: string + * - name: focus.point.lat + * description: Focus point latitude + * in: query + * type: number + * - name: focus.point.lon + * description: Focus point longitude + * in: query + * type: number + * - name: boundary.rect.min_lon + * description: Bounding box minimum longitude + * in: query + * type: number + * - name: boundary.rect.max_lon + * description: Bounding box maximum longitude + * in: query + * type: number + * - name: boundary.rect.min_lat + * description: Bounding box minimum latitude + * in: query + * type: number + * - name: boundary.rect.max_lat + * description: Bounding box maximum latitude + * in: query + * type: number + * - name: sources + * description: Sources + * in: query + * type: string + * enum: [openstreetmap, openaddresses, whosonfirst, geonames] + * - name: layers + * description: Layers + * in: query + * type: string + * enum: [venue, address, street, country, macroregion, region, macrocounty, county, locality, localadmin, borough, + * neighbourhood, coarse] + * - name: boundary.county + * description: Country boundary + * in: query + * type: string + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + */ + app.get ( base + 'autocomplete', routers.autocomplete ); + /** + * @swagger + * /v1/search: + * get: + * tags: + * - v1 + * operationId: search + * summary: to find a place by searching for an address or name + * produces: + * - application/json + * parameters: + * - name: text + * description: Text query + * in: query + * required: true + * type: string + * - name: size + * description: used to limit the number of results returned. + * in: query + * type: number + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + * post: + * tags: + * - v1 + * operationId: search + * summary: to find a place by searching for an address or name + * produces: + * - application/json + * parameters: + * - name: text + * description: Text query + * in: query + * required: true + * type: string + * - name: size + * description: used to limit the number of results returned. + * in: query + * type: number + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + */ + + app.get ( base + 'search', routers.search ); + app.post( base + 'search', routers.search ); + /** + * @swagger + * /v1/search/structured: + * get: + * tags: + * - v1 + * operationId: structured + * summary: to find a place with data already separated into housenumber, street, city, etc. + * produces: + * - application/json + * parameters: + * - name: text + * description: Text query + * in: query + * required: true + * type: string + * - name: venue + * description: WOF Venue + * in: query + * type: string + * - name: address + * description: can contain a full address with house number or only a street name. + * in: query + * type: string + * - name: neighbourhood + * description: vernacular geographic entities that may not necessarily be official administrative divisions but are important + * nonetheless. + * in: query + * type: string + * - name: borough + * description: mostly known in the context of New York City, even though they may exist in other cities, such as Mexico City. + * in: query + * type: string + * - name: locality + * description: equivalent to what are commonly referred to as cities. + * in: query + * type: string + * - name: county + * description: administrative divisions between localities and regions. + * in: query + * type: string + * - name: region + * description: the first-level administrative divisions within countries, analogous to states and provinces in the United States + * and Canada, respectively, though most other countries contain regions as well + * in: query + * type: string + * - name: postalcode + * description: used to aid in sorting mail with the format dictated by an administrative division + * in: query + * type: string + * - name: country + * description: highest-level administrative divisions supported in a search. In addition to full names, countries have common + * two- and three-letter abbreviations that are also supported values for the country parameter. + * in: query + * type: string + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + */ + app.get ( base + 'search/structured', routers.structured ); + /** + * @swagger + * /v1/reverse: + * get: + * tags: + * - v1 + * operationId: reverse + * summary: to find what is located at a certain coordinate location + * produces: + * - application/json + * parameters: + * - name: point.lat + * description: Latitude (decimal degrees) + * in: query + * required: true + * type: string + * - name: point.lon + * description: Longitude (decimal degrees) + * in: query + * required: true + * type: string + * - name: boundary.circle.radius + * description: Bounding circle radius + * in: query + * type: number + * - name: size + * description: used to limit the number of results returned. + * in: query + * type: number + * - name: sources + * description: one or more valid source names + * in: query + * type: string + * enum: [openstreetmap, openaddresses, whosonfirst, geonames] + * - name: layers + * description: Layers + * in: query + * type: string + * enum: [venue, address, street, country, macroregion, region, macrocounty, county, locality, localadmin, borough, + * neighbourhood, coarse] + * - name: boundary.county + * description: Country boundary + * in: query + * type: string + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + */ + app.get ( base + 'reverse', routers.reverse ); + /** + * @swagger + * /v1/nearby: + * get: + * tags: + * - v1 + * operationId: nearby + * summary: reverse geocode search including surrounding areas + * produces: + * - application/json + * parameters: + * - name: point.lat + * description: Latitude (decimal degrees) + * in: query + * required: true + * type: string + * - name: point.lon + * description: Longitude (decimal degrees) + * in: query + * required: true + * type: string + * responses: + * 200: + * description: 200 ok + * schema: + * type: object + * $ref: '#/definitions/standardPeliasReturn' + * 400: + * description: 400 bad request + * schema: + * type: object + * $ref: '#/definitions/standardPeliasErrorReturn' + * + */ + app.get ( base + 'nearby', routers.nearby ); } /**