Skip to content

Commit

Permalink
Merge pull request #5 from twin-te/feature/deleteByUserId
Browse files Browse the repository at this point in the history
  • Loading branch information
takonasu authored Apr 3, 2023
2 parents eda1a28 + 699b436 commit 8f19b37
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 52 deletions.
33 changes: 19 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,36 @@
`.env` を見ると雰囲気がわかるので `.env.local` にコピーしてよしなに上書きしてください

# 推奨開発環境

docker + vscode を使うことで簡単に開発可能。

1. [RemoteDevelopment](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)拡張機能をインストール
2. このプロジェクトのフォルダを開く
3. 右下に `Folder contains a Dev Container configuration file. Reopen folder to develop in a container` と案内が表示されるので`Reopen in Container`を選択する。(表示されない場合はコマンドパレットを開き`open folder in container`と入力する)
4. node14の開発用コンテナが立ち上がりVSCodeで開かれる。また、`.devcontainer/docker-compose.yml` に任意のサービスを追加するとvscode起動時に一緒に起動できる(データベース等)。
4. node14 の開発用コンテナが立ち上がり VSCode で開かれる。また、`.devcontainer/docker-compose.yml` に任意のサービスを追加すると vscode 起動時に一緒に起動できる(データベース等)。

# npmコマンド一覧
# npm コマンド一覧

|コマンド|説明|
|:--|:--|
|dev| 開発起動|
|proto|protoファイルから型定義を生成(proto-gen.shを実行している)|
|schema|prisma のスキーマから型定義を生成|
|apply-db|prisma のスキーマを破壊的にデータベースに適用する|
|client|grpcリクエストが送れるCLIを起動|
|test|テストを実行|
|build|distにビルド結果を出力|
| コマンド | 説明 |
| :------- | :---------------------------------------------------------- |
| dev | 開発起動 |
| proto | proto ファイルから型定義を生成(proto-gen.sh を実行している) |
| schema | prisma のスキーマから型定義を生成 |
| apply-db | prisma のスキーマを破壊的にデータベースに適用する |
| client | grpc リクエストが送れる CLI を起動 |
| test | テストを実行 |
| build | dist にビルド結果を出力 |

# とりあえず動かす

```bash
# 準備
yarn && yarn proto && yarn apply-db
$ yarn && yarn proto && yarn migrate:dev

# テスト(テストが通ればとりあえずOK)
$ yarn test

# 開発鯖立ち上げ
yarn dev
$ yarn dev

```
```
129 changes: 104 additions & 25 deletions __tests__/grpc/Session.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,59 @@ beforeAll(async () => {
) as unknown) as GrpcClient<SessionService>
})

const userId = [uuid(), uuid()]
let sessionId = ['', '']
test('セッションAを作成する', (done) => {
client.startSession({ userId: userId[0] }, (err, res) => {
const userId = {
userA: uuid(),
userB: uuid(),
userC: uuid(),
}
let sessionId = {
'userA-1': '',
'userB-1': '',
'userC-1': '',
'userC-2': '',
}
test('セッションA-1を作成する', (done) => {
client.startSession({ userId: userId['userA'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.session?.userId).toEqual(userId[0])
expect(res?.session?.userId).toEqual(userId['userA'])
expect(res?.session?.sessionId).not.toBeNull()
sessionId[0] = res?.session?.sessionId ?? ''
sessionId['userA-1'] = res?.session?.sessionId ?? ''
done()
})
})
test('セッションBを作成する', (done) => {
client.startSession({ userId: userId[1] }, (err, res) => {
test('セッションB-1を作成する', (done) => {
client.startSession({ userId: userId['userB'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.session?.userId).toEqual(userId[1])
expect(res?.session?.userId).toEqual(userId['userB'])
expect(res?.session?.sessionId).not.toBeNull()
sessionId[1] = res?.session?.sessionId ?? ''
sessionId['userB-1'] = res?.session?.sessionId ?? ''
done()
})
})
test('セッションC-1を作成する', (done) => {
client.startSession({ userId: userId['userC'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.session?.userId).toEqual(userId['userC'])
expect(res?.session?.sessionId).not.toBeNull()
sessionId['userC-1'] = res?.session?.sessionId ?? ''
done()
})
})
test('セッションC-2(同じユーザの別セッション)を作成する', (done) => {
client.startSession({ userId: userId['userC'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.session?.userId).toEqual(userId['userC'])
expect(res?.session?.sessionId).not.toBeNull()
sessionId['userC-2'] = res?.session?.sessionId ?? ''
done()
})
})

test('存在するセッションAを返す', (done) => {
client.getSession({ sessionId: sessionId[0] }, (err, res) => {
client.getSession({ sessionId: sessionId['userA-1'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.userId).toEqual(userId[0])
expect(res?.sessionId).toEqual(sessionId[0])
expect(res?.userId).toEqual(userId['userA'])
expect(res?.sessionId).toEqual(sessionId['userA-1'])
done()
})
})
Expand All @@ -67,39 +94,91 @@ test('存在しないセッション(空文字列)でエラーを返す', (done)
})
})

test('セッションAを消去する', (done) => {
client.deleteSession({ sessionId: sessionId[0] }, (err, res) => {
test('セッションAをセッションIdで消去する', (done) => {
client.deleteSessionBySessionId(
{ sessionId: sessionId['userA-1'] },
(err, res) => {
expect(err).toBeNull()
done()
}
)
})

test('存在しないセッションをセッションIdで消去するとエラーを返す', (done) => {
client.deleteSessionBySessionId({ sessionId: uuid() }, (err, res) => {
expect(err?.code).toBe(5)
done()
})
})

test('存在しないセッション(セッションIdが空文字列)を消去するとエラーを返す', (done) => {
client.deleteSessionBySessionId({ sessionId: '' }, (err, res) => {
expect(err?.code).toBe(3)
done()
})
})

test('消去したセッションAを得ようとしてもエラーを返す', (done) => {
client.getSession({ sessionId: sessionId['userA-1'] }, (err, res) => {
expect(err?.code).toBe(5)
done()
})
})

test('セッションBをuserIdで消去する', (done) => {
client.deleteSessionByUserId({ userId: userId['userB'] }, (err, res) => {
expect(err).toBeNull()
done()
})
})

test('存在しないセッションを消去するとエラーを返す', (done) => {
client.deleteSession({ sessionId: uuid() }, (err, res) => {
test('存在しないセッションをuserIdで消去するとエラーを返す', (done) => {
client.deleteSessionByUserId({ userId: uuid() }, (err, res) => {
expect(err?.code).toBe(5)
done()
})
})

test('存在しないセッション(空文字列)を消去するとエラーを返す', (done) => {
client.deleteSession({ sessionId: '' }, (err, res) => {
test('存在しないセッション(userIdが空文字列)を消去するとエラーを返す', (done) => {
client.deleteSessionByUserId({ userId: '' }, (err, res) => {
expect(err?.code).toBe(3)
done()
})
})

test('消去したセッションAを得ようとしてもエラーを返す', (done) => {
client.getSession({ sessionId: sessionId[0] }, (err, res) => {
test('消去したセッションBを得ようとしてもエラーを返す', (done) => {
client.getSession({ sessionId: sessionId['userB-1'] }, (err, res) => {
expect(err?.code).toBe(5)
done()
})
})

test('存在するセッションBを返す', (done) => {
client.getSession({ sessionId: sessionId[1] }, (err, res) => {
test('存在するセッションC-1を返す', (done) => {
client.getSession({ sessionId: sessionId['userC-1'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.userId).toEqual(userId['userC'])
expect(res?.sessionId).toEqual(sessionId['userC-1'])
done()
})
})

test('セッションC-1とC-2をuserIdで消去する', (done) => {
client.deleteSessionByUserId({ userId: userId['userC'] }, (err, res) => {
expect(err).toBeNull()
expect(res?.userId).toEqual(userId[1])
expect(res?.sessionId).toEqual(sessionId[1])
done()
})
})

test('セッションC-1が消えている', (done) => {
client.getSession({ sessionId: sessionId['userC-2'] }, (err, res) => {
expect(err?.code).toBe(5)
done()
})
})

test('セッションC-2も消えている', (done) => {
client.getSession({ sessionId: sessionId['userC-2'] }, (err, res) => {
expect(err?.code).toBe(5)
done()
})
})
Expand Down
13 changes: 10 additions & 3 deletions protos/SessionService.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import "google/protobuf/timestamp.proto";
service SessionService {
rpc StartSession(StartSessionRequest) returns (StartSessionResponse);
rpc GetSession(GetSessionRequest) returns (Session);
rpc DeleteSession(DeleteSessionRequest) returns (DeleteSessionResponse);
rpc DeleteSessionBySessionId(DeleteSessionBySessionIdRequest) returns (DeleteSessionBySessionIdResponse);
rpc DeleteSessionByUserId(DeleteSessionByUserIdRequest) returns (DeleteSessionByUserIdResponse);
}

message StartSessionRequest {
Expand All @@ -32,8 +33,14 @@ message Session {
google.protobuf.Timestamp expired_at = 3;
}

message DeleteSessionRequest {
message DeleteSessionBySessionIdRequest {
string session_id = 1;
}

message DeleteSessionResponse {}
message DeleteSessionBySessionIdResponse {}

message DeleteSessionByUserIdRequest {
string user_id = 1;
}

message DeleteSessionByUserIdResponse {}
16 changes: 12 additions & 4 deletions src/database/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,22 @@ export async function findActiveSessionById({
})
}

export async function deleteSessionById({
id,
export async function deleteSessionBySessionId({
sessionId,
}: {
id: string
sessionId: string
}): Promise<{ id: string } | null> {
return await prismaClient.session.delete({
where: {
id,
id: sessionId,
},
})
}

export async function deleteSessionByUserId({ userId }: { userId: string }) {
return await prismaClient.session.deleteMany({
where: {
user_id: userId,
},
})
}
33 changes: 27 additions & 6 deletions src/grpc/session.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import dayjs from 'dayjs'
import {
SessionService,
StartSessionResponse,
DeleteSessionResponse,
DeleteSessionBySessionIdResponse,
DeleteSessionByUserIdResponse,
} from '../../generated'
import { sessionLifeTimeHours } from '../constant'
import {
createSession,
findActiveSessionById,
deleteSessionById,
deleteSessionBySessionId,
deleteSessionByUserId,
} from '../database/session'
import { GrpcServer } from './type'
import { transformSession } from './transformer'
Expand Down Expand Up @@ -49,19 +51,38 @@ export const sessionService: GrpcServer<SessionService> = {

callback(null, transformSession(session))
},
async deleteSession({ request }, callback) {
async deleteSessionBySessionId({ request }, callback) {
if (!request.sessionId) {
return callback({ code: Status.INVALID_ARGUMENT })
}

try {
await deleteSessionById({
id: request.sessionId,
await deleteSessionBySessionId({
sessionId: request.sessionId,
})
} catch {
return callback({ code: Status.NOT_FOUND })
}

callback(null, DeleteSessionResponse.create())
callback(null, DeleteSessionBySessionIdResponse.create())
},

async deleteSessionByUserId({ request }, callback) {
if (!request.userId) {
return callback({ code: Status.INVALID_ARGUMENT })
}

try {
const result = await deleteSessionByUserId({
userId: request.userId,
})
if (result.count === 0) {
throw new Error('not found')
}
} catch {
return callback({ code: Status.NOT_FOUND })
}

callback(null, DeleteSessionByUserIdResponse.create())
},
}

0 comments on commit 8f19b37

Please sign in to comment.