---
# System prepended metadata

title: '#N API 規格大亂鬥 - 筆記共編'
tags: [Meetups]

---

---
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!
}

```