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