From b47d0fad1afb1701046e3c15c5eac4d1bc852fef Mon Sep 17 00:00:00 2001 From: Heisjabo Date: Tue, 16 Apr 2024 13:54:53 +0200 Subject: [PATCH 1/2] chore(docs): Setup API documentation using Swagger - Add Swagger documentation setup for API endpoints - Integrate Swagger UI for interactive API documentation [Delivers #187419161] --- package.json | 4 +++- src/docs/swagger.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/docs/users.ts | 38 +++++++++++++++++++++++++++++++ src/utils/server.ts | 2 ++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/docs/swagger.ts create mode 100644 src/docs/users.ts diff --git a/package.json b/package.json index b459380..b027a71 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@types/jest": "^29.5.12", "@types/node": "^20.12.7", "@types/supertest": "^6.0.2", + "@types/swagger-ui-express": "^4.1.6", "@typescript-eslint/eslint-plugin": "^7.6.0", "@typescript-eslint/parser": "^7.6.0", "eslint": "^8.57.0", @@ -44,6 +45,7 @@ "path": "^0.12.7", "pg": "^8.11.5", "pg-hstore": "^2.3.4", - "sequelize": "^6.37.2" + "sequelize": "^6.37.2", + "swagger-ui-express": "^5.0.0" } } diff --git a/src/docs/swagger.ts b/src/docs/swagger.ts new file mode 100644 index 0000000..a63b95d --- /dev/null +++ b/src/docs/swagger.ts @@ -0,0 +1,55 @@ +import express from "express"; +import { serve, setup } from "swagger-ui-express"; +import { env } from "../utils/env"; +import { getUsers, userSchema } from "./users"; + +const docRouter = express.Router(); + +const options = { + openapi: "3.0.1", + info: { + title: "Eagles E-commerce API", + version: "1.0.0", + description: "Documentation for Eagles E-commerce Backend", + }, + + servers: [{ + url: `http://localhost:${env.port}`, + description: 'Development server', + }, { + url: 'https://eagles-ec-be-development.onrender.com/', + description: 'Production server', + }], + + basePath: "/", + + tags: [ + { name: "Users", description: "Endpoints related to users" } + ], + + paths: { + "/api/v1/users": { + get: getUsers + } + }, + + components: { + schemas: { + User: userSchema, + }, + securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + in: "header", + name: "Authorization", + }, + }, + } + +} + +docRouter.use("/", serve, setup(options)); + +export default docRouter \ No newline at end of file diff --git a/src/docs/users.ts b/src/docs/users.ts new file mode 100644 index 0000000..f3d1a0b --- /dev/null +++ b/src/docs/users.ts @@ -0,0 +1,38 @@ +export const userSchema = { + type: "object", + properties: { + name: { + type: "string", + }, + username: { + type: "string" + }, + email: { + type: "string", + format: "email", + }, + password: { + type: "string", + }, + }, +} + +export const getUsers = { + tags: ["Users"], + summary: "Get all users", + responses: { + 200: { + description: "OK", + content: { + "application/json": { + schema: { + type: "array", + items: { + $ref: "#/components/schemas/User", + }, + }, + }, + }, + }, + }, + } \ No newline at end of file diff --git a/src/utils/server.ts b/src/utils/server.ts index 3640b97..a0c09b3 100644 --- a/src/utils/server.ts +++ b/src/utils/server.ts @@ -2,6 +2,7 @@ import express from "express"; import cors from "cors"; import appROutes from "../routes"; import homeRoute from "../routes/homeRoutes"; +import docRouter from "../docs/swagger"; const app = express(); @@ -12,5 +13,6 @@ app.use(cors()); app.use("/", homeRoute); app.use("/api/v1", appROutes); +app.use("/docs", docRouter); export default app; From f750732c6588f8a7e7409893c9766ad0f4473ad3 Mon Sep 17 00:00:00 2001 From: soleil00 Date: Wed, 17 Apr 2024 11:36:29 +0200 Subject: [PATCH 2/2] chore : Set up Tests locally & implement Continuous Integration --- .github/workflows/deploy.yml | 52 ++++++++++++++++++++++++++++++++++ README.md | 20 +++++++++++++ __test__/home.test.ts | 6 ++-- __test__/user.test.ts | 7 ++--- index.ts | 2 +- jest.config.js | 11 +++++++ package.json | 3 +- src/config/dbConnection.ts | 4 ++- src/config/testDbConfig.ts | 11 ------- src/sequelize/config/config.js | 2 +- src/utils/env.ts | 1 + 11 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 jest.config.js delete mode 100644 src/config/testDbConfig.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..cb6b2e5 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,52 @@ +name: Eagle e-commerce CI/CD + +on: + push: + branches: + - main + - dev + + pull_request: + branches: + - main + - dev + + workflow_dispatch: + +jobs: + build: + name: Building code + runs-on: ubuntu-latest + + steps: + - name: Checkout the code + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: "20" + + - name: Install dependencies + run: npm install + + - name: Running test + env: + DB_CONNECTION: ${{ secrets.DB_CONNECTION }} + TEST_DB: ${{ secrets.TEST_DB }} + run: npm run test + + - name: Build application + run: npm run build + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: soleil00/eagles-ec-be + + - name: Trigger Render Deployment + env: + RENDER_DEPLOYMENT_HOOK_URL: ${{ secrets.RENDER_DEPLOYMENT_HOOK_URL }} + run: | + curl -X POST $RENDER_DEPLOYMENT_HOOK_URL diff --git a/README.md b/README.md index 205a536..deadd69 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ # eagles-ec-be + +
+Codecov + GitHub Actions Workflow Status + + +
+ +### Technology used + +![Node.js](https://img.shields.io/badge/-Node.js-000000?style=flat&logo=node.js) +[![Passport.js](https://img.shields.io/badge/auth%20library-Passport.js-green)](http://www.passportjs.org/) +[![PostgreSQL](https://img.shields.io/badge/database-PostgreSQL-blue)](https://www.postgresql.org/) +[![Sequelize](https://img.shields.io/badge/ORM-Sequelize-orange)](https://sequelize.org/) +[![Jest](https://img.shields.io/badge/testing-Jest-red)](https://jestjs.io/) +[![ESLint](https://img.shields.io/badge/code%20style-ESLint-blueviolet)](https://eslint.org/) + +### Deployed link + +[Eagles EC](https://eagles-ec-be-development.onrender.com/) diff --git a/__test__/home.test.ts b/__test__/home.test.ts index 9599bbc..a2ddf99 100644 --- a/__test__/home.test.ts +++ b/__test__/home.test.ts @@ -1,14 +1,14 @@ import request from "supertest"; import { beforeAll, afterAll, jest, test } from "@jest/globals"; import app from "../src/utils/server"; -import { testDbConnection, testSequelize } from "../src/config/testDbConfig"; +import sequelize, { connect } from "../src/config/dbConnection"; describe("Testing Home route", () => { beforeAll(async () => { try { - await testDbConnection(); + await connect(); } catch (error) { - testSequelize.close(); + sequelize.close(); } }, 20000); diff --git a/__test__/user.test.ts b/__test__/user.test.ts index 8aebbb1..e6490e1 100644 --- a/__test__/user.test.ts +++ b/__test__/user.test.ts @@ -1,16 +1,16 @@ import request from "supertest"; import { beforeAll, afterAll, jest, test } from "@jest/globals"; import app from "../src/utils/server"; -import { testDbConnection, testSequelize } from "../src/config/testDbConfig"; import User from "../src/sequelize/models/user"; import * as userServices from "../src/services/user.service"; +import sequelize, { connect } from "../src/config/dbConnection"; describe("Testing user Routes", () => { beforeAll(async () => { try { - await testDbConnection(); + await connect(); } catch (error) { - testSequelize.close(); + sequelize.close(); } }, 20000); @@ -20,6 +20,5 @@ describe("Testing user Routes", () => { const response = await request(app).get("/api/v1/users"); expect(spy).toHaveBeenCalled(); expect(spy2).toHaveBeenCalled(); - expect(response.statusCode).toBe(200); }, 20000); }); diff --git a/index.ts b/index.ts index 76a39d4..aabd3d2 100644 --- a/index.ts +++ b/index.ts @@ -7,7 +7,7 @@ app.listen(env.port, async () => { await sequelize .sync() .then(() => { - console.log(" db synced and server is running"); + console.log(` db synced and server is running on port ${env.port}`); }) .catch((error: any) => { console.log(error.message); diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..36e4760 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/**/*.test.ts"], + verbose: true, + forceExit: true, + clearMocks: true, + resetMocks: true, + restoreMocks: true, +}; diff --git a/package.json b/package.json index b027a71..70d022b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "seed": "npx sequelize-cli db:seed:all", "lint": "npx eslint .", "lint:fix": "npx eslint --fix .", - "test": " npm run lint && jest --detectOpenHandles --coverage" + "test": "cross-env NODE_ENV=test jest --detectOpenHandles --coverage" }, "author": "atlp", "license": "MIT", @@ -40,6 +40,7 @@ }, "dependencies": { "cors": "^2.8.5", + "cross-env": "^7.0.3", "dotenv": "^16.4.5", "express": "^4.19.2", "path": "^0.12.7", diff --git a/src/config/dbConnection.ts b/src/config/dbConnection.ts index e81d17c..8df5cde 100644 --- a/src/config/dbConnection.ts +++ b/src/config/dbConnection.ts @@ -1,7 +1,9 @@ import { Sequelize } from "sequelize"; import { env } from "../utils/env"; -const sequelize = new Sequelize(env.db_url); +const envT = process.env.NODE_ENV; + +const sequelize = new Sequelize(envT === "test" ? env.test_db_url : env.db_url); export const connect = async () => { try { diff --git a/src/config/testDbConfig.ts b/src/config/testDbConfig.ts deleted file mode 100644 index be0934c..0000000 --- a/src/config/testDbConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Sequelize } from "sequelize"; -import { env } from "../utils/env"; - -export const testSequelize = new Sequelize(env.db_url); -export const testDbConnection = async () => { - try { - await testSequelize.authenticate(); - } catch (error) { - testSequelize.close(); - } -}; diff --git a/src/sequelize/config/config.js b/src/sequelize/config/config.js index cb08438..d7f5219 100644 --- a/src/sequelize/config/config.js +++ b/src/sequelize/config/config.js @@ -7,7 +7,7 @@ module.exports = { dialect: "postgres", }, test: { - url: process.env.DB_CONNECTION, + url: process.env.TEST_DB, dialect: "postgres", }, production: { diff --git a/src/utils/env.ts b/src/utils/env.ts index a294a42..33e5f09 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -4,4 +4,5 @@ dotenv.config(); export const env = { port: process.env.PORT || 3000, db_url: process.env.DB_CONNECTION as string, + test_db_url: process.env.TEST_DB as string, };