Started Swagger
This commit is contained in:
parent
81e49fc35f
commit
b2a06951c7
@ -34,7 +34,7 @@ app.post("/gettoken", (req, res) => {
|
||||
app.use((req, res, next) => {
|
||||
// First check for the Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
if (!authHeader || !authHeader.startsWith("Bearer")) {
|
||||
return res.status(401).send("No token specified");
|
||||
}
|
||||
|
||||
|
||||
@ -75,9 +75,8 @@ const ca = fs.readFileSync(`${keysPath}/modernjsbook.csr`);
|
||||
const cert = fs.readFileSync(`${keysPath}/modernjsbook.crt`);
|
||||
const key = fs.readFileSync(`${keysPath}/modernjsbook.key`);
|
||||
|
||||
https.createServer({ ca, cert, key }, app).listen(8443);
|
||||
https.createServer({ ca, cert, key }, app);
|
||||
|
||||
and remove the following line if HTTPS is used
|
||||
*/
|
||||
app.listen(8080, () =>
|
||||
console.log("Routing ready at http://localhost:8080")
|
||||
|
||||
736
chapter05/package-lock.json
generated
736
chapter05/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,7 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "flow-remove-types src/ -d out/",
|
||||
"buildWithMaps":
|
||||
"flow-remove-types src/ -d out/ --pretty --sourcemaps",
|
||||
"buildWithMaps": "flow-remove-types src/ -d out/ --pretty --sourcemaps",
|
||||
"start": "npm run build && node out/doroundmath.js",
|
||||
"start-db": "npm run build && node out/dbaccess.js",
|
||||
"nodemon": "nodemon --watch src --delay 1 --exec npm start",
|
||||
@ -16,12 +15,16 @@
|
||||
"flow-coverage": "flow-coverage-report",
|
||||
"eslint": "eslint src",
|
||||
"testNoCoverage": "jest out/",
|
||||
"test": "jest out/ --coverage --no-cache"
|
||||
"test": "jest out/ --coverage --no-cache",
|
||||
"newman": "newman run postman_collection.json"
|
||||
},
|
||||
"author": "Federico Kereki",
|
||||
"license": "ISC",
|
||||
"babel": {
|
||||
"presets": ["env", "flow"]
|
||||
"presets": [
|
||||
"env",
|
||||
"flow"
|
||||
]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parserOptions": {
|
||||
@ -34,8 +37,14 @@
|
||||
"es6": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:flowtype/recommended"],
|
||||
"plugins": ["babel", "flowtype"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:flowtype/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"babel",
|
||||
"flowtype"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"no-var": "error",
|
||||
@ -43,13 +52,23 @@
|
||||
"flowtype/no-types-missing-file-annotation": 0
|
||||
}
|
||||
},
|
||||
"eslintIgnore": ["**/out/*.js"],
|
||||
"eslintIgnore": [
|
||||
"**/out/*.js"
|
||||
],
|
||||
"flow-coverage-report": {
|
||||
"concurrentFiles": 1,
|
||||
"excludeGlob": ["node_modules/**"],
|
||||
"includeGlob": ["src/**/*.js"],
|
||||
"excludeGlob": [
|
||||
"node_modules/**"
|
||||
],
|
||||
"includeGlob": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"threshold": 90,
|
||||
"type": ["text", "html", "json"]
|
||||
"type": [
|
||||
"text",
|
||||
"html",
|
||||
"json"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"tabWidth": 4,
|
||||
@ -65,6 +84,7 @@
|
||||
"mariasql": "^0.2.6",
|
||||
"morgan": "^1.9.0",
|
||||
"node-mocks-http": "^1.7.0",
|
||||
"swagger-ui-express": "^3.0.9",
|
||||
"winston": "^3.0.0-rc6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -80,6 +100,7 @@
|
||||
"flow-remove-types": "^1.2.3",
|
||||
"flow-typed": "^2.4.0",
|
||||
"jest": "^23.1.0",
|
||||
"newman": "^3.9.4",
|
||||
"nodemon": "^1.17.5",
|
||||
"prettier": "^1.13.3"
|
||||
}
|
||||
|
||||
311
chapter05/postman_collection.json
Normal file
311
chapter05/postman_collection.json
Normal file
@ -0,0 +1,311 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "056f86ef-903d-4d01-9f6c-9d83998db7c2",
|
||||
"name": "Restful server testing for regions",
|
||||
"description": "This test is for /regions ",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Test Delete",
|
||||
"description": "",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get JWT",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "f5ba5984-536c-461b-8ac9-53369abd1386",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"pm.test(\"Response is long enough\", () => ",
|
||||
" pm.expect(pm.response.text()).to.have.lengthOf.above(40)); ",
|
||||
" ",
|
||||
"pm.test(\"Response has three parts\", () => ",
|
||||
" pm.expect(pm.response.text().split(\".\")).to.have.lengthOf(3));",
|
||||
" ",
|
||||
"pm.environment.set(\"token\", responseBody); // for later scripts",
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/x-www-form-urlencoded"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "urlencoded",
|
||||
"urlencoded": [
|
||||
{
|
||||
"key": "user",
|
||||
"value": "fkereki",
|
||||
"description": "",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "password",
|
||||
"value": "modernjsbook",
|
||||
"description": "",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "localhost:8443/gettoken",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"gettoken"
|
||||
]
|
||||
},
|
||||
"description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Delete non-existing region",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "706fe7f8-7417-48a9-b03d-0677eee4aad9",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"pm.test(\"Status code is 404 baby!!\", () => ",
|
||||
" pm.response.to.have.status(404));"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"key": "Authorization",
|
||||
"value": "Bearer {{token}}"
|
||||
}
|
||||
],
|
||||
"body": {},
|
||||
"url": {
|
||||
"raw": "localhost:8443/regions/zz/99",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"regions",
|
||||
"zz",
|
||||
"99"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Test Get",
|
||||
"description": "",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get JWT",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "f5ba5984-536c-461b-8ac9-53369abd1386",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"pm.test(\"Response is long enough\", () => ",
|
||||
" pm.expect(pm.response.text()).to.have.lengthOf.above(40)); ",
|
||||
" ",
|
||||
"pm.test(\"Response has three parts\", () => ",
|
||||
" pm.expect(pm.response.text().split(\".\")).to.have.lengthOf(3));",
|
||||
" ",
|
||||
"pm.environment.set(\"token\", responseBody); // for later scripts",
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/x-www-form-urlencoded"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "urlencoded",
|
||||
"urlencoded": [
|
||||
{
|
||||
"key": "user",
|
||||
"value": "fkereki",
|
||||
"description": "",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "password",
|
||||
"value": "modernjsbook",
|
||||
"description": "",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "localhost:8443/gettoken",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"gettoken"
|
||||
]
|
||||
},
|
||||
"description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Get /regions/uy",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "4a0fe82d-a640-42c1-b451-f7e6a5e3705e",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"pm.test(\"Answer should be JSON\", () => {",
|
||||
" pm.response.to.be.success;",
|
||||
" pm.response.to.have.jsonBody(); ",
|
||||
"});",
|
||||
" ",
|
||||
"pm.test(\"Answer should have at least 19 regions\", () => {",
|
||||
" const regions = JSON.parse(pm.response.text());",
|
||||
" pm.expect(regions).to.have.lengthOf.at.least(19);",
|
||||
"});",
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Authorization",
|
||||
"value": "Bearer {{token}}"
|
||||
},
|
||||
{
|
||||
"key": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"body": {},
|
||||
"url": {
|
||||
"raw": "localhost:8443/regions/uy",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"regions",
|
||||
"uy"
|
||||
]
|
||||
},
|
||||
"description": "Attempt getting all regions of UY\n"
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Get /regions/uy/10",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "c8a43643-d266-4e1a-9576-7647a544b796",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"pm.test(\"Answer is valid, JSON\", function () {",
|
||||
" pm.response.to.be.success;",
|
||||
" pm.response.to.have.jsonBody(); ",
|
||||
" ",
|
||||
" const jsonData = pm.response.json();",
|
||||
" ",
|
||||
" pm.test(\"Answer has a single region\", ",
|
||||
" () => pm.expect(jsonData).to.have.lengthOf(1));",
|
||||
" ",
|
||||
" pm.test(\"Country code is UY\", ",
|
||||
" () => pm.expect(jsonData[0].countryCode).to.equal(\"UY\"));",
|
||||
"",
|
||||
" pm.test(\"Region code is 11\", ",
|
||||
" () => pm.expect(jsonData[0].regionCode).to.equal(\"11\"));",
|
||||
"",
|
||||
" pm.test(\"Region name is Paysandu\", ",
|
||||
" () => pm.expect(jsonData[0].regionName).to.equal(\"Paysandu\"));",
|
||||
"});",
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Authorization",
|
||||
"value": "Bearer {{token}}"
|
||||
}
|
||||
],
|
||||
"body": {},
|
||||
"url": {
|
||||
"raw": "localhost:8443/regions/uy/11",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"regions",
|
||||
"uy",
|
||||
"11"
|
||||
]
|
||||
},
|
||||
"description": "Attempt getting a single region of UY\n"
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"event": [
|
||||
{
|
||||
"listen": "prerequest",
|
||||
"script": {
|
||||
"id": "bd5384f0-4651-4701-a733-e4e2c12aba7a",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
""
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "4af046c3-048a-4a41-b070-32481a04d094",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
138
chapter05/restful.json
Normal file
138
chapter05/restful.json
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "Swagger Petstore",
|
||||
"license": {
|
||||
"name": "MIT"
|
||||
}
|
||||
},
|
||||
"host": "petstore.swagger.io",
|
||||
"basePath": "/v1",
|
||||
"schemes": ["http"],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"summary": "List all pets",
|
||||
"operationId": "listPets",
|
||||
"tags": ["pets"],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"description":
|
||||
"How many items to return at one time (max 100)",
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "An paged array of pets",
|
||||
"headers": {
|
||||
"x-next": {
|
||||
"type": "string",
|
||||
"description":
|
||||
"A link to the next page of responses"
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Pets"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create a pet",
|
||||
"operationId": "createPets",
|
||||
"tags": ["pets"],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Null response"
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/pets/{petId}": {
|
||||
"get": {
|
||||
"summary": "Info for a specific pet",
|
||||
"operationId": "showPetById",
|
||||
"tags": ["pets"],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "petId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The id of the pet to retrieve",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description":
|
||||
"Expected response to a valid request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Pets"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"Pet": {
|
||||
"required": ["id", "name"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Pets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Pet"
|
||||
}
|
||||
},
|
||||
"Error": {
|
||||
"required": ["code", "message"],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
chapter05/src/restful_db.js
Normal file
20
chapter05/src/restful_db.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* @flow */
|
||||
"use strict";
|
||||
|
||||
const mariaSQL = require("mariasql");
|
||||
const { promisify } = require("util");
|
||||
|
||||
const DB_HOST = "127.0.0.1";
|
||||
const DB_USER = "fkereki";
|
||||
const DB_PASS = "modernJS!!";
|
||||
const DB_SCHEMA = "world";
|
||||
|
||||
const getDbConnection = (host, user, password, db) => {
|
||||
const dbConn = new mariaSQL({ host, user, password, db });
|
||||
dbConn.query = promisify(dbConn.query);
|
||||
return dbConn;
|
||||
};
|
||||
|
||||
const dbConn = getDbConnection(DB_HOST, DB_USER, DB_PASS, DB_SCHEMA);
|
||||
|
||||
module.exports = dbConn;
|
||||
@ -48,10 +48,11 @@ const getRegion = async (
|
||||
`;
|
||||
}
|
||||
|
||||
res.set("Connection", "close");
|
||||
|
||||
const regions = await dbConn.query(sqlQuery);
|
||||
if (regions.length > 0 || region === null) {
|
||||
res
|
||||
.status(200)
|
||||
res.status(200)
|
||||
.set("Content-Type", "application/json")
|
||||
.send(JSON.stringify(regions));
|
||||
} else {
|
||||
@ -69,6 +70,8 @@ const deleteRegion = async (
|
||||
region: string
|
||||
) => {
|
||||
try {
|
||||
res.set("Connection", "close");
|
||||
|
||||
const sqlCities = `
|
||||
SELECT 1 FROM cities
|
||||
WHERE countryCode="${country}"
|
||||
@ -103,6 +106,8 @@ const postRegion = async (
|
||||
country: string,
|
||||
name: string
|
||||
) => {
|
||||
res.set("Connection", "close");
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).send("Missing name");
|
||||
}
|
||||
@ -136,8 +141,7 @@ const postRegion = async (
|
||||
|
||||
const result = await dbConn.query(sqlAddRegion);
|
||||
if (result.info.affectedRows > 0) {
|
||||
res
|
||||
.status(201)
|
||||
res.status(201)
|
||||
.header("Location", `/regions/${country}/${newId}`)
|
||||
.send("Region created");
|
||||
} else {
|
||||
@ -155,6 +159,8 @@ const putRegion = async (
|
||||
region: string,
|
||||
name: string
|
||||
) => {
|
||||
res.set("Connection", "close");
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).send("Missing name");
|
||||
}
|
||||
|
||||
125
chapter05/src/restful_server.js
Normal file
125
chapter05/src/restful_server.js
Normal file
@ -0,0 +1,125 @@
|
||||
/* @flow */
|
||||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const app = express();
|
||||
const bodyParser = require("body-parser");
|
||||
const cors = require("cors");
|
||||
const swaggerUi = require("swagger-ui-express");
|
||||
const swaggerDocument = require("../restful.json");
|
||||
|
||||
const dbConn = require("./restful_db.js");
|
||||
const validateUser = require("./validate_user.js");
|
||||
|
||||
const SECRET_JWT_KEY = "modernJSbook";
|
||||
|
||||
const https = require("https");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const keysPath = path.join(__dirname, "../../certificates");
|
||||
const ca = fs.readFileSync(`${keysPath}/modernjsbook.csr`);
|
||||
const cert = fs.readFileSync(`${keysPath}/modernjsbook.crt`);
|
||||
const key = fs.readFileSync(`${keysPath}/modernjsbook.key`);
|
||||
|
||||
https.createServer({ ca, cert, key }, app);
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));
|
||||
|
||||
app.get("/", (req, res) =>
|
||||
res
|
||||
.status(200)
|
||||
.set("Connection", "close")
|
||||
.send("Ready")
|
||||
);
|
||||
|
||||
app.post("/gettoken", (req, res) => {
|
||||
validateUser(req.body.user, req.body.password, (idErr, userid) => {
|
||||
res.set("Connection", "close");
|
||||
|
||||
if (idErr !== null) {
|
||||
res.status(401).send(idErr);
|
||||
} else {
|
||||
jwt.sign(
|
||||
{ userid },
|
||||
SECRET_JWT_KEY,
|
||||
{ algorithm: "HS256", expiresIn: "1h" },
|
||||
(err, token) => res.status(200).send(token)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.set("Connection", "close");
|
||||
|
||||
// First check for the Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith("Bearer")) {
|
||||
return res.status(401).send("No token specified");
|
||||
}
|
||||
|
||||
// Now validate the token itself
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
jwt.verify(token, SECRET_JWT_KEY, (err, decoded) => {
|
||||
if (err) {
|
||||
// Token bad formed, or expired, or other problem
|
||||
return res.status(403).send("Token expired or not valid");
|
||||
} else {
|
||||
// Token OK; get the user id from it
|
||||
req.userid = decoded.userid;
|
||||
// Keep processing the request
|
||||
next();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const {
|
||||
getRegion,
|
||||
deleteRegion,
|
||||
postRegion,
|
||||
putRegion
|
||||
} = require("./restful_regions.js");
|
||||
|
||||
app.get("/regions/", (req, res) => getRegion(res, dbConn));
|
||||
|
||||
app.get("/regions/:country/", (req, res) =>
|
||||
getRegion(res, dbConn, req.params.country)
|
||||
);
|
||||
|
||||
app.get("/regions/:country/:region/", (req, res) =>
|
||||
getRegion(res, dbConn, req.params.country, req.params.region)
|
||||
);
|
||||
|
||||
app.delete("/regions/:country/:region", (req, res) =>
|
||||
deleteRegion(res, dbConn, req.params.country, req.params.region)
|
||||
);
|
||||
|
||||
app.post("/regions/:country", (req, res) =>
|
||||
postRegion(res, dbConn, req.params.country, req.body.name)
|
||||
);
|
||||
|
||||
app.put("/regions/:country/:region", (req, res) =>
|
||||
putRegion(
|
||||
res,
|
||||
dbConn,
|
||||
req.params.country,
|
||||
req.params.region,
|
||||
req.body.name
|
||||
)
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
app.use((err, req, res, next) => {
|
||||
console.error("Error....", err.message);
|
||||
res.status(500).send("INTERNAL SERVER ERROR");
|
||||
});
|
||||
|
||||
app.listen(8443, () =>
|
||||
console.log("Server ready at http://localhost:8443")
|
||||
);
|
||||
212
chapter05/swagger.version.3.yaml
Normal file
212
chapter05/swagger.version.3.yaml
Normal file
@ -0,0 +1,212 @@
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
title: World Data RESTful API
|
||||
description: "This is a RESTful API to access world data, including countries, regions, and cities."
|
||||
version: "0.0.1"
|
||||
servers:
|
||||
- url: http://127.0.0.1:8443
|
||||
|
||||
tags:
|
||||
- name: "token"
|
||||
description: "Get a JWT for authorization"
|
||||
- name: "countries"
|
||||
description: "Access the world countries"
|
||||
- name: "regions"
|
||||
description: "Access the regions of countries"
|
||||
- name: "cities"
|
||||
description: "Access the world cities"
|
||||
|
||||
paths:
|
||||
/gettoken:
|
||||
post:
|
||||
tags:
|
||||
- "token"
|
||||
summary: "Get a token to authorize future requests"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- user
|
||||
- password
|
||||
responses:
|
||||
200:
|
||||
description: A valid token to use for other requests
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
/regions:
|
||||
get:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Get all regions of all countries"
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "No token provided"
|
||||
|
||||
post:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Add a region to a given country"
|
||||
consumes:
|
||||
- "application/x-www-form-urlencoded"
|
||||
produces:
|
||||
- text/plain
|
||||
parameters:
|
||||
- in: formData
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
description: The new region's name.
|
||||
- in: header
|
||||
name: "Authorization: Bearer"
|
||||
required: true
|
||||
type: string
|
||||
description: Authorization Token
|
||||
responses:
|
||||
201:
|
||||
description: "OK, created"
|
||||
400:
|
||||
description: "Name missing, region not created"
|
||||
401:
|
||||
description: "No token provided"
|
||||
403:
|
||||
description: "Country not found, region not created"
|
||||
409:
|
||||
description: "Other failure, region not created"
|
||||
|
||||
/regions/{country}:
|
||||
get:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Get all regions of a given country"
|
||||
produces:
|
||||
- application/json
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) whose regions are required"
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
/regions/{country}/{id}:
|
||||
|
||||
get:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Get a specific region of a given country"
|
||||
produces:
|
||||
- application/json
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) of the region"
|
||||
type: string
|
||||
required: true
|
||||
- name: id
|
||||
in: path
|
||||
description: "Region (id) that is required"
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Delete a specific region of a given country"
|
||||
description: ""
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) of the region"
|
||||
type: string
|
||||
required: true
|
||||
- name: id
|
||||
in: path
|
||||
description: "Region (id) that is to be deleted"
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
204:
|
||||
description: "OK, region was deleted"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Region does not exist"
|
||||
405:
|
||||
description: "Region has cities, cannot be deleted"
|
||||
|
||||
put:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Update a specific region of a given country"
|
||||
consumes:
|
||||
- "application/x-www-form-urlencoded"
|
||||
produces:
|
||||
- text/plain
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) of the region"
|
||||
type: string
|
||||
required: true
|
||||
- name: id
|
||||
in: path
|
||||
description: "Region (id) that is to be deleted"
|
||||
type: string
|
||||
required: true
|
||||
- in: formData
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
description: The region's new name.
|
||||
responses:
|
||||
204:
|
||||
description: "OK, region was updated"
|
||||
400:
|
||||
description: "Name missing, region not updated"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
JwtAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Authorization
|
||||
|
||||
bearerAuth: # arbitrary name for the security scheme
|
||||
type: apiKey
|
||||
in: header
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
214
chapter05/swagger.yaml
Normal file
214
chapter05/swagger.yaml
Normal file
@ -0,0 +1,214 @@
|
||||
openapi: "2.0.0"
|
||||
info:
|
||||
description: "This is a RESTful API to access world data, including countries, regions, and cities."
|
||||
version: "1.0.0"
|
||||
title: "World Data API"
|
||||
|
||||
host: "localhost:8443"
|
||||
tags:
|
||||
- name: "token"
|
||||
description: "Get a JWT for authorization"
|
||||
- name: "countries"
|
||||
description: "Access the world countries"
|
||||
- name: "regions"
|
||||
description: "Access the regions of countries"
|
||||
- name: "cities"
|
||||
description: "Access the world cities"
|
||||
schemes:
|
||||
- "http"
|
||||
|
||||
paths:
|
||||
/gettoken:
|
||||
post:
|
||||
tags:
|
||||
- "token"
|
||||
summary: "Get a token to authorize future requests"
|
||||
consumes:
|
||||
- "application/x-www-form-urlencoded"
|
||||
produces:
|
||||
- text/plain
|
||||
parameters:
|
||||
- in: formData
|
||||
name: user
|
||||
required: true
|
||||
type: string
|
||||
description: A person's name.
|
||||
- in: formData
|
||||
name: password
|
||||
required: true
|
||||
type: string
|
||||
description: A person's favorite number.
|
||||
responses:
|
||||
200:
|
||||
description: A valid token to use for other requests
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
/regions:
|
||||
get:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Get all regions of all countries"
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "No token provided"
|
||||
|
||||
post:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Add a region to a given country"
|
||||
consumes:
|
||||
- "application/x-www-form-urlencoded"
|
||||
produces:
|
||||
- text/plain
|
||||
parameters:
|
||||
- in: formData
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
description: The new region's name.
|
||||
- in: header
|
||||
name: "Authorization: Bearer"
|
||||
required: true
|
||||
type: string
|
||||
description: Authorization Token
|
||||
responses:
|
||||
201:
|
||||
description: "OK, created"
|
||||
400:
|
||||
description: "Name missing, region not created"
|
||||
401:
|
||||
description: "No token provided"
|
||||
403:
|
||||
description: "Country not found, region not created"
|
||||
409:
|
||||
description: "Other failure, region not created"
|
||||
|
||||
/regions/{country}:
|
||||
get:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Get all regions of a given country"
|
||||
produces:
|
||||
- application/json
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) whose regions are required"
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
/regions/{country}/{id}:
|
||||
|
||||
get:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Get a specific region of a given country"
|
||||
produces:
|
||||
- application/json
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) of the region"
|
||||
type: string
|
||||
required: true
|
||||
- name: id
|
||||
in: path
|
||||
description: "Region (id) that is required"
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Delete a specific region of a given country"
|
||||
description: ""
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) of the region"
|
||||
type: string
|
||||
required: true
|
||||
- name: id
|
||||
in: path
|
||||
description: "Region (id) that is to be deleted"
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
204:
|
||||
description: "OK, region was deleted"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Region does not exist"
|
||||
405:
|
||||
description: "Region has cities, cannot be deleted"
|
||||
|
||||
put:
|
||||
tags:
|
||||
- "regions"
|
||||
summary: "Update a specific region of a given country"
|
||||
consumes:
|
||||
- "application/x-www-form-urlencoded"
|
||||
produces:
|
||||
- text/plain
|
||||
parameters:
|
||||
- name: country
|
||||
in: path
|
||||
description: "Country (id) of the region"
|
||||
type: string
|
||||
required: true
|
||||
- name: id
|
||||
in: path
|
||||
description: "Region (id) that is to be deleted"
|
||||
type: string
|
||||
required: true
|
||||
- in: formData
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
description: The region's new name.
|
||||
responses:
|
||||
204:
|
||||
description: "OK, region was updated"
|
||||
400:
|
||||
description: "Name missing, region not updated"
|
||||
401:
|
||||
description: "No token provided"
|
||||
404:
|
||||
description: "Country not found"
|
||||
|
||||
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
JwtAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Authorization
|
||||
|
||||
bearerAuth: # arbitrary name for the security scheme
|
||||
type: apiKey
|
||||
in: header
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
3
package-lock.json
generated
Normal file
3
package-lock.json
generated
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user