Skip to content

Commit

Permalink
Add user repor,error middleware and db connect
Browse files Browse the repository at this point in the history
  • Loading branch information
raulgonzalez committed Apr 30, 2024
1 parent 6e7afed commit 6edb3b7
Show file tree
Hide file tree
Showing 22 changed files with 587 additions and 22 deletions.
20 changes: 6 additions & 14 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,20 @@ module.exports = {
browser: true,
es2021: true,
node: true,
jest: true,
},
extends: 'xo',
extends: ['xo', 'prettier'],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
{
extends: ['xo-typescript'],
extends: ['xo-typescript', 'prettier'],
files: ['*.ts', '*.tsx'],
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
tsconfigRootDir: __dirname,
project: '/tsconfig.json',
},
rules: {
indent: 'off',
},
rules: {},
};
18 changes: 13 additions & 5 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
const config = {
clearMocks: true,
export default {
collectCoverage: true,

collectCoverageFrom: ['src/*/*.ts'],
coverageDirectory: 'coverage',

coveragePathIgnorePatterns: [
'index.ts',
'type.repo.ts',
'entities',
'interface',
'tools',
'_mock',
],
coverageProvider: 'v8',
preset: 'ts-jest',
testPathIgnorePatterns: ['dist'],
resolver: 'jest-ts-webcompat-resolver',
};
export default config;
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.6",
"@jest/reporters": "29.7.0",
"@types/morgan": "^1.9.9",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "^7.7.1",
Expand All @@ -55,6 +56,6 @@
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"nodemon": "^3.1.0",
"prisma": "^5.12.1"
"prisma": "^5.13.0"
}
}
34 changes: 34 additions & 0 deletions prisma/migrations/20240430112638_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- CreateEnum
CREATE TYPE "Race" AS ENUM ('men', 'elve', 'dwarf', 'urukhai', 'orc', 'hobbit');

-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Character" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL,
"imgUrl" TEXT NOT NULL,
"faction" TEXT NOT NULL,
"race" "Race" NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "Character_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- AddForeignKey
ALTER TABLE "Character" ADD CONSTRAINT "Character_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
12 changes: 12 additions & 0 deletions prisma/migrations/20240430123011_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Warnings:
- A unique constraint covering the columns `[userNamo]` on the table `User` will be added. If there are existing duplicate values, this will fail.
- Added the required column `userNamo` to the `User` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "User" ADD COLUMN "userNamo" TEXT NOT NULL;

-- CreateIndex
CREATE UNIQUE INDEX "User_userNamo_key" ON "User"("userNamo");
17 changes: 17 additions & 0 deletions prisma/migrations/20240430124831_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
Warnings:
- You are about to drop the column `userNamo` on the `User` table. All the data in the column will be lost.
- A unique constraint covering the columns `[userName]` on the table `User` will be added. If there are existing duplicate values, this will fail.
- Added the required column `userName` to the `User` table without a default value. This is not possible if the table is not empty.
*/
-- DropIndex
DROP INDEX "User_userNamo_key";

-- AlterTable
ALTER TABLE "User" DROP COLUMN "userNamo",
ADD COLUMN "userName" TEXT NOT NULL;

-- CreateIndex
CREATE UNIQUE INDEX "User_userName_key" ON "User"("userName");
3 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
46 changes: 46 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
userName String @unique
password String
character Character[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Character{
id String @id @default(cuid())
user User @relation(fields:[userId], references:[id])
userId String
name String
description String
imgUrl String
faction String
race Race
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}


enum Race {
men
elve
dwarf
urukhai
orc
hobbit
}

2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ sonar.organization=isdi-coders-2023
sonar.sources=./src
sonar.test.inclusions=./src///.test., ./src///.spec.
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions= src/index.ts, src/app.ts, src//.test., src//routing., src/main.ts, src//.spec., src/model/,
sonar.coverage.exclusions= src/index.ts, src/app.ts, src//.test., src//routing., src/main.ts, src//.spec., src/model/,**/type.*,
19 changes: 19 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import express, { type Express } from 'express';
import morgan from 'morgan';
import cors from 'cors';
import createDebug from 'debug';
import { type PrismaClient } from '@prisma/client';

const debug = createDebug('GONJI:app');
export const createApp = () => {
debug('Creating app');
return express();
};

export const startApp = (app: Express, prisma: PrismaClient) => {
debug('Starting app');
app.use(express.json());
app.use(morgan('dev'));
app.use(cors());
app.use(express.static('public'));
};
17 changes: 17 additions & 0 deletions src/entities/character.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import joy from 'joi';
import { type CharacterCreateDto } from './character';

export const characterCreateSchema = joy.object<CharacterCreateDto>({
name: joy.string().required(),
imgUrl: joy.string().uri().required(),
description: joy.string().required(),
faction: joy.string().required(),
userId: joy.string().required(),
});
export const characterUpdateSchema = joy.object<Partial<CharacterCreateDto>>({
name: joy.string(),
imgUrl: joy.string().uri(),
description: joy.string(),
faction: joy.string(),
userId: joy.string(),
});
18 changes: 18 additions & 0 deletions src/entities/character.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type Character = {
id: string;
name: string;
imgUrl: string;
description: string;
faction: string;
race: 'Men' | 'Elf' | 'Elve' | 'Dwarf' | 'Uruk-hai' | 'Orc' | 'Hobbit';
userId: string;
};
export type CharacterCreateDto = {
name: string;
imgUrl: string;
description: string;
faction: string;
race: 'Men' | 'Elf' | 'Elve' | 'Dwarf' | 'Uruk-hai' | 'Orc' | 'Hobbit';
userId: string;
};
export type CharacterUpdateDto = Partial<CharacterCreateDto>;
13 changes: 13 additions & 0 deletions src/entities/user.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import joy from 'joi';
import { type UserCreateDto } from './user';

export const userCreateSchema = joy.object<UserCreateDto>({
email: joy.string().email().required(),
password: joy.string().min(5).required(),
userName: joy.string().min(3).required(),
});
export const userUpdateSchema = joy.object<Partial<UserCreateDto>>({
email: joy.string().email(),
password: joy.string().min(5),
userName: joy.string().min(3),
});
16 changes: 16 additions & 0 deletions src/entities/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { type Character } from './character';

export type User = {
id: string;
email: string;
password: string;
userName: string;
characters: Character[];
};

export type UserCreateDto = {
email: string;
password: string;
userName: string;
};
export type UserUpdateDto = Partial<UserCreateDto>;
30 changes: 30 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
import { createServer } from 'http';
import createDebug from 'debug';
import 'dotenv/config';
import { createApp, startApp } from './app.js';
import { dbConnect } from './tools/db.connect.js';

const debug = createDebug('GONJI:server');
debug('Starting server');

const port = process.env.PORT ?? 3000;

const app = createApp();
const server = createServer(app);

dbConnect()
.then((prisma) => {
startApp(app, prisma);
server.listen(port);
})
.catch((error) => {
server.emit('error', error);
});

server.on('error', (error) => {
debug('Error:', error);
process.exit(1);
});

server.on('listening', () => {
console.log(`Server Express is running http://localhost:${port}`);
});
45 changes: 45 additions & 0 deletions src/middleware/errors.middleware.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { type Request, type Response } from 'express';
import { ErrorsMiddleware, HttpError } from './errors.middleware';
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';

const req = {} as unknown as Request;
const res = {
json: jest.fn(),
status: jest.fn(),
} as unknown as Response;
const next = jest.fn();

describe('Given a instance of the class ErrorsMiddleware', () => {
const middleware = new ErrorsMiddleware();
test('Then it should be instance of the class', () => {
expect(middleware).toBeInstanceOf(ErrorsMiddleware);
});
describe('When we use the method handle with a HttpError', () => {
test('Then it should call res.status 404', () => {
const error = new HttpError(404, 'Not Found', 'Article not found');
middleware.handle(error, req, res, next);
expect(res.status).toHaveBeenCalledWith(404);
expect(res.json).toHaveBeenCalled();
});
});
describe('When we use the method handle with a PrismaClientKnownRequestError', () => {
test('Then it should call res.status 404', () => {
const error = new PrismaClientKnownRequestError('error', {
code: 'P2025',
clientVersion: '3.0.0',
});
middleware.handle(error, req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
expect(res.json).toHaveBeenCalled();
});
});

describe('When we use the method handle with a Error', () => {
test('Then it should call res.status with 500', () => {
const error = new Error('Something went wrong');
middleware.handle(error, req, res, next);
expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalled();
});
});
});
Loading

0 comments on commit 6edb3b7

Please sign in to comment.