Skip to content

Commit

Permalink
chore: add ci/cd for tests on different packages
Browse files Browse the repository at this point in the history
  • Loading branch information
brunotot committed Aug 30, 2024
1 parent b4d9df8 commit 9de241e
Show file tree
Hide file tree
Showing 28 changed files with 292 additions and 450 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"ignorePatterns": ["node_modules/", "dist/", "logger.js", "docs/", "npx", "assets"],
"ignorePatterns": ["node_modules/", "dist/", "docs/", "assets/"],
"rules": {
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/ban-ts-comment": "off"
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: MERN Sample App - Backend Tests

on:
push:
paths:
- "packages/backend/**"
branches:
- main
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: 🛒 Checkout Repository
uses: actions/checkout@v4

- name: 🤳 Install
uses: ./.github/composite-actions/install

- name: 🚀 Run Backend Tests
run: pnpm run backend:test
23 changes: 23 additions & 0 deletions .github/workflows/test-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: MERN Sample App - Frontend Tests

on:
push:
paths:
- "packages/frontend/**"
branches:
- main
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: 🛒 Checkout Repository
uses: actions/checkout@v4

- name: 🤳 Install
uses: ./.github/composite-actions/install

- name: 🚀 Run Frontend Tests
run: pnpm run frontend:test
23 changes: 23 additions & 0 deletions .github/workflows/test-shared.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: MERN Sample App - Shared Tests

on:
push:
paths:
- "packages/shared/**"
branches:
- main
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: 🛒 Checkout Repository
uses: actions/checkout@v4

- name: 🤳 Install
uses: ./.github/composite-actions/install

- name: 🚀 Run Shared Tests
run: pnpm run shared:test
19 changes: 3 additions & 16 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
{
"version": "2.0.0",
"configurations": [
{
"name": "root > install",
"runtimeArgs": ["install"],
"presentation": { "group": "1" },
// Defaults below
"runtimeVersion": "21.7.0",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"console": "integratedTerminal",
"outputCapture": "std",
"autoAttachChildProcesses": false
},
{
"name": "root > lint",
"runtimeArgs": ["lint"],
Expand Down Expand Up @@ -215,7 +202,7 @@
"autoAttachChildProcesses": false
},
{
"name": "shared > clean",
"name": "@org/shared: clean",
"runtimeArgs": ["run", "--filter", "shared", "clean"],
"presentation": { "group": "5" },
// Defaults below
Expand All @@ -228,7 +215,7 @@
"autoAttachChildProcesses": false
},
{
"name": "shared > build",
"name": "@org/shared: build",
"runtimeArgs": ["run", "shared:build"],
"presentation": { "group": "5" },
// Defaults below
Expand All @@ -241,7 +228,7 @@
"autoAttachChildProcesses": false
},
{
"name": "shared > test",
"name": "@org/shared: test",
"runtimeArgs": ["run", "--filter", "shared", "test"],
"presentation": { "group": "5" },
// Defaults below
Expand Down
43 changes: 19 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"name": "mern-monorepo-starter",
"version": "0.0.1",
"description": "mern-monorepo-starter description",
"author": "Bruno Tot",
"license": "MIT",
"main": "index.js",
"type": "module",
"private": true,
Expand All @@ -18,29 +20,32 @@
"build": "pnpm run -r build",
"typedoc": "npx typedoc && npm run script:customizeTypedocOutput",
"lint": "npx eslint . --fix",
"backend:build": "pnpm --filter backend run build",
"backend:start": "npm run start --prefix packages/backend",
"shared:build": "npm run build --prefix packages/shared",
"script:customizeTypedocOutput": "bash assets/sh/customizeTypedocOutput.sh",
"script:writeReadmeMarkdown": "node assets/js/writeReadmeMarkdown.js",
"script:printRepoTreeStructure": "bash assets/sh/printRepoTreeStructure.sh"
"script:printRepoTreeStructure": "bash assets/sh/printRepoTreeStructure.sh",
"backend:build": "npm run build --prefix packages/backend",
"backend:test": "npm run test --prefix packages/backend",
"backend:start": "npm run start --prefix packages/backend",
"frontend:build": "npm run build --prefix packages/frontend",
"frontend:test": "npm run test --prefix packages/frontend",
"frontend:start": "npm run start --prefix packages/frontend",
"shared:build": "npm run build --prefix packages/shared",
"shared:test": "npm run test --prefix packages/shared"
},
"author": "Bruno Tot",
"license": "MIT",
"devDependencies": {
"@types/express-session": "^1.18.0",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"eslint": "^8.56.0",
"@types/jest": "^29.5.12",
"cross-dirname": "^0.1.0",
"markdown-toc": "^1.2.0",
"prettier": "^3.2.5",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.8",
"typedoc": "^0.26.6",
"typedoc-github-theme": "^0.1.2",
"typedoc-material-theme": "^1.0.2",
"typedoc-plugin-custom-tags": "^1.0.2",
"typedoc-plugin-merge-modules": "^6.0.0",
"typedoc-plugin-remove-references": "^0.0.6",
"typedoc-theme-hierarchy": "^4.0.0"
"typedoc-plugin-remove-references": "^0.0.6"
},
"keywords": [
"monorepo",
Expand All @@ -52,15 +57,5 @@
"mongodb",
"express",
"node"
],
"dependencies": {
"axios": "^1.6.8",
"express-session": "^1.18.0",
"glob": "^11.0.0",
"helmet": "^7.1.0",
"helmet-csp": "^4.0.0",
"keycloak-connect": "^25.0.2",
"memorystore": "^1.6.7",
"react-use": "^17.5.0"
}
]
}
8 changes: 5 additions & 3 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"author": "Bruno Tot",
"license": "MIT",
"dependencies": {
"keycloak-connect": "^25.0.2",
"axios": "^1.6.8",
"@org/shared": "workspace:*",
"@ts-rest/core": "^3.45.0",
"@ts-rest/express": "^3.45.0",
Expand All @@ -29,7 +31,6 @@
"dotenv": "^16.4.5",
"express": "^4.18.2",
"flatted": "^3.3.1",
"glob": "^11.0.0",
"helmet": "^7.1.0",
"hpp": "^0.2.3",
"jsonwebtoken": "^9.0.2",
Expand All @@ -40,9 +41,11 @@
"winston": "^3.11.0",
"winston-daily-rotate-file": "^5.0.0",
"zod": "^3.22.5",
"express-session": "^1.18.0"
"express-session": "^1.18.0",
"memorystore": "^1.6.7"
},
"devDependencies": {
"@types/express-session": "^1.18.0",
"@babel/preset-env": "^7.24.5",
"@types/bcrypt": "^5.0.2",
"@types/body-parser": "^1.19.5",
Expand All @@ -64,7 +67,6 @@
"nodemon": "^3.1.4",
"supertest": "^7.0.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsconfig-paths-jest": "^0.0.1",
"tsx": "^4.9.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/src/ExpressApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import { buildMongoClient } from "./config/MongoDB.config";

function getModules() {
return {
KeycloakAuthorization,
Authorization: KeycloakAuthorization,
UserController,
UserRepository,
ErrorLogRepository,
UserService,
KeycloakRepository,
AuthorizationRepository: KeycloakRepository,
} as const satisfies Record<string, new () => TODO>;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/decorators/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function contract<const Route extends AppRoute, This, Fn extends RouteHan
await iocRegistry
.inject<ErrorLogRepository>(ErrorLogRepository.name)
.insertOne(typedError.content);
return { status: 500, body: typedError.content };
return { status: typedError.content.status, body: typedError.content };
} finally {
session.endSession();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

import { type RouteMiddlewareFactory } from "@org/backend/config/Route.config";
import { iocRegistry } from "@org/backend/setup/registry.setup";
import { KeycloakAuthorization } from "@org/backend/infrastructure/security/KeycloakAuthorization";
import { type Authorization } from "@org/backend/interface/Authorization";

export const withKeycloak: RouteMiddlewareFactory = () => {
return iocRegistry.inject<Authorization>(KeycloakAuthorization.name).middleware();
return iocRegistry.inject<Authorization>("Authorization").middleware();
};
12 changes: 7 additions & 5 deletions packages/backend/src/infrastructure/middleware/withSecured.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import type { NextFunction, Request, RequestHandler, Response } from "express";
import { ErrorResponse } from "@org/shared";
import jwt from "jsonwebtoken";
import { iocRegistry } from "@org/backend/setup/registry.setup";
import { KeycloakRepository } from "../repository/impl/KeycloakRepository";
import { KeycloakAuthorization } from "@org/backend/infrastructure/security/KeycloakAuthorization";
import { getTypedError } from "@org/shared";
import { type Authorization } from "@org/backend/interface/Authorization";
import { type AuthorizationRepository } from "@org/backend/interface/AuthorizationRepository";
import { env } from "@org/backend/setup/env.setup";

export type KeycloakRole = "admin" | "user";

Expand All @@ -20,7 +20,7 @@ export function withSecured(...roles: KeycloakRole[]): RequestHandler[] {
const flattenedRoles = roles.flat();

const roleSecuredMiddleware: RequestHandler = async (req, res, next) => {
if (flattenedRoles.length === 0) {
if (flattenedRoles.length === 0 || env.NODE_ENV === "test") {
next();
return;
}
Expand All @@ -30,7 +30,7 @@ export function withSecured(...roles: KeycloakRole[]): RequestHandler[] {
const token = bearerToken.split(" ")[1];
const { sub: userId } = jwt.decode(token) as KeycloakTokenData;
const roles = await iocRegistry
.inject<KeycloakRepository>(KeycloakRepository.name)
.inject<AuthorizationRepository>("AuthorizationRepository")
.findRolesByUserId(userId);
const hasRole = flattenedRoles.some(role => roles.includes(role));

Expand All @@ -40,16 +40,18 @@ export function withSecured(...roles: KeycloakRole[]): RequestHandler[] {

next();
} catch (error: unknown) {
console.log("error in withSecured", error);
next(getTypedError(error));
}
};

const protect = async (req: Request, res: Response, next: NextFunction) => {
try {
const keycloakAuthorization = iocRegistry.inject<Authorization>(KeycloakAuthorization.name);
const keycloakAuthorization = iocRegistry.inject<Authorization>("Authorization");
const handler = keycloakAuthorization.protect();
handler(req, res, next);
} catch (error: unknown) {
console.log("Error in keycloak.protect()", error);
next(getTypedError(error));
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as KC from "@org/backend/config/Keycloak.config";
import { type AuthorizationRepository } from "@org/backend/interface/AuthorizationRepository";

// PROBLEM JE STO SE KeycloakRepository svakako pokrene (konstruktor, super.KeycloakDao) a to ne zelimo u testovima
// logika je takva da se svi defaultni instanciraju i tek onda se mergaju mockane instance u jedan objekt

/**
* @see {@link https://www.keycloak.org/docs-api/22.0.1/rest-api/index.html Keycloak Admin REST API} documentation.
*/
Expand Down
8 changes: 4 additions & 4 deletions packages/backend/src/infrastructure/service/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ import { type PaginationResult } from "@org/shared";
import { type UserRepository } from "@org/backend/infrastructure/repository/impl/UserRepository";
import { type User } from "@org/shared";
import { autowired } from "@org/backend/decorators/autowired";
import { type KeycloakRepository } from "@org/backend/infrastructure/repository/impl/KeycloakRepository";
import { type AuthorizationRepository } from "@org/backend/interface/AuthorizationRepository";
import type * as KC from "@org/backend/config/Keycloak.config";

export class UserService {
@autowired private userRepository: UserRepository;
@autowired private keycloakRepository: KeycloakRepository;
@autowired private authorizationRepository: AuthorizationRepository;

async search(options: Partial<PaginationOptions>): Promise<PaginationResult<User>> {
return await this.userRepository.findAllPaginated(PaginationOptions.parse(options));
}

async findAll(): Promise<KC.KeycloakUser[]> {
return await this.keycloakRepository.findAllUsers();
return await this.authorizationRepository.findAllUsers();
}

async findOneByUsername(username: string): Promise<KC.KeycloakUser> {
const user = await this.keycloakRepository.findUserByUsername(username);
const user = await this.authorizationRepository.findUserByUsername(username);
if (user === null) throw new ErrorResponse(404, "User not found");
return user;
}
Expand Down
Loading

0 comments on commit 9de241e

Please sign in to comment.