diff --git a/src/routes/docs/rooms.js b/src/routes/docs/rooms.js new file mode 100644 index 00000000..3292b127 --- /dev/null +++ b/src/routes/docs/rooms.js @@ -0,0 +1,830 @@ +const { roomsSchema } = require("./roomsSchema"); +const { objectIdPattern, roomsPattern } = require("./utils"); + +const tag = "rooms"; +const apiPrefix = "/rooms"; + +const roomsDocs = {}; + +roomsDocs[`${apiPrefix}/create`] = { + post: { + tags: [tag], + summary: "방 생성", + description: `방을 생성합니다. 한 유저당 최대 5개의 진행중인 방에 참여할 수 있습니다.
`, + requestBody: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + name: { + type: "string", + pattern: roomsPattern.rooms.name, + description: `방 이름
+ 1~50 글자로 구성되며 영어 대소문자, 숫자, 한글, 특정 특수기호("-", ",", ".", "?", "!", "_")만 가능`, + }, + from: { + type: "string", + pattern: roomsPattern.rooms.from, + description: "출발지 location Document의 ObjectId", + }, + to: { + type: "string", + pattern: roomsPattern.rooms.to, + description: "도착지 location Document의 ObjectId", + }, + time: { + type: "string", + format: "date-time", + description: "방 출발 시각. 현재 이후여야 함.", + }, + maxPartLength: { + type: "integer", + minimum: 2, + maximum: 4, + description: "방의 최대 인원 수", + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "생성 완성된 방 목록", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 400: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + examples: { + "출발지와 도착지가 같음": { + value: { + error: "Room/create : locations are same", + }, + }, + "현재로부터 2주일보다 이후의 방을 생성": { + value: { + error: + "Room/create : cannot over 2 weeks on the basis of current Date", + }, + }, + "존재하지 않는 location Document를 입력": { + value: { + error: "Rooms/create : no corresponding locations", + }, + }, + "사용자가 참여하는 진행 중 방이 5개 이상": { + value: { + error: "Rooms/create : participating in too many rooms", + }, + }, + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/create : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/publicInfo`] = { + get: { + tags: [tag], + summary: "정산 정보를 제외한 방 세부 사항 반환", + description: + "특정 id 방의 정산 정보를 제외한 세부사항을 반환합니다. 로그인을 하지 않아도 접근 가능합니다.", + parameters: [ + { + in: "query", + name: "id", + schema: { + type: "string", + pattern: objectIdPattern, + }, + description: "찾고 싶은 방의 Object id", + }, + ], + responses: { + 200: { + description: "방의 세부 정보가 담긴 room Object", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 404: { + description: "해당 id가 존재하지 않음", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/publicInfo : id does not exist", + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/publicInfo : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/info`] = { + get: { + tags: [tag], + summary: "방 세부 사항 반환", + description: "유저가 참여한 방의 세부사항을 반환합니다.", + parameters: [ + { + in: "query", + name: "id", + schema: { + type: "string", + pattern: objectIdPattern, + }, + description: "찾고 싶은 방의 Object id", + }, + ], + responses: { + 200: { + description: "방의 세부 정보가 담긴 room Object", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 404: { + description: "해당 id가 존재하지 않음", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/info : id does not exist", + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/info : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/join`] = { + post: { + tags: [tag], + summary: "진행 중인 방에 참여", + description: `room의 ID를 받아 해당 room의 참가자 목록에 요청을 보낸 사용자를 추가합니다.
+ 하나의 User는 최대 5개의 진행중인 방에 참여할 수 있습니다.
+ 아직 정원이 차지 않은 방과 아직 출발하지 않은 방에만 참여할 수 있습니다.`, + requestBody: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + roomId: { + type: "string", + pattern: objectIdPattern, + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "방의 세부 정보가 담긴 room Object", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 400: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + examples: { + "사용자가 참여하는 진행 중 방이 5개 이상": { + value: { + error: "Rooms/join : participating in too many rooms", + }, + }, + "입력한 시간의 방이 이미 출발함": { + value: { + error: "Room/join : The room has already departed", + }, + }, + "방의 인원이 모두 찼음": { + value: { + error: "Room/join : The room is already full", + }, + }, + }, + }, + }, + }, + 404: { + description: "해당 id를 가진 방이 존재하지 않음", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/join : no corresponding room", + }, + }, + }, + }, + 409: { + description: "사용자가 이미 참여중임", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/join : {userID} Already in room", + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "text/html": { + example: "Rooms/join : internal server error", + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/abort`] = { + post: { + tags: [tag], + summary: "참여 중인 방에서 퇴장", + description: `room의 ID를 받아 해당 room의 참가자 목록에서 요청을 보낸 사용자를 삭제합니다.
+ 출발했지만 정산이 완료되지 않은 방에서는 나갈 수 없습니다.`, + requestBody: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + roomId: { + type: "string", + pattern: objectIdPattern, + }, + }, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 400: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + examples: { + "잘못된 userId를 포함한 요청임": { + value: { + error: "Rooms/abort : Bad request", + }, + }, + "정산이 되지 않은 출발한 방은 나갈 수 없음": { + value: { + error: + "Rooms/abort : cannot exit room. Settlement is not done", + }, + }, + }, + }, + }, + }, + 403: { + description: "사용자가 해당 방의 구성원이 아님", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/abort : did not joined the room", + }, + }, + }, + }, + 404: { + description: "해당 id를 가진 방이 존재하지 않음", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/abort : no corresponding room", + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/abort : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/search`] = { + get: { + tags: [tag], + summary: "방 검색", + description: `출발지/도착지/날짜를 받아 조건에 맞는 방을 검색합니다.
+ 조건에 맞는 방이 있을 경우, 방들의 정보를 반환하고 없다면 빈 배열을 반환합니다.
+ 로그인을 하지 않아도 접근 가능합니다.`, + parameters: [ + { + in: "query", + name: "name", + schema: { + type: "string", + }, + description: `검색할 방의 이름
+ 주어진 경우 해당 텍스트가 방의 이름에 포함된 방들만 반환.
+ 주어지지 않은 경우 임의의 이름을 가지는 방들을 검색.`, + }, + { + in: "query", + name: "from", + schema: { + type: "string", + pattern: objectIdPattern, + }, + description: `출발지 Document의 ObjectId
+ 주어진 경우 출발지가 일치하는 방들만 반환.
+ 주어지지 않은 경우 임의의 출발지를 가지는 방들을 검색.`, + }, + { + in: "query", + name: "to", + schema: { + type: "string", + pattern: objectIdPattern, + }, + description: `도착지 Document의 ObjectId
+ 주어진 경우 도착지가 일치하는 방들만 반환.
+ 주어지지 않은 경우 임의의 도착지를 가지는 방들을 검색.`, + }, + { + in: "query", + name: "time", + schema: { + type: "string", + format: "date-time", + }, + description: `출발 시각
+ 주어진 경우 주어진 시간부터 주어진 시간부터 그 다음에 찾아오는 오전 5시 전에 출발하는 방들만 반환.
+ 주어지지 않은 경우 현재 시각부터 그 다음으로 찾아오는 오전 5시 전까지의 방들을 반환.`, + }, + { + in: "query", + name: "withTime", + schema: { + type: "boolean", + }, + description: `검색 옵션에 시간 옵션이 포함되어 있는지 여부.
+ false이고 검색하는 날짜가 오늘 이후인 경우 검색하는 시간을 0시 0분 0초로 설정함.`, + }, + { + in: "query", + name: "maxPartLength", + schema: { + type: "integer", + }, + description: ` 방의 최대 인원 수.
+ 주어진 경우 최대 인원 수가 일치하는 방들만 반환.
+ 주어지지 않은 경우 임의의 최대 인원 수를 가지는 방들을 검색.`, + }, + { + in: "query", + name: "isHome", + schema: { + type: "boolean", + }, + description: `홈 페이지 검색인지 여부
+ true인 경우 검색 날짜 범위를 7일로 설정.
+ false인 경우 검색 날짜 범위를 14일로 설정.
`, + }, + ], + responses: { + 200: { + content: { + "application/json": { + schema: { + type: "array", + items: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + }, + 400: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + examples: { + "출발지와 도착지가 같음": { + value: { + error: "Room/search : Bad request", + }, + }, + "출발/도착지가 존재하지 않는 장소": { + value: { + error: "Room/search : no corresponding locations", + }, + }, + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/search : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/searchByUser`] = { + get: { + tags: [tag], + summary: "사용자가 참여 중인 방 검색", + description: `로그인 된 사용자가 참여 중인 방을 검색합니다.
+ 정산 완료 여부 기준으로 진행 중인 방과 완료된 방을 \`ongoing\`과 \`done\`으로 각각 분리하여 응답을 전송합니다.
+ (\`ongoing\`은 \`isOver\`이 flase인 방, \`done\`은 \`isOver\`이 true인 방을 의미합니다.)`, + parameters: {}, + responses: { + 200: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + ongoing: { + type: "array", + items: { + $ref: "#/components/schemas/room", + }, + }, + done: { + type: "array", + items: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/searchByUser : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/commitPayment`] = { + post: { + tags: [tag], + summary: "방 결제 처리", + description: `해당 방에 요청을 보낸 유저를 결제자로 처리합니다.
+ 이미 출발한 방에 대해서만 요청을 처리합니다.
+ 방의 \`part\` 배열에서 요청을 보낸 유저의 \`isSettlement\` 속성은 \`paid\`로 설정됩니다.
+ 나머지 유저들의 \`isSettlement\` 속성을 \`send-required\`로 설정합니다.`, + requestBody: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + roomId: { + type: "string", + pattern: objectIdPattern, + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "결제 정보가 수정된 방의 세부 정보가 담긴 room Object", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 404: { + description: `잘못된 방 요청
+ (사용자가 참여 중인 방이 아니거나, 이미 다른 사람이 결제자이거나, 아직 방이 출발하지 않은 경우)`, + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/:id/commitPayment : cannot find settlement info", + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/:id/commitPayment : internal server error", + }, + }, + }, + }, + }, + }, +}; + +roomsDocs[`${apiPrefix}/commitSettlement`] = { + post: { + tags: [tag], + summary: "방 정산 완료 처리", + description: `해당 방에 요청을 보낸 유저를 정산 완료로 처리합니다.
+ 방의 \`part\` 배열에서 요청을 보낸 유저의 \`isSettlement\` 속성은 \`send-required\`에서 \`sent\`로 변경합니다.
+ 방의 참여한 유저들이 모두 정산완료를 하면 방의 \`isOver\` 속성이 \`true\`로 변경되며, 과거 방으로 취급됩니다.`, + requestBody: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + roomId: { + type: "string", + pattern: objectIdPattern, + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "결제 정보가 수정된 방의 세부 정보가 담긴 room Object", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/room", + }, + }, + }, + }, + 404: { + description: `잘못된 방 요청
+ (사용자가 참여 중인 방이 아니거나, 사용자가 결제를 했거나 이미 정산한 경우)`, + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/:id/settlement : cannot find settlement info", + }, + }, + }, + }, + 500: { + description: "내부 서버 오류", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + }, + }, + example: { + error: "Rooms/:id/settlement : internal server error", + }, + }, + }, + }, + }, + }, +}; + +module.exports = roomsDocs; diff --git a/src/routes/docs/rooms.md b/src/routes/docs/rooms.md deleted file mode 100755 index 3346d753..00000000 --- a/src/routes/docs/rooms.md +++ /dev/null @@ -1,367 +0,0 @@ -# `/rooms` API - -## Table of contents - -- [`/rooms` API](#rooms-api) - - [Table of contents](#table-of-contents) - - [Description](#description) - - [Available endpoints](#available-endpoints) - - [`/publicInfo` **(GET)**](#publicinfo-get) - - [URL parameters](#url-parameters) - - [Response](#response) - - [Errors](#errors) - - [`/info` **(GET)**](#info-get) - - [URL parameters](#url-parameters-1) - - [Response](#response-1) - - [Errors](#errors-1) - - [`/create` **(POST)**](#create-post) - - [POST request form](#post-request-form) - - [Errors](#errors-2) - - [Response](#response-2) - - [`/join` (POST)](#join-post) - - [request JSON form](#request-json-form) - - [Errors](#errors-3) - - [`/abort` (POST)](#abort-post) - - [request JSON form](#request-json-form-1) - - [Errors](#errors-4) - - [`/search` **(GET)**](#search-get) - - [URL parameters](#url-parameters-2) - - [Response](#response-3) - - [Errors](#errors-5) - - [`/searchByUser` **(GET)**](#searchbyuser-get) - - [URL parameters](#url-parameters-3) - - [Response](#response-4) - - [Errors](#errors-6) - - [`/commitPayment` **(POST)**](#commitpayment-post) - - [Request Body](#request-body) - - [Response](#response-5) - - [Errors](#errors-7) - - [`/commitSettlement/` **(POST)**](#commitsettlement-post) - - [Request Body](#request-body-1) - - [Response](#response-6) - - [Errors](#errors-8) - - [`/edit/` **(POST)** **(for dev)**](#edit-post-for-dev) - - [POST request form](#post-request-form-1) - - [Response](#response-7) - - [Errors](#errors-9) - - [`/getAllRoom` **(GET)** (for dev)](#getallroom-get-for-dev) - - [`/removeAllRoom` **(GET)** (for dev)](#removeallroom-get-for-dev) - - [`/:id/delete/` **(GET)** **(for dev)**](#iddelete-get-for-dev) - - [URL Parameters](#url-parameters-4) - - [Response](#response-8) - - [Errors](#errors-10) - -## Description - -- 방 생성/수정/삭제/조회 기능을 지원하는 API. -- `/publicInfo`, `/search`를 제외한 endpoint는 로그인된 상태에서만 접근 가능 -- Request form에서 요구하는 property 이름에 ? 이 붙은 경우 필수가 아니라는 뜻 -- 방을 반환할 경우 그 type은 다음과 같다. - -```javascript -Room { - _id: ObjectId, //ObjectID - name: String, // 1~50글자로 구성되며 영어 대소문자, 숫자, 한글, "-", ",", ".", "?", "!", "_"로만 이루어져야 함. - from: { - _id: ObjectId, // 출발지 document의 ObjectId - koName: String, // 출발지의 한국어 명칭 - enName: String, // 출발지의 영어 명칭 - }, - to: { - _id: ObjectId, // 도착지 document의 ObjectId - koName: String, // 도착지의 한국어 명칭 - enName: String, // 도착지의 영어 명칭 - }, - time: String(ISO 8601), // ex) 방 출발 시각. '2022-01-12T13:58:20.180Z' - isDeparted: Boolean, // 이미 출발한 택시인지 여부 (출발했으면 true) - part: [ - { - _id: ObjectId, // part의 ObjectId - user: { - _id: ObjectId, // 참여 중인 사용자 Document의 ObjectId - name: String, // 참여 중인 사용자 이름 - nickname: String, // 참여 중인 사용자 닉네임 - profileImageUrl: String, // 프로필 사진 url - isSettlement: String || undefined, //해당 사용자의 정산 상태 (주의: "/publicInfo"와 "/search"에서는 isSettlement 속성이 undefined로 설정됨). - }, - } - ], - maxPartLength: Number(2~4), //방의 최대 인원 수 - madeat: String(ISO 8601), // ex) 방 생성 시각. '2022-01-12T13:58:20.180Z' - settlementTotal: Number(2~4), // 정산이 완료된 사용자 수 (주의: "/publicInfo"와 "/search"에서는 settlementTotal 속성이 undefined로 설정됨). - isOver: Boolean, // 요청을 보낸 사용자가 해당 방의 정산을 완료됐는지 여부(완료 시 true) (주의: rooms/search에서는 isOver 속성을 반환하지 않고 undefined를 반환함). - __v: Number, // 문서 버전. mongoDB 내부적으로 사용됨. -} -``` - -`settlementStatus` 속성은 아래 네 가지 값들 중 하나를 가진다. - -1. `"not-departed"` : 아무도 결제/정산하지 않은 상태 -2. `"paid"` : 택시비를 결제한 참가가 "결제하기" 버튼을 누르면 해당 참가자에게 설정되는 정산 상태. -3. `"send-required"` : 특정 참가자가 "결제하기" 버튼을 눌렀을 때 그 방의 나머지 참가자에게 설정되는 정산 상태. -4. `"sent"` : 정산 상태가`"send-required"`인 사용자가 "정산하기" 버튼을 눌렀을 때 그 사용자에게 설정되는 정산 상태. - -## Available endpoints - -### `/publicInfo` **(GET)** - -ID를 parameter로 받아 해당 ID의 room의 정보 출력 - -#### URL parameters - -- id : 조회할 room의 ID - -#### Response - -- 해당 방의 정보 -- 각 참여자의 isSettlement 속성과 방의 settlementTotal 속성은 undefined로 설정됨. - -#### Errors - -- 404 "id does not exist" -- 500 "internal server error" - -### `/info` **(GET)** - -ID를 parameter로 받아 해당 ID의 room의 정보 출력 - -#### URL parameters - -- id : 조회할 room의 ID - -#### Response - -- 해당 방의 정보 - -#### Errors - -- 403 "not logged in" -- 403 "did not joined the room" -- 404 "id does not exist" -- 500 "internal server error" - -### `/create` **(POST)** - -요청을 받아 room을 생성 -하나의 User는 최대 5개의 진행중인 방에 참여할 수 있다. - -#### POST request form - -`Request body` - -```javascript -{ - name : String, // 방 이름. 문서 상단에 명시된 규칙을 만족시켜야 함 - from : ObjectId, // 출발지 Document의 ObjectId - to : ObjectId, // 도착지 Document의 ObjectId - time : Date, // 방 출발 시각. 현재 이후여야 함. - part? : String[], // 방 사람들의 ObjectId. 따라서 빈 배열로 요청하시면 됩니다. - maxPartLength: Number(2~4), //방의 최대 인원 수 -} -``` - -#### Errors - -- 400 "bad request" -- 400 "participating in too many rooms" -- 400 "locations are same" -- 400 "no corresponding locations" -- 500 "internal server error" - -#### Response - -- 새로이 만들어진 방 - -### `/join` (POST) - -room의 ID를 받아 해당 room의 참가자 목록에 요청을 보낸 사용자를 추가한다. -하나의 User는 최대 5개의 진행중인 방에 참여할 수 있다. -아직 정원이 차지 않은 방과 아직 출발하지 않은 방에만 참여할 수 있다. - -#### request JSON form - -```javascript -{ - roomId : ObjectId, // 초대 혹은 참여하려는 방 Document의 ObjectId -} -``` - -#### Errors - -- 400 "Bad request" -- 400 "participating in too many rooms" -- 400 "The room is full" -- 400 "The room has already departed" -- 404 "no corresponding room" -- 409 "{userID} Already in room" -- 500 "internal server error" - -### `/abort` (POST) - -room의 ID를 받아 해당 room의 참가자 목록에서 요청을 보낸 사용자를 삭제한다. -출발했지만 정산이 완료되지 않은 방에서는 나갈 수 없다. - -#### request JSON form - -```javascript -{ - roomId : ObjectId, // 초대 혹은 참여하려는 방 Document의 ObjectId -} -``` - -#### Errors - -- 400 "Bad request" -- 400 "cannot exit room. Settlement is not done" -- 404 "no corresponding room" -- 500 "internal server error" - - -### `/search` **(GET)** - -출발지/도착지/날짜를 받아 해당하는 room들을 반환한다. - -#### URL parameters - -- name?: String, // 검색할 방의 이름. 주어진 경우 해당 텍스트가 방의 이름에 포함된 방들만 반환. 주어지지 않은 경우 임의의 이름을 가지는 방들을 검색. -- from? : ObjectId, // 출발지 Document의 ObjectId. 주어진 경우 출발지가 일치하는 방들만 반환. 주어지지 않은 경우 임의의 출발지를 가지는 방들을 검색. -- to? : ObjectId, // 도착지 Document의 ObjectId. 주어진 경우 도착지가 일치하는 방들만 반환. 주어지지 않은 경우 임의의 도착지를 가지는 방들을 검색. -- time? : Date, // 출발 시각. 주어진 경우 주어진 시간부터 주어진 시간부터 그 다음에 찾아오는 오전 5시 전에 출발하는 방들만 반환. 주어지지 않은 경우 현재 시각부터 그 다음으로 찾아오는 오전 5시 전까지의 방들을 반환. -- withTime? : Boolean, // 검색 옵션에 시간 옵션이 포함되어 있는지 여부. false이고 검색하는 날짜가 오늘 이후인 경우 검색하는 시간을 0시 0분 0초로 설정함. -- maxPartLength?: Number(2~4), // 방의 최대 인원 수. 주어진 경우 최대 인원 수가 일치하는 방들만 반환. 주어지지 않은 경우 임의의 최대 인원 수를 가지는 방들을 검색. - - -#### Response - -조건에 맞는 방**들**의 정보: `Room[]` -조건에 일치하는 방이 없더라도 빈 배열을 반환함. - -#### Errors - -- 400 "Bad request" -- 400 "no corresponding locations" -- 500 "Internal server error" - -### `/searchByUser` **(GET)** - -로그인된 사용자가 참여 중인 room들을 반환한다. - -#### URL parameters - -없음. - -#### Response - -```javascript -{ - ongoing: [Room], // 정산이 완료되지 않은 방 (방의 isOver 속성이 false인 방) - done: [Room], // 정산이 완료된 방 (방의 isOver 속성이 true인 방) -} -``` - -#### Errors - -- 403 "not logged in" -- 500 "internal server error" - - -### `/commitPayment` **(POST)** - -- ID를 받아 해당 방에 요청을 보낸 유저를 결제자로 처리 -- 이미 출발한 방(현재 시각이 출발 시각 이후인 경우)에 대해서만 요청을 처리함 -- 방의 part 배열에서 요청을 보낸 유저의 isSettlement 속성을 `paid`로 설정하고, 나머지 유저들의 isSettlement 속성을 `"send-required"`로 설정함. - -#### Request Body - -- roomId : 정산할 room의 ID - -#### Response - -- 멤버들의 정산정보가 반영된 방의 정보 - -#### Errors - -- 400 "Bad request": 로그인이 되어있지 않은 경우 -- 404 "cannot find settlement info": 사용자가 참여 중인 방이 아니거나, 이미 다른 사람이 결제자이거나, 아직 방이 출발하지 않은 경우 -- 500 "internal server error" - - - -### `/commitSettlement/` **(POST)** - -- ID를 받아 해당 방에 요청을 보낸 유저의 정산을 완료로 처리 -- 방의 part 배열에서 요청을 보낸 유저의 isSettlement 속성을 `send-required`에서 `"sent"`로 변경함. -- 방에 참여한 멤버들이 모두 정산완료를 하면 방의 `isOver` 속성이 `true`로 변경되며, 과거 방으로 취급됨 - -#### Request Body - -- roomId : 정산할 room의 ID - -#### Response - -- 멤버들의 정산정보가 반영된 방의 정보 - -#### Errors - -- 400 "Bad request" : 로그인이 되어있지 않은 경우 -- 404 "cannot find settlement info": 사용자가 참여중인 방이 아니거나, 사용자가 결제를 했거나 이미 정산한 경우 -- 500 "internal server error" - -### `/edit/` **(POST)** **(for dev)** - -- ID와 수정할 데이터를 JSON으로 받아 해당 ID의 room을 수정 -- 방에 참여중인 사용자만 정보를 수정할 수 있음. -- 프론트엔드에서 쓰일 일은 없어 보임. - -#### POST request form - -```javascript -{ - roomId : String, // 수정할 room의 ID - name? : String, // 방 이름. 문서 상단에 명시된 규칙을 만족시켜야 함 - from? : ObjectId, // 출발지 Document의 ObjectId - to? : ObjectId, // 도착지 Document의 ObjectId - time? : Date, // 방 출발 시각. 현재 이후여야 함. - maxPartLength?: Number(2~4), // 방의 최대 인원 수. 현재 참여 인원수보다 크거나 같은 값이어야 함. -} -``` - -#### Response - -- 변경된 방의 정보 - -#### Errors - -- 400 "Bad request" -- 404 "id does not exist" -- 500 "internal server error" - -### `/getAllRoom` **(GET)** (for dev) - -모든 방 가져옴 - -### `/removeAllRoom` **(GET)** (for dev) - -모든 방 삭제 - -### `/:id/delete/` **(GET)** **(for dev)** - -ID를 받아 해당 ID의 room을 제거 - -#### URL Parameters - -- id : 삭제할 room의 ID - -#### Response - -```javascript -{ - id: ObjectId, // 삭제할 방 Document의 ObjectId - isDeleted: true -} -``` - -#### Errors - -- 404 "ID does not exist" -- 500 "Internal server error" \ No newline at end of file diff --git a/src/routes/docs/roomsSchema.js b/src/routes/docs/roomsSchema.js new file mode 100644 index 00000000..e256c5dd --- /dev/null +++ b/src/routes/docs/roomsSchema.js @@ -0,0 +1,90 @@ +const { objectIdPattern, roomsPattern } = require("./utils"); + +const participantSchema = { + part: { + type: "object", + required: ["_id", "name", "nickname", "profileImageUrl", "readAt"], + properties: { + _id: { + type: "string", + pattern: objectIdPattern, + }, + name: { + type: "string", + }, + nickname: { + type: "string", + }, + profileImageUrl: { + type: "string", + }, + isSettlement: { + type: "string", + enum: ["not-departed", "paid", "send-required", "sent"], + default: "not-departed", + }, + readAt: { + type: "string", + format: "date-time", + }, + }, + }, +}; + +const roomsSchema = { + room: { + type: "object", + required: [ + "name", + "from", + "to", + "time", + "part", + "madeat", + "maxPartLength", + "isDeparted", + ], + properties: { + name: { + type: "string", + pattern: objectIdPattern, + }, + from: { + type: "string", + pattern: objectIdPattern, + }, + to: { + type: "string", + pattern: objectIdPattern, + }, + time: { + type: "string", + format: "date-time", + }, + part: { + type: "array", + items: participantSchema["part"], + }, + madeat: { + type: "string", + format: "date-time", + }, + maxPartLength: { + type: "integer", + default: 4, + }, + settlementTotal: { + type: "integer", + default: 0, + }, + isOver: { + type: "boolean", + }, + isDeparted: { + type: "boolean", + }, + }, + }, +}; + +module.exports = { roomsSchema, participantSchema }; diff --git a/src/routes/docs/swaggerDocs.js b/src/routes/docs/swaggerDocs.js index c0b474b4..a6dab17a 100644 --- a/src/routes/docs/swaggerDocs.js +++ b/src/routes/docs/swaggerDocs.js @@ -1,9 +1,11 @@ const reportsSchema = require("./reportsSchema"); +const { participantSchema, roomsSchema } = require("./roomsSchema"); const reportsDocs = require("./reports"); const logininfoDocs = require("./logininfo"); const locationsDocs = require("./locations"); const authDocs = require("./auth"); const usersDocs = require("./users"); +const roomsDocs = require("./rooms"); const chatsDocs = require("./chats"); const { port, nodeEnv } = require("../../../loadenv"); @@ -57,6 +59,10 @@ const swaggerDocs = { name: "users", description: "유저 계정 정보 수정 및 조회", }, + { + name: "rooms", + description: "방 생성/수정/삭제/조회 및 관리 지원", + }, { name: "chats", description: "채팅 시 발생하는 이벤트 정리", @@ -71,10 +77,13 @@ const swaggerDocs = { ...usersDocs, ...authDocs, ...chatsDocs, + ...roomsDocs, }, components: { schemas: { ...reportsSchema, + ...participantSchema, + ...roomsSchema, }, }, }; diff --git a/src/routes/docs/utils.js b/src/routes/docs/utils.js index b435476b..8181ceac 100644 --- a/src/routes/docs/utils.js +++ b/src/routes/docs/utils.js @@ -1,3 +1,12 @@ const objectIdPattern = `^[a-fA-F\d]{24}$`; +const roomsPattern = { + rooms: { + name: RegExp( + "^[A-Za-z0-9가-힣ㄱ-ㅎㅏ-ㅣ,.?! _~/#'\\\\@=\"\\-\\^()+*<>{}[\\]]{1,50}$" + ), + from: RegExp("^[A-Za-z0-9가-힣 -]{1,20}$"), + to: RegExp("^[A-Za-z0-9가-힣 -]{1,20}$"), + }, +}; -module.exports = { objectIdPattern }; +module.exports = { objectIdPattern, roomsPattern }; diff --git a/src/services/rooms.js b/src/services/rooms.js index d4b7557e..a2562c67 100644 --- a/src/services/rooms.js +++ b/src/services/rooms.js @@ -110,13 +110,13 @@ const publicInfoHandler = async (req, res) => { res.send(formatSettlement(roomObject, { includeSettlement: false })); } else { res.status(404).json({ - error: "Rooms/info : id does not exist", + error: "Rooms/publicInfo : id does not exist", }); } } catch (err) { logger.error(err); res.status(500).json({ - error: "Rooms/info : internal server error", + error: "Rooms/publicInfo : internal server error", }); } }; @@ -153,7 +153,7 @@ const joinHandler = async (req, res) => { // 사용자의 참여중인 진행중인 방이 5개 이상이면 오류를 반환합니다. if (user.ongoingRoom.length >= 5) { return res.status(400).json({ - error: "Rooms/create : participating in too many rooms", + error: "Rooms/join : participating in too many rooms", }); } @@ -244,7 +244,7 @@ const abortHandler = async (req, res) => { .indexOf(user._id.toString()); if (roomPartIndex === -1) { return res.status(403).json({ - error: "Rooms/info : did not joined the room", + error: "Rooms/abort : did not joined the room", }); } @@ -254,7 +254,7 @@ const abortHandler = async (req, res) => { // 방의 출발시간이 지나고 정산이 되지 않으면 나갈 수 없음 if (isOvertime(room, req.timestamp) && userOngoingRoomIndex !== -1) { return res.status(400).json({ - error: "Rooms/info : cannot exit room. Settlement is not done", + error: "Rooms/abort : cannot exit room. Settlement is not done", }); } @@ -377,7 +377,6 @@ const searchHandler = async (req, res) => { .limit(1000) .populate(roomPopulateOption) .lean(); - res.json( rooms.map((room) => formatSettlement(room, { includeSettlement: false })) );