Skip to content

Commit

Permalink
add endpoint api/user (#14)
Browse files Browse the repository at this point in the history
* add endpoint api/user

* Init prisma
yarn prisma init

* Add DATABASE_URL

* Add a migration for user

* Add createUser

* Implement user endpoints

- docker compose exec backend yarn prisma migrate dev
- Implement GET /user
- Implement GET /user/:id
- Implement PATCH /user/:id
- Implement DELETE /user/:id

* ✨ Add user page

* Add user list page

* findUser page

* findUser() return Errorpage if Arg Id was not found

* fetch data from backend without cache

* /user/signup create user

* Install shadcn
npx shadcn-ui@latest init

* Add Nav.tsx

* Add button from shadcn
npx shadcn-ui@latest add button

* Install theme provider

* Create user list page

* Fix backend tests

* Make build work

* [frontend] Add User type for build

* Fix compose files

* Fix backend e2e tests

- Add missing `src` alias to `jest-e2e.json`

* Fix Dockerfile and compose.yml

* Fix workflow to build images before running tests

* Fix workflow to generate .env file

* Fix deploy.yml to deploy the commit that triggered the workflow

---------

Co-authored-by: Takumi Hara <[email protected]>
Co-authored-by: Shun Usami <[email protected]>
  • Loading branch information
3 people authored Oct 27, 2023
1 parent 1f72654 commit 6716647
Show file tree
Hide file tree
Showing 48 changed files with 1,854 additions and 299 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ jobs:
run: |
echo "${{ secrets.EC2_SSH_KEY }}" > ssh_key
chmod 600 ssh_key
ssh -oStrictHostKeyChecking=no -i ssh_key ubuntu@ec2-54-168-233-240.ap-northeast-1.compute.amazonaws.com "cd /home/ubuntu/pong && git checkout main && git pull origin main && make prod"
ssh -oStrictHostKeyChecking=no -i ssh_key ubuntu@ec2-35-78-76-108.ap-northeast-1.compute.amazonaws.com "cd /home/ubuntu/pong && git fetch --all && git checkout ${{ github.sha }} && make prod"
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3

- name: Generate .env file
run: |
touch .env
echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env
echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env
echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env
- name: Build images
run: docker compose build

- name: Run backend unit tests
run: docker compose run backend sh -c "yarn test"

Expand Down
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@ down:
build:
docker compose build

.PHONY: rebuild
rebuild:
docker compose build --no-cache

.PHONY: clean
clean:
docker compose down --rmi all --volumes --remove-orphans

.PHONY: test
test:
test: build
# Unit tests for backend
docker compose run backend sh -c "yarn test"
docker compose -f compose.yml -f compose.dev.yml run backend sh -c "yarn test"
# E2E tests for backend
docker compose run backend sh -c "yarn test:e2e"
docker compose -f compose.yml -f compose.dev.yml run backend sh -c "yarn test:e2e"

.PHONY: e2e
e2e:
./test.sh

.PHONY: dev
dev:
dev: build
docker compose -f compose.yml -f compose.dev.yml up -d

.PHONY: prod
prod: clean build
prod: clean rebuild
docker compose -f compose.yml -f compose.prod.yml up -d
5 changes: 2 additions & 3 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
node_modules
Dockerfile
dist
**/node_modules
**/dist
6 changes: 5 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ FROM node:20

WORKDIR /app

COPY package.json yarn.lock ./
COPY prisma ./prisma/
RUN yarn install && yarn prisma generate

COPY . .

RUN yarn install && yarn build
RUN yarn build
9 changes: 8 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/platform-express": "^10.0.0",
"@prisma/client": "5.5.0",
"pg-promise": "^11.5.4",
"prisma": "^5.5.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
},
Expand Down Expand Up @@ -51,10 +54,14 @@
"typescript": "^5.1.3"
},
"jest": {
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/$1"
},
"moduleFileExtensions": [
"js",
"json",
"ts"
"ts",
"tsx"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
Expand Down
11 changes: 11 additions & 0 deletions backend/prisma/migrations/20231025035946_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,

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

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
3 changes: 3 additions & 0 deletions backend/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"
17 changes: 17 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

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

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
16 changes: 0 additions & 16 deletions backend/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import * as pgPromise from 'pg-promise';

const pgp = pgPromise({});
// postgres://user:password@host:port/database
const db = pgp(`postgres://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@db:5432/${process.env.POSTGRES_DB}`);

@Controller()
export class AppController {
Expand All @@ -14,15 +9,4 @@ export class AppController {
getHello(): string {
return this.appService.getHello();
}

@Get('/api')
getApi(): string {
return "42Tokyo Hello from API\n";
}

@Get('/api/db')
async getDb(): Promise<string> {
const {value} = await db.one('SELECT $1 AS value', 123);
return value;
}
}
3 changes: 2 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';

@Module({
imports: [],
imports: [UserModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
1 change: 1 addition & 0 deletions backend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
await app.listen(3000);
}
bootstrap();
9 changes: 9 additions & 0 deletions backend/src/prisma.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}
1 change: 1 addition & 0 deletions backend/src/user/dto/create-user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class CreateUserDto {}
4 changes: 4 additions & 0 deletions backend/src/user/dto/update-user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';

export class UpdateUserDto extends PartialType(CreateUserDto) {}
1 change: 1 addition & 0 deletions backend/src/user/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class User {}
21 changes: 21 additions & 0 deletions backend/src/user/user.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { PrismaService } from 'src/prisma.service';

describe('UserController', () => {
let controller: UserController;

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

controller = module.get<UserController>(UserController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
44 changes: 44 additions & 0 deletions backend/src/user/user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { UserService } from './user.service';
import { UpdateUserDto } from './dto/update-user.dto';
import { User as UserModel } from '@prisma/client';

@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}

@Post()
create(
@Body() userData: { name?: string; email: string },
): Promise<UserModel> {
return this.userService.create(userData);
}

@Get()
findAll() {
return this.userService.findAll();
}

@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}

@Patch(':id')
update(@Param('id') id: string, @Body() userData: { name?: string; email?: string }) {
return this.userService.update(+id, userData);
}

@Delete(':id')
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
}
10 changes: 10 additions & 0 deletions backend/src/user/user.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { PrismaService } from 'src/prisma.service';

@Module({
controllers: [UserController],
providers: [UserService, PrismaService],
})
export class UserModule {}
19 changes: 19 additions & 0 deletions backend/src/user/user.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { PrismaService } from 'src/prisma.service';

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

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

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

it('should be defined', () => {
expect(service).toBeDefined();
});
});
34 changes: 34 additions & 0 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@nestjs/common';
import { UpdateUserDto } from './dto/update-user.dto';
import { PrismaService } from 'src/prisma.service';
import { User, Prisma } from '@prisma/client';

@Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}

async create(data: Prisma.UserCreateInput): Promise<User> {
return this.prisma.user.create({ data });
}

findAll() {
return this.prisma.user.findMany();
}

findOne(id: number) {
return this.prisma.user.findFirst( { where: { id: id } });
}

update(id: number, data: Prisma.UserUpdateInput) {
return this.prisma.user.update({
data,
where: { id: id },
});
}

remove(id: number) {
return this.prisma.user.delete({
where: { id: id },
});
}
}
3 changes: 3 additions & 0 deletions backend/test/jest-e2e.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/../src/$1"
},
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
Expand Down
29 changes: 29 additions & 0 deletions backend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,11 @@
path-to-regexp "3.2.0"
tslib "2.6.2"

"@nestjs/mapped-types@*":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz#c8a090a8d22145b85ed977414c158534210f2e4f"
integrity sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg==

"@nestjs/platform-express@^10.0.0":
version "10.2.7"
resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.2.7.tgz#b2ef2df01c0c757a3d356659460563a5246e7d0f"
Expand Down Expand Up @@ -803,6 +808,23 @@
picocolors "^1.0.0"
tslib "^2.6.0"

"@prisma/[email protected]":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.5.0.tgz#d3f787a66e34d97c33325a3f1b14af918f07e723"
integrity sha512-JiCj/h79PRawWiBVa4Ng4tBaKMrfJgUwuRrZi4NuQRd58gWIP4kEYMqIidpUrab3dU2NM617pWRG2avX+dh+Sg==
dependencies:
"@prisma/engines-version" "5.5.0-19.475c616176945d72f4330c92801f0c5e6398dc0f"

"@prisma/engines-version@5.5.0-19.475c616176945d72f4330c92801f0c5e6398dc0f":
version "5.5.0-19.475c616176945d72f4330c92801f0c5e6398dc0f"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.5.0-19.475c616176945d72f4330c92801f0c5e6398dc0f.tgz#c3ddca62ee65158a20ce194c2492791f506f4bfc"
integrity sha512-/1orOUq7I54ISYBJockYB15FmF/VkCBeFzYYkWyLtBl2tLlJWHmA3NEUBM+wSmXGLK78BmuSNHrLbf8bmXkukQ==

"@prisma/[email protected]":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.5.0.tgz#80b9035d533e3dcd4297ddeada4806fe176ae7bf"
integrity sha512-WX+8l4sJuWeS3A/5WqJpdBHp32UzUticiKwUWbFggcd6/sqGtCiiaFmXYmNw6Au2O6hjSX37Y07Vu2ZhP9cmWg==

"@sinclair/typebox@^0.27.8":
version "0.27.8"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
Expand Down Expand Up @@ -4152,6 +4174,13 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
ansi-styles "^5.0.0"
react-is "^18.0.0"

prisma@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.5.0.tgz#088c3733ddc578cabdbddb89adc53ddc423ac27f"
integrity sha512-QyMMh1WQiGU07Iz3Q8jd6y5KlomGLDVb4etkMWpQ4EmDAaY+zGgNHmBk2MfRPb0A+un1Ior1nZWZorfnKD6E5A==
dependencies:
"@prisma/engines" "5.5.0"

process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
Expand Down
Loading

0 comments on commit 6716647

Please sign in to comment.