--- title: "#N API 規格大亂鬥 - 筆記共編" tags: Meetups --- ## 參與者事前準備 這是一個待辦事項專案提供的 API 組,根據以下個別的 API,準備好自己團隊 style 的規格: 1. 取得所有 task 列表 2. 取得單一 task 項目 3. 更新 task 內容 4. 標記 task 為已完成(check) 5. 刪除 task 6. 寄信給 task 的所有者(owner) 當天就是分享各種鬼故事(?)style ## 抽籤流程 被抽到要講的內容: 1. 簡單自我介紹,至少講職位,與寫什麼語言的程式 2. 分享 API 規格 3. 目前串接遇到的問題,或是分享覺得這份規格好的地方 4. QA 時間 ### from Gson Example ```gherkin= Feature: Todo服務作業 Background: Given 目前 UTC 時間為 "2022-01-01T01:01:01Z" Scenario: 新增一筆 Todo Given 前端已準備 HttpRequest Body 參數 | Name | | 寫作業 | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "POST" 請求和以上參數資料至 "api/todos" Then 預期回傳 todo 資料如下 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Then 預期在資料庫的 todo 資料表應存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Scenario: 取得 Active Todo 列表 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T01:01:01Z | TestUser | | 2 | 畫畫 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 前端已準備 HttpRequest QueryString 參數 | state | | IsActive | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "GET" 請求和以上參數資料至 "api/todos" Then 預期回傳 todo 資料如下 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 2 | 畫畫 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Scenario: 取得 Completed Todo 列表 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T01:01:01Z | TestUser | | 2 | 畫畫 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 前端已準備 HttpRequest QueryString 參數 | state | | IsCompleted | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "GET" 請求和以上參數資料至 "api/todos" Then 預期回傳 StatusCode "200" 及以下 todo | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T01:01:01Z | TestUser | Scenario: 將 Todo 標示為已完成 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 2 | 畫畫 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 目前 UTC 時間為 "2022-01-02T02:02:02Z" Given 前端已準備 HttpRequest Path 參數 | id | | 2 | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "POST" 請求和以上參數資料至 "api/todos/{id}/complete" Then 預期回傳 StatusCode "200" 及以下 todo | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 2 | 畫畫 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T02:02:02Z | TestUser | Then 預期在資料庫的 todo 資料表應存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 2 | 畫畫 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T02:02:02Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Scenario: 將 Todo 標示為未完成 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 2 | 畫畫 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T02:02:02Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 目前 UTC 時間為 "2022-01-03T03:03:03Z" Given 前端已準備 HttpRequest Path 參數 | id | | 2 | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "POST" 請求和以上參數資料至 "api/todos/{id}/activate" Then 預期回傳 StatusCode "200" 及以下 todo | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 2 | 畫畫 | IsActive | 3 | 2022-01-01T01:01:01Z | TestUser | 2022-01-03T03:03:03Z | TestUser | Then 預期在資料庫的 todo 資料表應存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 2 | 畫畫 | IsActive | 3 | 2022-01-01T01:01:01Z | TestUser | 2022-01-03T03:03:03Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Scenario: 將已完成的 Todo 再次標示為已完成 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 2 | 畫畫 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T02:02:02Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 目前 UTC 時間為 "2022-01-03T03:03:03Z" Given 前端已準備 HttpRequest Path 參數 | id | | 2 | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "POST" 請求和以上參數資料至 "api/todos/{id}/complete" Then 預期回傳 StatusCode "200" 及以下 todo | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 2 | 畫畫 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T02:02:02Z | TestUser | Then 預期在資料庫的 todo 資料表應存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | | 2 | 畫畫 | IsCompleted | 2 | 2022-01-01T01:01:01Z | TestUser | 2022-01-02T02:02:02Z | TestUser | | 3 | 健身 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Scenario: 對不存在的 Todo 標示為已完成 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 目前 UTC 時間為 "2022-01-02T02:02:02Z" Given 前端已準備 HttpRequest Path 參數 | id | | 2 | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "POST" 請求和以上參數資料至 "api/todos/{id}/complete" Then 預期回傳 StatusCode "404" 及以下 Payload | Code | Message | | 2 | The todo(2) was not found. | Scenario: 對不存在的 Todo 標示為未完成 Given 資料庫的 todo 資料表已存在以下資料 | Id | Name | State | Version | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | | 1 | 寫作業 | IsActive | 1 | 2022-01-01T01:01:01Z | TestUser | 2022-01-01T01:01:01Z | TestUser | Given 目前 UTC 時間為 "2022-01-02T02:02:02Z" Given 前端已準備 HttpRequest Path 參數 | id | | 2 | And 前端已準備 HttpRequest Header 參數 | user-id | | TestUser | When 送出 "POST" 請求和以上參數資料至 "api/todos/{id}/activate" Then 預期回傳 StatusCode "404" 及以下 Payload | Code | Message | | 2 | The todo(2) was not found. | ``` https://app.swaggerhub.com/apis/yuanyu90221/task/1.0.0 ```yaml= openapi: 3.0.0 servers: # Added by API Auto Mocking Plugin - description: SwaggerHub API Auto Mocking url: https://virtserver.swaggerhub.com/yuanyu90221/task/1.0.0 info: description: This is a task API version: "1.0.0" title: Task Management API contact: email: yuanyu90221@gmail.com license: name: Apache 2.0 url: 'http://www.apache.org/licenses/LICENSE-2.0.html' tags: - name: developers description: Operations available to regular developers paths: /tasks: get: tags: - developers summary: get task list description: get task list parameters: - in: query name: limit required: true schema: type: integer format: int64 default: 10 - in: query name: offset schema: type: integer format: int64 default: 0 responses: '400': description: 'invalid input, object invalid' '409': description: 'an existing item already exists' '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/TasksResponse' post: tags: - developers summary: create new tasks by owner description: create new tasks by owner requestBody: content: application/json: schema: $ref: '#/components/schemas/TaskMeta' responses: '400': description: 'invalid input, object invalid' '201': description: task creatted /tasks/{id}: get: tags: - developers summary: get task by id description: get specific task parameters: - in: path name: id required: true schema: type: integer format: int64 responses: '404': description: 'request resource not found' '400': description: 'invalid input, object invalid' '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/Task' put: tags: - developers summary: update task by id description: update specific task parameters: - in: path name: id required: true schema: type: integer format: int64 requestBody: content: application/json: schema: $ref: '#/components/schemas/TaskUpdateMeta' responses: '404': description: 'request resource not found' '400': description: 'invalid input, object invalid' '201': description: task updated delete: tags: - developers summary: delete task by id description: deleete specific task parameters: - in: path name: id required: true schema: type: integer format: int64 responses: '404': description: 'request resource not found' '400': description: 'invalid input, object invalid' '201': description: task deleted /tasks/{id}/send_email: post: tags: - developers summary: send email to owner by task id description: send email to email to specific task parameters: - in: path name: id required: true schema: type: integer format: int64 responses: '404': description: 'request resource not found' '400': description: 'invalid input, object invalid' '201': description: email sended components: schemas: PageInfo: type: object properties: total: type: integer description: total offset: type: integer description: offset limit: type: integer description: limit TasksResponse: type: object properties: pageInfo: $ref: '#/components/schemas/PageInfo' tasks: type: array items: $ref: '#/components/schemas/Task' Owner: type: object properties: name: type: string description: Owner Name email: type: string format: email description: Owner email required: - name - email TaskStatus: type: string description: Task Status example: "INITAL" enum: - INITIAL - DOING - DONE TaskMeta: type: object properties: name: type: string description: type: string owner: $ref: '#/components/schemas/Owner' status: $ref: '#/components/schemas/TaskStatus' required: - name - owner - status - description TaskUpdateMeta: type: object properties: name: type: string description: type: string owner: $ref: '#/components/schemas/Owner' status: $ref: '#/components/schemas/TaskStatus' Task: type: object properties: id: type: integer format: int64 name: type: string description: type: string owner: $ref: '#/components/schemas/Owner' createdAt: type: string format: date description: Creation date example: "2022-01-30" updatedAt: type: string format: date description: Creation date example: "2022-03-30" status: $ref: '#/components/schemas/TaskStatus' required: - id - name - description - owner - createdAt - updatedAt - status ``` ### Graphql ```graphql= # Exposes a URL that specifies the behaviour of this scalar. directive @specifiedBy( # The URL that specifies the behaviour of this scalar. url: String! ) on SCALAR scalar DateTime type Mutation { updateTask(id: ID!, description: String, name: String, ownerId: String, status: TASK_STATUS): Boolean! deleteTask(id: ID!): Boolean! sendEmailToTaskOwner(id: ID!): Boolean! } type Query { task(id: ID!): Task! tasks(limit: Int=10, offset: Int=0): TaskList! } enum TASK_STATUS { INITIAL DOING DONE } type PageInfo { total: Int! hasNext: Boolean! hasPrev: Boolean! } type Owner { id: ID! email: String! name: String! } type Task { id: ID! name: String! owner: Owner! status: TASK_STATUS! description: String! updatedAt: DateTime! createdAt: DateTime! } type TaskList { id: ID! tasks: [Task!]! pageInfo: PageInfo! } ```