Skip to content

Commit

Permalink
✨ Add openapi document and logs
Browse files Browse the repository at this point in the history
  • Loading branch information
johnrazeur committed Jun 13, 2019
1 parent 69af271 commit 65473d6
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 11 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ Then generate your new project:
```bash
yo rest-express-typescript myapp
```
## Very coolos features

* generate openapi documention with a simple command, and read it via swagger-ui
* Logs with morgan

## Getting Started

Expand All @@ -42,7 +46,6 @@ docker run -p 3000:3000 -v $(pwd):/usr/src/app myapp
## TODO 🚧 

* Ready to use CI/CD with Travis
* Write api doc (Swagger ?)
* Serverless export
* Add subcommand to create a new endpoint

Expand Down
32 changes: 29 additions & 3 deletions __tests__/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ describe("generator-rest-express-typescript:app", () => {
"src/services/UserService.ts",
"src/entities",
"src/entities/Project.ts",
"src/entities/User.ts"
"src/entities/User.ts",
"swaggerDef.js",
"swagger.json"
]);
});

Expand All @@ -65,19 +67,43 @@ describe("generator-rest-express-typescript:app", () => {
assert.fileContent("package.json", '"build"');
assert.fileContent("package.json", '"test"');
assert.fileContent("package.json", '"debug"');
assert.fileContent("package.json", '"swagger:generate"');
});

it("shouldn't add Dockerfile", () => {
assert.noFile("Dockerfile");
assert.noFile(".dockerignore");
});

it("should generate the same appname in every file", () => {
it("should generate variables in every file", () => {
const expectedName = "myapp";
const expectedDescription = "the description";
const expectedVersion = "1.0.0";
const nameDir = path.basename(process.cwd());
assert.fileContent("package.json", `"name": "${expectedName}"`);
assert.fileContent(
"package.json",
`"description": "${expectedDescription}"`
);
assert.fileContent("package.json", `"version": "${expectedVersion}"`);

assert.fileContent("README.md", `# ${expectedName}`);
assert.fileContent("README.md", "the description");
assert.fileContent("README.md", expectedDescription);

assert.fileContent("swagger.json", `"title": "${expectedName}"`);
assert.fileContent(
"swagger.json",
`"description": "${expectedDescription}"`
);
assert.fileContent("swagger.json", `"version": "${expectedVersion}"`);

assert.fileContent("swaggerDef.js", `title: '${expectedName}'`);
assert.fileContent(
"swaggerDef.js",
`description: '${expectedDescription}'`
);
assert.fileContent("swaggerDef.js", `version: '${expectedVersion}'`);

assert.strictEqual(nameDir, expectedName);
});
});
Expand Down
7 changes: 6 additions & 1 deletion generators/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ module.exports = class extends Generator {

this.fs.copy(src, dest, copyOpts);

const files = ["package.json", "README.md"];
const files = [
"package.json",
"README.md",
"swagger.json",
"swaggerDef.js"
];

const opts = {
name: this.name,
Expand Down
9 changes: 9 additions & 0 deletions generators/app/templates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ This project comes with these endpoints:
}
```

## Documentation

The swagger document is available at: http://localhost:3000

If you modify the endpoint documentation or add a new one, run the following command:
```bash
npm run swagger:generate
```

## Production

To build the production files:
Expand Down
10 changes: 7 additions & 3 deletions generators/app/templates/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"ts-jest": "^24.0.2",
"ts-node": "3.3.0",
"typescript": "3.3.3333",
"ts-node-dev": "^1.0.0-pre.39"
"ts-node-dev": "^1.0.0-pre.39",
"swagger-jsdoc": "^3.2.9",
"swagger-ui-express": "^4.0.6"
},
"dependencies": {
"bcryptjs": "^2.4.3",
Expand All @@ -27,15 +29,17 @@
"jsonwebtoken": "^8.5.1",
"pg": "^7.11.0",
"reflect-metadata": "^0.1.10",
"typeorm": "0.2.17"
"typeorm": "0.2.17",
"morgan": "^1.9.1"
},
"scripts": {
"start": "set debug=* && ts-node-dev --respawn --transpileOnly ./src/lib/server.ts",
"migration:run": "ts-node ./node_modules/typeorm/cli.js migration:run",
"debug": "cross-env TZ=UTC NODE_ENV=development tsnd --inspect --respawn ./src/lib/server.ts",
"pretest": "eslint src/**/*.ts",
"test": "cross-env NODE_ENV=test jest",
"build": "tsc"
"build": "tsc",
"swagger:generate": "./node_modules/swagger-jsdoc/bin/swagger-jsdoc.js -d swaggerDef.js src/controllers/*.ts"
},
"jest": {
"transform": {
Expand Down
60 changes: 60 additions & 0 deletions generators/app/templates/src/controllers/AuthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ import { User } from "../entities/User";
import config from "../config/config";

export class AuthController {
/**
* @swagger
*
* /auth/login:
* post:
* summary: Login to the application
* tags:
* - auth
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* username:
* type: string
* password:
* type: string
* responses:
* 200:
* content:
* application/json:
* schema:
* type: object
* properties:
* token:
* type: string
* 401:
* description: Invalid credential
*/
public async login(req: Request, res: Response): Promise<void> {
//Check if username and password are set
let { username, password } = req.body;
Expand Down Expand Up @@ -48,6 +79,35 @@ export class AuthController {
});
};

/**
* @swagger
*
* /auth/change-password:
* post:
* summary: Change the user password
* tags:
* - auth
* security:
* BearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* oldPassword:
* type: string
* newPassword:
* type: string
* responses:
* 204:
* description: The password was changed successfully
* 400:
* description: Invalid parameter
* 401:
* description: User not found
*/
public async changePassword(req: Request, res: Response): Promise<void> {
//Get ID from JWT
const id = res.locals.jwtPayload.userId;
Expand Down
31 changes: 31 additions & 0 deletions generators/app/templates/src/controllers/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@ import { User } from "../entities/User";
import { UserService } from '../services/UserService';

class UserController {
/**
* @swagger
*
* /user:
* post:
* summary: Register
* tags:
* - user
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* username:
* type: string
* password:
* type: string
* responses:
* 201:
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* 500:
* description: Server error
*/
public async newUser(request: Request, response: Response): Promise<void> {
//Get parameters from the body
let { username, password } = request.body;
Expand Down
12 changes: 11 additions & 1 deletion generators/app/templates/src/lib/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as express from "express";
import * as bodyParser from "body-parser";
import * as morgan from "morgan";
import routes from "../routes";

class App {
Expand All @@ -11,9 +12,18 @@ class App {
}

private config(): void {
// enable logging
switch (process.env.NODE_ENV) {
case 'production':
this.app.use(morgan('combined'));
case 'development':
this.app.use(morgan('dev'));
default:
break;
}
// support application/json type post data
this.app.use(bodyParser.json());
//support application/x-www-form-urlencoded post data
// support application/x-www-form-urlencoded post data
this.app.use(bodyParser.urlencoded({ extended: false }));
this.app.use("/", routes);
}
Expand Down
5 changes: 4 additions & 1 deletion generators/app/templates/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Router } from 'express';
import { AuthRoutes } from './AuthRoutes';
import { UserRoutes } from './UserRoutes';
import * as swaggerUi from 'swagger-ui-express';
import * as swaggerDocument from './../../swagger.json';

const routes = Router();

routes.use('/auth', new AuthRoutes().getRouter());
routes.use('/user', new UserRoutes().getRouter());

routes.use('/', swaggerUi.serve);
routes.get('/', swaggerUi.setup(swaggerDocument));
export default routes;
Loading

0 comments on commit 65473d6

Please sign in to comment.