Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/documentations #31

Merged
merged 16 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/documentations-server-e2e/eslint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const baseConfig = require('../../eslint.config.js');

module.exports = [...baseConfig];
18 changes: 18 additions & 0 deletions apps/documentations-server-e2e/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default {
displayName: 'documentations-server-e2e',
preset: '../../jest.preset.js',
globalSetup: '<rootDir>/src/support/global-setup.ts',
globalTeardown: '<rootDir>/src/support/global-teardown.ts',
setupFiles: ['<rootDir>/src/support/test-setup.ts'],
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
},
],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/documentations-server-e2e',
};
17 changes: 17 additions & 0 deletions apps/documentations-server-e2e/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "documentations-server-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": ["documentations-server"],
"targets": {
"e2e": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"],
"options": {
"jestConfig": "apps/documentations-server-e2e/jest.config.ts",
"passWithNoTests": true
},
"dependsOn": ["documentations-server:build"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import axios from 'axios';

describe('GET /api', () => {
it('should return a message', async () => {
const res = await axios.get(`/api`);

expect(res.status).toBe(200);
expect(res.data).toEqual({ message: 'Hello API' });
});
});
10 changes: 10 additions & 0 deletions apps/documentations-server-e2e/src/support/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable */
var __TEARDOWN_MESSAGE__: string;

module.exports = async function () {
// Start services that that the app needs to run (e.g. database, docker-compose, etc.).
console.log('\nSetting up...\n');

// Hint: Use `globalThis` to pass variables to global teardown.
globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n';
};
7 changes: 7 additions & 0 deletions apps/documentations-server-e2e/src/support/global-teardown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-disable */

module.exports = async function () {
// Put clean up logic here (e.g. stopping services, docker-compose, etc.).
// Hint: `globalThis` is shared between setup and teardown.
console.log(globalThis.__TEARDOWN_MESSAGE__);
};
9 changes: 9 additions & 0 deletions apps/documentations-server-e2e/src/support/test-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* eslint-disable */
import axios from 'axios';

module.exports = async function () {
// Configure axios for tests to use.
const host = process.env.HOST ?? 'localhost';
const port = process.env.PORT ?? '3000';
axios.defaults.baseURL = `http://${host}:${port}`;
};
13 changes: 13 additions & 0 deletions apps/documentations-server-e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
}
9 changes: 9 additions & 0 deletions apps/documentations-server-e2e/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["jest.config.ts", "src/**/*.ts"]
}
3 changes: 3 additions & 0 deletions apps/documentations-server/eslint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const baseConfig = require('../../eslint.config.js');

module.exports = [...baseConfig];
10 changes: 10 additions & 0 deletions apps/documentations-server/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default {
displayName: 'documentations-server',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/documentations-server',
};
43 changes: 43 additions & 0 deletions apps/documentations-server/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "documentations-server",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/documentations-server/src",
"projectType": "application",
"tags": [],
"targets": {
"build": {
"executor": "nx:run-commands",
"options": {
"command": "webpack-cli build",
"args": ["node-env=production"]
},
"configurations": {
"development": {
"args": ["node-env=development"]
}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"dependsOn": ["build"],
"options": {
"buildTarget": "documentations-server:build",
"runBuildTargetDependencies": false
},
"configurations": {
"development": {
"buildTarget": "documentations-server:build:development"
},
"production": {
"buildTarget": "documentations-server:build:production"
}
}
},
"test": {
"options": {
"passWithNoTests": true
}
}
}
}
21 changes: 21 additions & 0 deletions apps/documentations-server/src/app/app.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';

describe('AppController', () => {
let app: TestingModule;

beforeAll(async () => {
app = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
});

describe('getData', () => {
it('should return "Hello API"', () => {
const appController = app.get<AppController>(AppController);
expect(appController.getData()).toEqual({ message: 'Hello API' });
});
});
});
12 changes: 12 additions & 0 deletions apps/documentations-server/src/app/app.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
getData() {
return this.appService.getData();
}
}
19 changes: 19 additions & 0 deletions apps/documentations-server/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ApiResponseInterceptor } from './core/utilities/http-response.interceptor';
import { AppController } from './app.controller';
import { AppMenuModule } from './modules/app-menu/app-menu.module';
import { AppPageModule } from './modules/app-page/app-page.module';
import { AppService } from './app.service';
import { Module } from '@nestjs/common';
@Module({
imports: [AppMenuModule, AppPageModule],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_INTERCEPTOR,
useClass: ApiResponseInterceptor,
},
],
})
export class AppModule {}
20 changes: 20 additions & 0 deletions apps/documentations-server/src/app/app.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Test } from '@nestjs/testing';
import { AppService } from './app.service';

describe('AppService', () => {
let service: AppService;

beforeAll(async () => {
const app = await Test.createTestingModule({
providers: [AppService],
}).compile();

service = app.get<AppService>(AppService);
});

describe('getData', () => {
it('should return "Hello API"', () => {
expect(service.getData()).toEqual({ message: 'Hello API' });
});
});
});
8 changes: 8 additions & 0 deletions apps/documentations-server/src/app/app.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
getData(): { message: string } {
return { message: 'Hello API' };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Test, TestingModule } from '@nestjs/testing';

import { ContentFetchingService } from './content-fetching.service';

describe('ContentFetchingService', () => {
let service: ContentFetchingService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ContentFetchingService],
}).compile();

service = module.get<ContentFetchingService>(ContentFetchingService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Injectable, Logger } from '@nestjs/common';
import { existsSync, readFile } from 'fs';

import { join } from 'path';
import { promisify } from 'util';
import { workspaceRoot } from '@nx/devkit';

@Injectable()
export class ContentFetchingService {
async load(filePath: string): Promise<string> {
Logger.log('ContentFetchingService, load file by given filePath', {
filePath,
});
const fullPath = join(
workspaceRoot,
'apps/documentations-server/src/assets/contents',
filePath,
);

Logger.log('ContentFetchingService, parsed fullPath', { fullPath });
if (!existsSync(fullPath)) {
Logger.error('ContentFetchingService, File does not exist', { fullPath });
throw new Error(`File not found: ${fullPath}`);
}

const readFileAsync = promisify(readFile);

try {
Logger.log('ContentFetchingService, start reading file', { fullPath });
const data = await readFileAsync(fullPath, 'utf-8');
Logger.log('ContentFetchingService, Success readFile given filepath', {
fullPath,
data,
});
return data;
} catch (err) {
Logger.error('ContentFetchingService, Error while reading filepath', {
fullPath,
err,
});
throw err;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable, lastValueFrom } from 'rxjs';
import { failureResponse, successResponse } from './http-response.utils';

@Injectable()
export class ApiResponseInterceptor<T> implements NestInterceptor<T> {
async intercept(
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<unknown>> {
const httpContext = context.switchToHttp();
const response = httpContext.getResponse();

try {
const data = await lastValueFrom(next.handle());

// Set success status code
response.status(200);

return new Observable((observer) => {
observer.next(successResponse('Request success', data || null)); // Ensure null is handled
observer.complete();
});
} catch (error) {
// Extract error status or fallback to 500
const statusCode = error.status || 500;

// Set error status code
response.status(statusCode);

return new Observable((observer) => {
observer.next(
failureResponse(
'Request failed',
error.response || error.message || error,
),
);
observer.complete();
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export class ApiResponseHelper<T> {
success: boolean;
message: string;
data?: T; // Generic type for response payload
error?: Error | Record<string, unknown> | unknown; // Error details, if applicable
}

export function createApiResponse<T>(
success: boolean,
message: string,
data?: T,
error?: Error | Record<string, unknown> | unknown,
): ApiResponseHelper<T> {
return {
success,
message,
data,
error,
};
}

export const successResponse = <T>(message: string, data: T) =>
createApiResponse(true, message, data);
export const failureResponse = <E>(message: string, error: E) =>
createApiResponse(false, message, null, error);
Loading
Loading