From c37c21b3fa115993a90a2234380f496d714522da Mon Sep 17 00:00:00 2001 From: Hiroki Takeda Date: Wed, 15 Nov 2023 15:13:52 +0900 Subject: [PATCH 01/19] feat: migrate from GitLab --- .../OpenAPI_Specification_3.0.3.md | 492 +++++++++++++++++- 1 file changed, 480 insertions(+), 12 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index c43110a6..ea7d965d 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -9,31 +9,499 @@ meta: -本コーディング規約は、世の中のシステム開発プロジェクトのために無償で提供致します。 -ただし、掲載内容および利用に際して発生した問題、それに伴う損害については、フューチャー株式会社は一切の責務を負わないものとします。 +本コーディング規約は、世の中のシステム開発プロジェクトのために無償で提供致します。 +ただし、掲載内容および利用に際して発生した問題、それに伴う損害については、フューチャー株式会社は一切の責務を負わないものとします。 また、掲載している情報は予告なく変更することがございますので、あらかじめご了承下さい。 # はじめに -## 前提条件 +[OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則ったAPIドキュメントを記述する際のコーディング規約をまとめます。古いバージョンとして[OpenAPI Specification 2.0の規約](OpenAPI_Specification_2.0.md)がありますので、v2をご利用の方はこちらをご参照ください。 -## 全体規約 +## 適用箇所 -## 要素規約 +本規約は以下の[前提条件](prerequisite.md)で作られたものである。 -### info +## Web API 自体の設計について -### host +[API 設計標準](API_Design.md) に準じる。 -### security +## ファイルフォーマット -### paths +[ファイルフォーマット規約](yaml_standards.md)に準じる。 -### tags +## ファイル単位 -### externalDocs +OpenAPI ドキュメントは単一のファイルで構成することも複数の分割されたファイルで構成することもできるが、**複数のファイルに分割する**ことを推奨する。 +理由は下記の通りである。 -### components +- XXX +- XXX + +(相談事項) + +- 分割のパターンがあるのであれば、いくつかケースに応じてパターン分けして記載する。 +- ファイルの命名についてもここで触れる + +# OpenAPI ドキュメントの構成要素 + +OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ、ルートオブジェクトは下記の要素で構成される。 +各種規約を読み進めるにあたってあらかじめ大まかに理解しておくことを推奨する。 +各オブジェクトの詳細については[公式ドキュメント](https://spec.openapis.org/oas/v3.0.3#openapi-object)を参照されたい。 + +| フィールド名 | 必須 | 説明 | +| ------------ | :---: | -------------------------------------------------------- | +| openapi | ○ | OpenAPIドキュメントが使用するOpenAPI仕様のバージョン番号 | +| info | ○ | APIに関するメタデータ | +| servers | | APIサーバへの接続情報 | +| paths | ○ | APIの利用可能なパスと操作方法 | +| components | | 複数のAPIにおける共通の定義 | +| security | | API全体で利用可能なセキュリティ(認証)機構 | +| tags | | 各種APIをグルーピングするためのタグ | +| externalDocs | | 追加の外部ドキュメント | + +## コンポーネント化 + +(相談事項) + +- どの単位でコンポーネント化をするか、については個別の規約に入る前に全体として触れておいた方がいいと思う。 +- 結構難しいネタ + +# 要素規約 + +先述した OpenAPI ドキュメントを構成する要素別に具体的なコーディング規約を記載する。 + +## openapi + +OpenAPI ドキュメントが使用する OpenAPI 仕様のセマンティックバージョン番号を記載する。 +本規約は`3.0.3`を対象としているため、`3.0.3`とする。 + +良い例: + +```yaml +openapi: 3.0.3 +``` + +悪い例: + +```yaml +openapi: 3.0 +``` + +## info + +`info`オブジェクトにはWeb APIに関するメタデータを記載する。 +`title`, `description`, `version` を必須項目とする。 + + +| フィールド名 | 必須 | 記載内容 | +| -------------- | :---: | ------------------------------ | +| title | ○ | Web APIの総称 | +| description | ○ | Web APIの簡単な説明 | +| version | ○ | OpenAPI ドキュメントのバージョン | +| termsOfService | | 利用規約のURL | +| contact | | 連絡先情報 | +| license | | ライセンス情報 | + +### title + +WebAPIの総称を記載する。システム名やサービス名 + API のような命名とすることを推奨する。 +例. `X System API` + +### desctiption + +Web APIが提供する機能の概要・想定する利用者やユースケース・制約などを記載する。 + +### version + +サービスが採用するバージョン管理と同期を取る。 + +サービスのバージョン管理と同期をとることが重要である、本規約では形式は任意とする。 +例えば `major.minor.patch` のようなセマンティックバージョニングや `1.0-beta` や `2023.03.26` といった形式を許容する。 + +## servers + +Web APIを提供するサーバの情報を記載する。 +`url`, `description` を必須項目とし、ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。 +ただしLSUDs向けのWeb API開発においては本番環境のURLを不用意に公開したくないケースが多く、記載は避けるべきである。 + +良い例: + +```yaml +servers: +- url: http://localhost:8001/ + description: Localhost Server +- url: https://dev.api.example.com/v1 + description: Development Server +- url: https://staging.api.example.com/v1 + description: Staging Server +``` + +悪い例: +```yaml +servers: +- url: https://prod.api.example.com/v1 + description: Production Server +``` + +## paths + +### operationId + +### description + +### parameters + +### responses + +## components + +## security + +## tags + +## externalDocs + +# 設計上のポイント + +(相談事項) + +- 要素規約が「どのように書くか」に焦点を当てているのに対し、そもそも「何を書くか」といった部分については、切り出して要素規約から refer させる形の方がスッキリするかも。要素規約に入れられるならそれでよし。描きながらバランス見て。 + +## バリデーションをどこまで厳密に定義するか + +## 値が存在しないという状態の表現 + +#### undefined と null + +- リクエスト/レスポンスにおいて、ある項目の値が存在しないという状態を表現する場合、①その項目自体を含めず `undefined` とする方法と、②値に `null` を使用する方法がある。 + + ① `undefined` とする場合、OpenAPI定義とJSONデータの例 + + ```yaml + application/json: + schema: + type: object + properties: + id: + type: string + name: + type: string + required: + - id + ``` + ```jsonc + {"id": "00001"} //nameの値が存在しない + ``` + + ```jsonc + {} //idの値も存在しない場合、idは項目必須であるためバリデーションエラーとなる + ``` + + ② 値に `null` を使用する場合、OpenAPI定義(OpenAPI3.0)とJSONデータの例 + + ```yaml + application/json: + schema: + type: object + properties: + id: + type: string + nullable: true + name: + type: string + nullable: true + required: + - id + ``` + + ```jsonc + {"id": "00001", "name": null} //nameの値が存在しない + ``` + + ```jsonc + {"id": null, "name": null} //id, nameとも値が存在しない + ``` + + ```jsonc + {} //この場合は、required指定の、id項目がないためバリデーションエラーとなる + ``` + +- 原則としては、①`undefined` による定義を使用する方が、API仕様の表現が煩雑にならず、また通信サイズの点からも有利である。 + +#### 差分更新APIの場合 + +- 差分更新(PATCH)APIにおいては、項目が更新対象外であることと、項目が更新してクリアする対象であることを明確に区別する必要がある。このような場合には、以下のいずれかの方法を採用する。 + + 1)項目を文字列として定義し、空値として空文字("")を採用する。 + - floatやint等の数値についても、文字列型として定義することで、項目が `undefined` または `null` であれば「更新対象外」、項目が空文字であれば「更新してクリアする対象」であると判定できる。 + - この場合、空文字はOpenAPIの`format`定義に許容されないため注意が必要である。`format`定義の代わりに、`pattern`に空文字を許容する正規表現を定義する必要がある。 +
+ + 2)カスタムモデルを使用する + - APIで受け取るデータをデシリアライズする際に、`undefined` と `null` を区別できるようなカスタムモデルを作成すると、1)のように数値を文字列型として扱う必要がなく、OpenAPIの`format` 定義を使用することも可能である。 + - この場合、OpenAPIからの自動生成モデルにはカスタマイズが必要となる。 + - カスタムモデルの例 (参照元: [技術ブログ](https://future-architect.github.io/articles/20211028b/#プログラムの観点)) + + +#### 各言語による表現の違い + +
+ Golang + + - Golangの場合、以下のように、`nil` になり得る項目はポインタ型とした上で、`undefined` となり得る項目は `omitempty` タグを付与する。 + + ```go + type Data struct { + NotNullableString1 string `json:"not_nullable_string_1"` + NotNullableString2 string `json:"not_nullable_string_2"` + NullableString1 *string `json:"nullable_string_1"` + NullableString2 *string `json:"nullable_string_2,omitempty"` + NullableString3 *string `json:"nullable_string_3"` + NullableString4 *string `json:"nullable_string_4,omitempty"` + NotNullableInt1 int64 `json:"not_nullable_int_1"` + NotNullableInt2 int64 `json:"not_nullable_int_2"` + NullableInt1 *int64 `json:"nullable_int_1"` + NullableInt2 *int64 `json:"nullable_int_2,omitempty"` + NullableInt3 *int64 `json:"nullable_int_3"` + NullableInt4 *int64 `json:"nullable_int_4,omitempty"` + } + + // Set values + data := Data{ + NotNullableString1: "value", + NullableString1: nil, + NullableString2: nil, + NotNullableInt1: 1, + NullableInt1: nil, + NullableInt2: nil, + } + // Serialize + jsonString, _ := json.Marshal(data) + fmt.Println(string(jsonString)) + buf := bytes.NewBuffer(jsonString) + + decoder := json.NewDecoder(buf) + var v Data + // Deserialize + decoder.Decode(&v) + fmt.Printf("After decoded: %#v\n", v) + ``` + + - シリアライズ後のjsonを見ると、値がセットされない場合には、項目にゼロ値(ポインタ型は`nil`, string型は空文字、int型は`0`)が入っている。 + - 項目がゼロ値の場合に`omitempty` が付与されていると、 項目ごと除外されている(`undefined` となっている)。 + + ```json + { + "not_nullable_string_1": "value", + "not_nullable_string_2": "", + "nullable_string_1": null, + "nullable_string_3": null, + "not_nullable_int_1": 1, + "not_nullable_int_2": 0, + "nullable_int_1": null, + "nullable_int_3": null + } + ``` + + - デシリアライズ後の構造体を見ると、jsonの項目が`undefined`であっても `null` であっても、`nil` として保持されている。 + + ```go + After decoded: + Data{ + NotNullableString1:"value", + NotNullableString2:"", + NullableString1:(*string)(nil), + NullableString2:(*string)(nil), + NullableString3:(*string)(nil), + NullableString4:(*string)(nil), + NotNullableInt1:1, + NotNullableInt2:0, + NullableInt1:(*int64)(nil), + NullableInt2:(*int64)(nil), + NullableInt3:(*int64)(nil), + NullableInt4:(*int64)(nil) + } + ``` +
+ + +
+ Java + + + - Javaの場合、`int` や `double` などのプリミティブ型は `null` になれないため、`nullable` にするためには、それぞれのラッパークラスである参照型(`Integer`, `Double` など)を使用する必要がある。 + - jsonにシリアライズ後に`null` の項目を保持するか否かは、例えば、[Jacksonライブラリ](https://github.com/FasterXML)を用いて以下のように区別される。 + + + ```java + public class Data { + public Data(){}; + public Data(String str1, String str2, int notNullableInt){ + this.nullableString1 = str1; + this.nullableString2 = str2; + this.notNullableInt = notNullableInt; + }; + @JsonInclude(JsonInclude.Include.ALWAYS) + private String nullableString1; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String nullableString2; + + private int notNullableInt; + + // Setters + public void setNullableString1(String nullableString1) { + this.nullableString1 = nullableString1; + } + public void setNullableString2(String nullableString2) { + this.nullableString2 = nullableString2; + } + public void setNotNullableInt(int notNullableInt) { + this.notNullableInt = notNullableInt; + } + // Getters + public String getNullableString1() { + return nullableString1; + } + public String getNullableString2() { + return nullableString2; + } + public int getNotNullableInt() { + return notNullableInt; + } + } + ``` + + ```java + // Set nothing to the fields. + Data dataWithNothing = new Data(); + // Set intial values to the fields. + Data dataWithInitialValues = new Data(null,null,0); + // Set values to the fields. + Data dataWithValues = new Data("","",1); + + List dataList = Arrays.asList(dataWithNothing, dataWithInitialValues, dataWithValues); + ObjectMapper mapper = new ObjectMapper(); + for(Data d : dataList){ + // Serialize + String json = mapper.writeValueAsString(d); + System.out.println(json); + + // Deserialize + Data deserialized = mapper.readValue(json, Data.class); + System.out.println(ToStringBuilder.reflectionToString(deserialized, ToStringStyle.SHORT_PREFIX_STYLE)); + } + ``` + + - シリアライズ後のjsonを見ると、参照型`String`の初期値は`null`、プリミティブ型`int`の初期値は`0`となっている。 + - `@JsonInclude(JsonInclude.Include.ALWAYS)` アノテーションを付与した項目は、値が`null`の場合でも項目が保持される。 + - `@JsonInclude(JsonInclude.Include.NON_NULL)` アノテーションを付与した項目は、値が`null`の場合には項目ごと除外されている(`undefined`となっている)。 + + ```json + { + "nullableString1": null, + "notNullableInt": 0 + } + + { + "nullableString1": null, + "notNullableInt": 0 + } + + { + "nullableString1": "", + "nullableString2": "", + "notNullableInt": 1 + } + ``` + + - デシリアライズ後のオブジェクトを見ると、jsonの項目が`undefined`であっても `null` であっても、`null` として保持されている。 + + ```java + Data[nullableString1=,nullableString2=,notNullableInt=0] + + Data[nullableString1=,nullableString2=,notNullableInt=0] + + Data[nullableString1=,nullableString2=,notNullableInt=1] + ``` +
+ +
+ TypeScript + + - TypeScriptの場合、以下のように、`null`, `undefined` を区別して定義することが可能である。Optional指定(項目名に`?`を付与)することで`undefined`をセットする必要がなくなる(項目が「非必須」となる)。 + + ```typescript + interface Data { + nullable_string1: string; + nullable_string2: string; + nullable_string3: string; + nullable_string4: string; + nullable_string5?: string; + nullable_num1: number; + nullable_num2: number; + nullable_num3: number; + nullable_num4: number; + nullable_num5?: number; + } + + const body: Data = { + nullable_string1: 'value1', + nullable_string2: '', + nullable_string3: null, + nullable_string4: undefined, // 定義しないとエラーとなる + nullable_num1: 1, + nullable_num2: 0, + nullable_num3: null, + nullable_num4: undefined, // 定義しないとエラーとなる + } + var jsonString = JSON.stringify(body) + console.log(jsonString) + + const deserialized: Data = JSON.parse(jsonString); + console.log(deserialized) + ``` + + - シリアライズ後のjsonを見ると、`undefined`定義した項目は除外されている。 + + ```json + { + "nullable_string1": "value1", + "nullable_string2": "", + "nullable_string3": null, + "nullable_num1": 1, + "nullable_num2": 0, + "nullable_num3": null + } + ``` + + - デシリアライズ後のオブジェクトを見ると、jsonの項目が`null` の場合にのみ`null` として保持されており、項目のない場合と区別されている。 + + ```typescript + nullable_string1: "value1" + nullable_string2: "" + nullable_string3: null + nullable_num1: 1 + nullable_num2: 0 + nullable_num3: null + ``` + +
+ +#### 参照リンク + +- `undefined` と `null` の使い方について詳細な解説は、[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい。 +- OpenAPI定義をDB定義に対応させることにより、異なるAPI間で整合のとれた処理設計をすることがのぞましい。DB定義とOpenAPI定義の対応例は、[DB定義とOpenAPI定義のマッピング](./reference/DB_OpenAPI_Mapping_Example.md)を参照されたい。 + + + +# 各種ツール、サービスとの統合 + +特定のツール、サービスに依存する拡張系 + +## oapi-codegen + +## Amazon API Gateway --- From a21cfb0e9aa45cd2f1097cee9660ee0a922425c3 Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Thu, 16 Nov 2023 15:21:48 +0900 Subject: [PATCH 02/19] =?UTF-8?q?v2=E3=81=8B=E3=82=89=E6=8C=AF=E3=82=8A?= =?UTF-8?q?=E4=B8=8B=E3=82=8D=E3=81=97=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OpenAPI_Specification_2.0.md | 2 +- .../OpenAPI_Specification_3.0.3.md | 763 +++++++++++++----- 2 files changed, 544 insertions(+), 221 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md index c0d76eee..0740a0bc 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md @@ -27,7 +27,7 @@ meta: ## ファイルフォーマット -[ファイルフォーマット規約](yaml_standards.md)に準じる。 +[ファイルフォーマット規約](file_standards.md)に準じる。 # 要素規約 diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index ea7d965d..f9cd6b1b 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -15,7 +15,7 @@ meta: # はじめに -[OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則ったAPIドキュメントを記述する際のコーディング規約をまとめます。古いバージョンとして[OpenAPI Specification 2.0の規約](OpenAPI_Specification_2.0.md)がありますので、v2をご利用の方はこちらをご参照ください。 +[OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則った API ドキュメントを記述する際のコーディング規約をまとめます。古いバージョンとして[OpenAPI Specification 2.0 の規約](OpenAPI_Specification_2.0.md)がありますので、v2 をご利用の方はこちらをご参照ください。 ## 適用箇所 @@ -27,20 +27,7 @@ meta: ## ファイルフォーマット -[ファイルフォーマット規約](yaml_standards.md)に準じる。 - -## ファイル単位 - -OpenAPI ドキュメントは単一のファイルで構成することも複数の分割されたファイルで構成することもできるが、**複数のファイルに分割する**ことを推奨する。 -理由は下記の通りである。 - -- XXX -- XXX - -(相談事項) - -- 分割のパターンがあるのであれば、いくつかケースに応じてパターン分けして記載する。 -- ファイルの命名についてもここで触れる +[ファイルフォーマット規約](file_standards.md)に準じる。 # OpenAPI ドキュメントの構成要素 @@ -48,16 +35,16 @@ OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ 各種規約を読み進めるにあたってあらかじめ大まかに理解しておくことを推奨する。 各オブジェクトの詳細については[公式ドキュメント](https://spec.openapis.org/oas/v3.0.3#openapi-object)を参照されたい。 -| フィールド名 | 必須 | 説明 | -| ------------ | :---: | -------------------------------------------------------- | -| openapi | ○ | OpenAPIドキュメントが使用するOpenAPI仕様のバージョン番号 | -| info | ○ | APIに関するメタデータ | -| servers | | APIサーバへの接続情報 | -| paths | ○ | APIの利用可能なパスと操作方法 | -| components | | 複数のAPIにおける共通の定義 | -| security | | API全体で利用可能なセキュリティ(認証)機構 | -| tags | | 各種APIをグルーピングするためのタグ | -| externalDocs | | 追加の外部ドキュメント | +| フィールド名 | 必須 | 説明 | +| ------------ | :--: | ----------------------------------------------------------- | +| openapi | ○ | OpenAPI ドキュメントが使用する OpenAPI 仕様のバージョン番号 | +| info | ○ | API に関するメタデータ | +| servers | | API サーバへの接続情報 | +| paths | ○ | API の利用可能なパスと操作方法 | +| components | | 複数の API における共通の定義 | +| security | | API 全体で利用可能なセキュリティ(認証)機構 | +| tags | | 各種 API をグルーピングするためのタグ | +| externalDocs | | 追加の外部ドキュメント | ## コンポーネント化 @@ -89,78 +76,401 @@ openapi: 3.0 ## info -`info`オブジェクトにはWeb APIに関するメタデータを記載する。 +`info`オブジェクトには Web API に関するメタデータを記載する。 `title`, `description`, `version` を必須項目とする。 - -| フィールド名 | 必須 | 記載内容 | -| -------------- | :---: | ------------------------------ | -| title | ○ | Web APIの総称 | -| description | ○ | Web APIの簡単な説明 | -| version | ○ | OpenAPI ドキュメントのバージョン | -| termsOfService | | 利用規約のURL | -| contact | | 連絡先情報 | -| license | | ライセンス情報 | +| フィールド名 | 必須 | 記載内容 | +| -------------- | :--: | -------------------------------- | +| title | ○ | Web API の総称 | +| description | ○ | Web API の簡単な説明 | +| version | ○ | OpenAPI ドキュメントのバージョン | +| termsOfService | | 利用規約の URL | +| contact | | 連絡先情報 | +| license | | ライセンス情報 | ### title -WebAPIの総称を記載する。システム名やサービス名 + API のような命名とすることを推奨する。 +WebAPI の総称を記載する。システム名やサービス名 + API のような命名とすることを推奨する。 例. `X System API` ### desctiption -Web APIが提供する機能の概要・想定する利用者やユースケース・制約などを記載する。 +Web API が提供する機能の概要・想定する利用者やユースケース・制約などを記載する。 ### version -サービスが採用するバージョン管理と同期を取る。 +この API 仕様のドキュメントのバージョンを記載する。アプリケーションのバージョン(git tag やリリースで管理するようなバージョン)とは別である。 -サービスのバージョン管理と同期をとることが重要である、本規約では形式は任意とする。 -例えば `major.minor.patch` のようなセマンティックバージョニングや `1.0-beta` や `2023.03.26` といった形式を許容する。 +本規約の推奨は `major.minor` 形式である。 `0.1 `固定で開発を進め、サービスのリリース時に `1.0` とし、その後の項目やオプション、パスの追加ごとに `1.1` などインクリメントしていく。もし他チームへのドキュメントの頻繁な共有が必要だれば、`1.0` のかわりに `2023.03.26` といった形式も許容する。 ## servers -Web APIを提供するサーバの情報を記載する。 -`url`, `description` を必須項目とし、ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。 -ただしLSUDs向けのWeb API開発においては本番環境のURLを不用意に公開したくないケースが多く、記載は避けるべきである。 +Web API を提供するサーバの情報を記載する。 +`url`, `description` を必須項目とする。 + +| フィールド名 | 必須 | 記載内容 | +| ------------ | :--: | ---------- | +| url | ○ | 対象の URL | +| description | ○ | 説明 | +| variables | | なし | + +ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。 ただし LSUDs 向けの Web API 開発においては本番環境の URL を不用意に公開したくないケースが多く、記載は避けるべきである。 良い例: ```yaml servers: -- url: http://localhost:8001/ - description: Localhost Server -- url: https://dev.api.example.com/v1 - description: Development Server -- url: https://staging.api.example.com/v1 - description: Staging Server + - url: http://localhost:8001/ + description: Localhost Server + - url: https://dev.api.example.com/v1 + description: Development Server + - url: https://staging.api.example.com/v1 + description: Staging Server ``` 悪い例: + ```yaml servers: -- url: https://prod.api.example.com/v1 - description: Production Server + - url: https://prod.api.example.com/v1 + description: Production Server ``` ## paths +API エンドポイント定義 + +- 規約 + - [ ] paths 以下エンドポイントは機能 ID の昇順に定義 + - [ ] HTTP メソッドは `GET`,`POST`,`PUT`,`PATCH`,`DELETE` の順に定義 + +```yml +paths: + /product: # エンドポイント + get: # HTTPメソッド + ... +``` + +| フィールド名 | 必須 | 記載内容 | +| ------------ | :--: | -------- | +| tags | ○ | | +| operationId | ○ | | +| summary | ○ | | +| description | ○ | | +| response | ○ | | +| parameters | | | +| requestBody | | | +| security | | | + ### operationId +v2 だと以下のように記載している。 + +コード生成で利用される項目なので、必須で指定する + +- 原則、`camelCase` の `${HTTPメソッド}${機能物理名}` で記載する(例: getUser, postUser, deleteUser) + +* ※ Spotlight Studio による自動生成に準拠すると、ケバブケース + + - [ ] `${HTTPメソッド}-${URLパス1}-${URLパス2}..` 形式で定義 + - [ ] ケバブケースで定義 + ### description +`必須` +​ + +- API の機能概要を記載 + ​ + +```yaml +description: IDを指定して商品情報を取得する。 +``` + ### parameters +`任意` +​ + +- リクエストパラメータの定義 +- 規約 + - [ ] HTTP メソッドが GET,DELETE 時に利用 + - [ ] パスパラメータ、クエリパラメータ、ヘッダパラメータを指定 + - [ ] パラメータ名はスネークケースで定義 + ​ + +```yaml +parameters: + - in: query # パラメータ配置 (path, query, header 指定可) + name: product_id # パラメータ名(物理名) + description: プロダクトID # 論理名 + schema: # パラメータスキーマ定義 + type: string + ... +``` + +### requestBody + +​ +`任意` +​ + +- API のリクエストボディを定義 +- 規約 + - [ ] HTTP メソッドが POST,PUT,PATCH 時に利用 + - [ ] リクエストボディが必須の場合は `requestBody` 直下の `required: true` を指定 + - [ ] スキーマ定義は components として schemas 以下に定義し、 `$ref` で参照 + - [ ] スキーマ名は `Req` をプレフィックスに付与し、`operation_id` をアッパーキャメルケースへ変換の上で定義 \* 例) `operation_id=post-product` => `ReqPostProduct` + ​ + +```yaml +operation_id: post-product +requestBody: + required: true + content: + application/json: # media typeを指定 + schema: + $ref: '#/components/schemas/ReqPostProduct' # リクエストボディのスキーマ構造指定 +​ +... +​ +components: + schemas: + ReqPostProduct: + type: object + properties: + ... +``` + +​ + ### responses +`必須` +​ + +- ステータスコード別の API レスポンススキーマを定義 +- 規約 + - [ ] スキーマ定義は `components.schemas` 以下に定義し、 `$ref` で参照 + - [ ] スキーマ定義の第一階層の type は `object` 必須 + - 設計書の `3.戻り値` 定義の第一階層に `object` の追加必須 ([補足: 設計書の戻り値との整合性](#補足-設計書の戻り値との整合性)参照) + - [ ] スキーマ名は `Res` をプレフィックスに付与し、`operation_id` をアッパーキャメルケースへ変換の上で定義 + - 例) `operation_id=post-product` => `ResPostProduct` + - [ ] 異常系(`4xx`,`5xx`)の定義は共通のため、以下記載の方式で定義 + - [ ] ステータスコード`200`,`201` でレスポンススキーマが定義されている場合は、 example 名が `default` の定義必須 + - [ ] example のケースを追加したい場合は `./examples` ディレクトリ配下にファイルを配置して、`$ref` で参照 + ​ + +```yml +operation_id: post-product +responses: + '200': + description: プロダクト一覧 + content: + application/json: + schema: # スキーマ定義 + $ref: '#/components/schemas/ResPostProduct' + examples: # サンプルレスポンス + default: # サンプル名 + value: + ... # サンプル定義 + example-1: + $ref: './examples/post-product.example.1.yaml' + # 以下はデフォルトで指定 + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + # POST の場合のみ利用 + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '500': + $ref: '#/components/responses/InternalServer' + '503': + $ref: '#/components/responses/ServiceUnavaliable' +​ +... +​ +components: + schemas: + ResPostProduct: + type: object + properties: + products: + type: array + items: + $ref: '#/components/schemas/Product' + Product: + type: object + properties: + ... + BasicError: + type: object + properties: + ... + responses: + BadRequest: + description: リクエストデータ不正 + content: + application/json: + schema: + "$ref": "#/components/schemas/BasicError" + ... +``` + ## components +​ +API 定義で利用する共通のデータモデルを定義 +​ + +```yml +components: + schemas: ... + securitySchemes: ... + responses: ... + parameters: ... +``` + + +### schemas + +* API 定義で共通で利用するスキーマを定義 +* 規約 + * [ ] リソース名はアッパーキャメルケースで定義 + * [ ] リソース名は単数形で定義 + * [ ] `type` に複数の型定義の指定不可 + * [ ] `type: null`は原則として利用しない + * [ ] `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない + * [ ] schemas 以下は [リクエストボディ](#リクエストボディ)、[レスポンス](#レスポンス)、[リソース](#リソース) の順に定義 + +```yaml +Pagination: + type: object + properties: + total_counts: + type: integer + offset: + type: integer + limit: + type: integer + required: + - total_counts + - offset + - limit +``` + +スキーマ定義のモデルは以下3種類 + +* [リクエストボディ](#リクエストボディ) +* [レスポンス](#レスポンス) +* [リソース](#リソース) + +```yaml +# ソート順は以下の通り +components: + schemas: + ReqGetProduct: + type: object + ... + ReqPostProduct: + type: object + ... + ResGetProduct: + type: object + ... + ResPostProduct: + type: object + ... + Product: + type: object + ... +``` + + +#### parameters(components) + +API 共通で利用するクエリパラメータを定義 + +* 規約 + * [ ] API 全体で利用可能な共通のクエリパラメータを定義 (例: 検索数のlimit,offset) + +```yml +parameters: + limit: + name: limit + in: query + required: false + schema: + type: integer + description: 検索数上限 + +# 利用方法 +paths: + get: + /products: + parameters: + - $ref: '#/components/parameters/limit' +``` + + ## security +- API の認証方式の設定 +- 規約 + - [ ] API 共通で設定済みのため設定しない + - [ ] 認証を通す必要のない API を定義する場合にのみ定義 + ​ + +```yml +# 認証設定方法 (デフォルトで設定済みの為不要) +security: + - Bearer: [] +​ +# 認証しない場合 +security: [] +``` + ## tags +タグを用いて、API 操作をグループ化することができる。ドキュメントやツールにとって非常に重要であるため、 **必須** で指定する。 + +- Swagger UI(HTML ドキュメント)の順序を制御できる + - 未指定の場合は、登場順で生成されてしまう +- 命名は、 **単数形** で、小文字かつ半角スペース区切り で記載する + - コード生成で利用され、Go ではパッケージ名や TypeScript の Class 単位となるため、シンプルな命名にする + - HTML ドキュメントで参照する場合の可読性を上げるため、単語を半角スペース区切りとする +- タグごとに `description` も必須で記載する + +```yaml +# NG +tags: + - name: product + description: 製品 + - name: store + description: 店舗 + - name: user account + description: ユーザーアカウント + +# NG +tags: + - name: products + - name: stores + - name: user_account + - name: UserAccount +``` + + ## externalDocs + + # 設計上のポイント (相談事項) @@ -173,9 +483,9 @@ servers: #### undefined と null -- リクエスト/レスポンスにおいて、ある項目の値が存在しないという状態を表現する場合、①その項目自体を含めず `undefined` とする方法と、②値に `null` を使用する方法がある。 +- リクエスト/レスポンスにおいて、ある項目の値が存在しないという状態を表現する場合、① その項目自体を含めず `undefined` とする方法と、② 値に `null` を使用する方法がある。 - ① `undefined` とする場合、OpenAPI定義とJSONデータの例 + ① `undefined` とする場合、OpenAPI 定義と JSON データの例 ```yaml application/json: @@ -189,15 +499,16 @@ servers: required: - id ``` + ```jsonc - {"id": "00001"} //nameの値が存在しない + { "id": "00001" } //nameの値が存在しない ``` ```jsonc - {} //idの値も存在しない場合、idは項目必須であるためバリデーションエラーとなる + {} //idの値も存在しない場合、idは項目必須であるためバリデーションエラーとなる ``` - ② 値に `null` を使用する場合、OpenAPI定義(OpenAPI3.0)とJSONデータの例 + ② 値に `null` を使用する場合、OpenAPI 定義(OpenAPI3.0)と JSON データの例 ```yaml application/json: @@ -215,33 +526,34 @@ servers: ``` ```jsonc - {"id": "00001", "name": null} //nameの値が存在しない + { "id": "00001", "name": null } //nameの値が存在しない ``` ```jsonc - {"id": null, "name": null} //id, nameとも値が存在しない + { "id": null, "name": null } //id, nameとも値が存在しない ``` ```jsonc - {} //この場合は、required指定の、id項目がないためバリデーションエラーとなる + {} //この場合は、required指定の、id項目がないためバリデーションエラーとなる ``` -- 原則としては、①`undefined` による定義を使用する方が、API仕様の表現が煩雑にならず、また通信サイズの点からも有利である。 +- 原則としては、①`undefined` による定義を使用する方が、API 仕様の表現が煩雑にならず、また通信サイズの点からも有利である。 -#### 差分更新APIの場合 +#### 差分更新 API の場合 -- 差分更新(PATCH)APIにおいては、項目が更新対象外であることと、項目が更新してクリアする対象であることを明確に区別する必要がある。このような場合には、以下のいずれかの方法を採用する。 +- 差分更新(PATCH)API においては、項目が更新対象外であることと、項目が更新してクリアする対象であることを明確に区別する必要がある。このような場合には、以下のいずれかの方法を採用する。 1)項目を文字列として定義し、空値として空文字("")を採用する。 - - floatやint等の数値についても、文字列型として定義することで、項目が `undefined` または `null` であれば「更新対象外」、項目が空文字であれば「更新してクリアする対象」であると判定できる。 - - この場合、空文字はOpenAPIの`format`定義に許容されないため注意が必要である。`format`定義の代わりに、`pattern`に空文字を許容する正規表現を定義する必要がある。 -
- + + - float や int 等の数値についても、文字列型として定義することで、項目が `undefined` または `null` であれば「更新対象外」、項目が空文字であれば「更新してクリアする対象」であると判定できる。 + - この場合、空文字は OpenAPI の`format`定義に許容されないため注意が必要である。`format`定義の代わりに、`pattern`に空文字を許容する正規表現を定義する必要がある。 +
+ 2)カスタムモデルを使用する - - APIで受け取るデータをデシリアライズする際に、`undefined` と `null` を区別できるようなカスタムモデルを作成すると、1)のように数値を文字列型として扱う必要がなく、OpenAPIの`format` 定義を使用することも可能である。 - - この場合、OpenAPIからの自動生成モデルにはカスタマイズが必要となる。 - - カスタムモデルの例 (参照元: [技術ブログ](https://future-architect.github.io/articles/20211028b/#プログラムの観点)) + - API で受け取るデータをデシリアライズする際に、`undefined` と `null` を区別できるようなカスタムモデルを作成すると、1)のように数値を文字列型として扱う必要がなく、OpenAPI の`format` 定義を使用することも可能である。 + - この場合、OpenAPI からの自動生成モデルにはカスタマイズが必要となる。 + - カスタムモデルの例 (参照元: [技術ブログ](https://future-architect.github.io/articles/20211028b/#プログラムの観点)) #### 各言語による表現の違い @@ -285,145 +597,145 @@ servers: // Deserialize decoder.Decode(&v) fmt.Printf("After decoded: %#v\n", v) - ``` - - - シリアライズ後のjsonを見ると、値がセットされない場合には、項目にゼロ値(ポインタ型は`nil`, string型は空文字、int型は`0`)が入っている。 - - 項目がゼロ値の場合に`omitempty` が付与されていると、 項目ごと除外されている(`undefined` となっている)。 - - ```json - { - "not_nullable_string_1": "value", - "not_nullable_string_2": "", - "nullable_string_1": null, - "nullable_string_3": null, - "not_nullable_int_1": 1, - "not_nullable_int_2": 0, - "nullable_int_1": null, - "nullable_int_3": null - } - ``` - - デシリアライズ後の構造体を見ると、jsonの項目が`undefined`であっても `null` であっても、`nil` として保持されている。 +```` + +- シリアライズ後のjsonを見ると、値がセットされない場合には、項目にゼロ値(ポインタ型は`nil`, string型は空文字、int型は`0`)が入っている。 +- 項目がゼロ値の場合に`omitempty` が付与されていると、 項目ごと除外されている(`undefined` となっている)。 + +```json + { + "not_nullable_string_1": "value", + "not_nullable_string_2": "", + "nullable_string_1": null, + "nullable_string_3": null, + "not_nullable_int_1": 1, + "not_nullable_int_2": 0, + "nullable_int_1": null, + "nullable_int_3": null + } +```` + +- デシリアライズ後の構造体を見ると、json の項目が`undefined`であっても `null` であっても、`nil` として保持されている。 + +```go + After decoded: + Data{ + NotNullableString1:"value", + NotNullableString2:"", + NullableString1:(*string)(nil), + NullableString2:(*string)(nil), + NullableString3:(*string)(nil), + NullableString4:(*string)(nil), + NotNullableInt1:1, + NotNullableInt2:0, + NullableInt1:(*int64)(nil), + NullableInt2:(*int64)(nil), + NullableInt3:(*int64)(nil), + NullableInt4:(*int64)(nil) + } +``` - ```go - After decoded: - Data{ - NotNullableString1:"value", - NotNullableString2:"", - NullableString1:(*string)(nil), - NullableString2:(*string)(nil), - NullableString3:(*string)(nil), - NullableString4:(*string)(nil), - NotNullableInt1:1, - NotNullableInt2:0, - NullableInt1:(*int64)(nil), - NullableInt2:(*int64)(nil), - NullableInt3:(*int64)(nil), - NullableInt4:(*int64)(nil) - } - ``` -
Java +- Java の場合、`int` や `double` などのプリミティブ型は `null` になれないため、`nullable` にするためには、それぞれのラッパークラスである参照型(`Integer`, `Double` など)を使用する必要がある。 +- json にシリアライズ後に`null` の項目を保持するか否かは、例えば、[Jackson ライブラリ](https://github.com/FasterXML)を用いて以下のように区別される。 + +```java + public class Data { + public Data(){}; + public Data(String str1, String str2, int notNullableInt){ + this.nullableString1 = str1; + this.nullableString2 = str2; + this.notNullableInt = notNullableInt; + }; + @JsonInclude(JsonInclude.Include.ALWAYS) + private String nullableString1; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String nullableString2; + + private int notNullableInt; + + // Setters + public void setNullableString1(String nullableString1) { + this.nullableString1 = nullableString1; + } + public void setNullableString2(String nullableString2) { + this.nullableString2 = nullableString2; + } + public void setNotNullableInt(int notNullableInt) { + this.notNullableInt = notNullableInt; + } + // Getters + public String getNullableString1() { + return nullableString1; + } + public String getNullableString2() { + return nullableString2; + } + public int getNotNullableInt() { + return notNullableInt; + } + } +``` - - Javaの場合、`int` や `double` などのプリミティブ型は `null` になれないため、`nullable` にするためには、それぞれのラッパークラスである参照型(`Integer`, `Double` など)を使用する必要がある。 - - jsonにシリアライズ後に`null` の項目を保持するか否かは、例えば、[Jacksonライブラリ](https://github.com/FasterXML)を用いて以下のように区別される。 - - - ```java - public class Data { - public Data(){}; - public Data(String str1, String str2, int notNullableInt){ - this.nullableString1 = str1; - this.nullableString2 = str2; - this.notNullableInt = notNullableInt; - }; - @JsonInclude(JsonInclude.Include.ALWAYS) - private String nullableString1; - - @JsonInclude(JsonInclude.Include.NON_NULL) - private String nullableString2; - - private int notNullableInt; - - // Setters - public void setNullableString1(String nullableString1) { - this.nullableString1 = nullableString1; - } - public void setNullableString2(String nullableString2) { - this.nullableString2 = nullableString2; - } - public void setNotNullableInt(int notNullableInt) { - this.notNullableInt = notNullableInt; - } - // Getters - public String getNullableString1() { - return nullableString1; - } - public String getNullableString2() { - return nullableString2; - } - public int getNotNullableInt() { - return notNullableInt; - } - } - ``` - - ```java - // Set nothing to the fields. - Data dataWithNothing = new Data(); - // Set intial values to the fields. - Data dataWithInitialValues = new Data(null,null,0); - // Set values to the fields. - Data dataWithValues = new Data("","",1); - - List dataList = Arrays.asList(dataWithNothing, dataWithInitialValues, dataWithValues); - ObjectMapper mapper = new ObjectMapper(); - for(Data d : dataList){ - // Serialize - String json = mapper.writeValueAsString(d); - System.out.println(json); - - // Deserialize - Data deserialized = mapper.readValue(json, Data.class); - System.out.println(ToStringBuilder.reflectionToString(deserialized, ToStringStyle.SHORT_PREFIX_STYLE)); - } - ``` - - - シリアライズ後のjsonを見ると、参照型`String`の初期値は`null`、プリミティブ型`int`の初期値は`0`となっている。 - - `@JsonInclude(JsonInclude.Include.ALWAYS)` アノテーションを付与した項目は、値が`null`の場合でも項目が保持される。 - - `@JsonInclude(JsonInclude.Include.NON_NULL)` アノテーションを付与した項目は、値が`null`の場合には項目ごと除外されている(`undefined`となっている)。 - - ```json - { - "nullableString1": null, - "notNullableInt": 0 - } +```java + // Set nothing to the fields. + Data dataWithNothing = new Data(); + // Set intial values to the fields. + Data dataWithInitialValues = new Data(null,null,0); + // Set values to the fields. + Data dataWithValues = new Data("","",1); + + List dataList = Arrays.asList(dataWithNothing, dataWithInitialValues, dataWithValues); + ObjectMapper mapper = new ObjectMapper(); + for(Data d : dataList){ + // Serialize + String json = mapper.writeValueAsString(d); + System.out.println(json); + + // Deserialize + Data deserialized = mapper.readValue(json, Data.class); + System.out.println(ToStringBuilder.reflectionToString(deserialized, ToStringStyle.SHORT_PREFIX_STYLE)); + } +``` - { - "nullableString1": null, - "notNullableInt": 0 - } +- シリアライズ後の json を見ると、参照型`String`の初期値は`null`、プリミティブ型`int`の初期値は`0`となっている。 +- `@JsonInclude(JsonInclude.Include.ALWAYS)` アノテーションを付与した項目は、値が`null`の場合でも項目が保持される。 +- `@JsonInclude(JsonInclude.Include.NON_NULL)` アノテーションを付与した項目は、値が`null`の場合には項目ごと除外されている(`undefined`となっている)。 + +```json + { + "nullableString1": null, + "notNullableInt": 0 + } + + { + "nullableString1": null, + "notNullableInt": 0 + } + + { + "nullableString1": "", + "nullableString2": "", + "notNullableInt": 1 + } +``` - { - "nullableString1": "", - "nullableString2": "", - "notNullableInt": 1 - } - ``` +- デシリアライズ後のオブジェクトを見ると、json の項目が`undefined`であっても `null` であっても、`null` として保持されている。 - - デシリアライズ後のオブジェクトを見ると、jsonの項目が`undefined`であっても `null` であっても、`null` として保持されている。 - - ```java - Data[nullableString1=,nullableString2=,notNullableInt=0] +```java + Data[nullableString1=,nullableString2=,notNullableInt=0] - Data[nullableString1=,nullableString2=,notNullableInt=0] + Data[nullableString1=,nullableString2=,notNullableInt=0] + + Data[nullableString1=,nullableString2=,notNullableInt=1] +``` - Data[nullableString1=,nullableString2=,notNullableInt=1] - ```
@@ -462,38 +774,49 @@ servers: console.log(deserialized) ``` - - シリアライズ後のjsonを見ると、`undefined`定義した項目は除外されている。 - - ```json - { - "nullable_string1": "value1", - "nullable_string2": "", - "nullable_string3": null, - "nullable_num1": 1, - "nullable_num2": 0, - "nullable_num3": null - } - ``` - - - デシリアライズ後のオブジェクトを見ると、jsonの項目が`null` の場合にのみ`null` として保持されており、項目のない場合と区別されている。 +- シリアライズ後のjsonを見ると、`undefined`定義した項目は除外されている。 + +```json +{ + "nullable_string1": "value1", + "nullable_string2": "", + "nullable_string3": null, + "nullable_num1": 1, + "nullable_num2": 0, + "nullable_num3": null +} +``` - ```typescript - nullable_string1: "value1" - nullable_string2: "" - nullable_string3: null - nullable_num1: 1 - nullable_num2: 0 - nullable_num3: null - ``` +- デシリアライズ後のオブジェクトを見ると、json の項目が`null` の場合にのみ`null` として保持されており、項目のない場合と区別されている。 + +```typescript +nullable_string1: "value1"; +nullable_string2: ""; +nullable_string3: null; +nullable_num1: 1; +nullable_num2: 0; +nullable_num3: null; +```
-#### 参照リンク +## ファイル単位 -- `undefined` と `null` の使い方について詳細な解説は、[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい。 -- OpenAPI定義をDB定義に対応させることにより、異なるAPI間で整合のとれた処理設計をすることがのぞましい。DB定義とOpenAPI定義の対応例は、[DB定義とOpenAPI定義のマッピング](./reference/DB_OpenAPI_Mapping_Example.md)を参照されたい。 +OpenAPI ドキュメントは単一のファイルで構成することも複数の分割されたファイルで構成することもできるが、**複数のファイルに分割する**ことを推奨する。 +理由は下記の通りである。 + +- XXX +- XXX +(相談事項) +- 分割のパターンがあるのであれば、いくつかケースに応じてパターン分けして記載する。 +- ファイルの命名についてもここで触れる + +#### 参照リンク + +- `undefined` と `null` の使い方について詳細な解説は、[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい。 +- OpenAPI 定義を DB 定義に対応させることにより、異なる API 間で整合のとれた処理設計をすることがのぞましい。DB 定義と OpenAPI 定義の対応例は、[DB 定義と OpenAPI 定義のマッピング](./reference/DB_OpenAPI_Mapping_Example.md)を参照されたい。 # 各種ツール、サービスとの統合 From 0f2b9b19e70b918bf871a3f49fb8cc1935af38a1 Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Fri, 24 Nov 2023 16:01:35 +0900 Subject: [PATCH 03/19] Add security schem (#66) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add security schem : 2023.11.24 Taskforce meetingにてapproved --- .../OpenAPI_Specification_3.0.3.md | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index f9cd6b1b..30c46dde 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -324,6 +324,17 @@ components: ... ``` +### security + +APIレベルの認証方式の設定だが、ルートレベルのsecurityで定義済みであるため、通常設定しない。 + +ヘルスチェックのような認証を通す必要がないAPIのみ、上書きで定義する。 + +```yml +# 認証しない場合のみ個別で定義する +security: [] +``` + ## components ​ @@ -419,24 +430,35 @@ paths: - $ref: '#/components/parameters/limit' ``` +### securitySchemes + +標準で用いるAPI認証の定義を行う。 + +```yml +# Bearer トークによる認証 +securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: 'Bearer トークン認証' +``` + ## security -- API の認証方式の設定 -- 規約 - - [ ] API 共通で設定済みのため設定しない - - [ ] 認証を通す必要のない API を定義する場合にのみ定義 - ​ +ルートレベルのsecurityを定義すると、全APIに共通で適用される。 + +業務システムのWeb APIで認証が全く存在しないことは考えにくいため、本規約ではルートレベルで認証を設定し、漏れをなくす。 ```yml # 認証設定方法 (デフォルトで設定済みの為不要) security: - Bearer: [] -​ -# 認証しない場合 -security: [] ``` +ヘルスチェックなどAPI種別によって認証が不要な場合がある。それらに対しては個別に、認証情報を上書き定義する。 + ## tags タグを用いて、API 操作をグループ化することができる。ドキュメントやツールにとって非常に重要であるため、 **必須** で指定する。 From 41ae3701c4d419121c330b81c1d29c4328869331 Mon Sep 17 00:00:00 2001 From: Hiroki Takeda Date: Tue, 28 Nov 2023 17:48:00 +0900 Subject: [PATCH 04/19] Add path object in OpenAPI v3 (#65) --- .../OpenAPI_Specification_3.0.3.md | 332 ++++++++++-------- 1 file changed, 194 insertions(+), 138 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 30c46dde..78cebc96 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -93,7 +93,7 @@ openapi: 3.0 WebAPI の総称を記載する。システム名やサービス名 + API のような命名とすることを推奨する。 例. `X System API` -### desctiption +### description Web API が提供する機能の概要・想定する利用者やユースケース・制約などを記載する。 @@ -138,102 +138,179 @@ servers: ## paths -API エンドポイント定義 +API の利用可能なエンドポイントと操作方法を記載する。 -- 規約 - - [ ] paths 以下エンドポイントは機能 ID の昇順に定義 - - [ ] HTTP メソッドは `GET`,`POST`,`PUT`,`PATCH`,`DELETE` の順に定義 +* API ごとに機能IDを定義している場合、`paths` 配下の各パスは機能 ID の昇順に定義する。 +* URLパスが複数の単語からなる場合、ケバブケースで表現する。 +* HTTP メソッドは `GET`, `POST`, `PUT`, `PATCH`, `DELETE` の順に定義する。 -```yml -paths: - /product: # エンドポイント - get: # HTTPメソッド - ... -``` + 良い例: + + ```yaml + paths: + /products: + get: + ... + post: + ... + ``` + + 悪い例: + + ```yaml + paths: + /products: + post: + ... + get: + ... + ``` + +* HTTPメソッドの配下に定義されるオペレーションオブジェクトは、下記の項目を必須項目とする。 + +| フィールド名 | 必須 | 記載内容 | +| ------------ | :--: | ---------------------------------------- | +| tags | ○ | API の論理的なグループ | +| operationId | ○ | API の利用可能なエンドポイントと操作方法 | +| summary | ○ | API の操作概要 | +| description | | API の振る舞いの詳細や注意点を記載する。 | +| parameters | | API のリクエストパラメータ | +| requestBody | | API のリクエストボディ | +| response | ○ | API のレスポンス | +| security | | | + + +### tags + +API の論理的なグループを指定する。 -| フィールド名 | 必須 | 記載内容 | -| ------------ | :--: | -------- | -| tags | ○ | | -| operationId | ○ | | -| summary | ○ | | -| description | ○ | | -| response | ○ | | -| parameters | | | -| requestBody | | | -| security | | | +* タグオブジェクトとして事前定義したタグの中から選択すること。 + + 良い例: + + ```yaml + paths: + /users/me: + get: + tags: + - users + ... + tags: + - name: users + ``` + + 悪い例: + + ```yaml + paths: + /users/me: + get: + tags: + # タグオブジェクトとして定義されていないタグが指定されている + - users + ... + tags: [] + ``` + +* 1 API につき 1つのタグを指定すること。 + + 良い例: + + ```yaml + paths: + /users/me: + get: + tags: + - users + ... + ``` + + 悪い例: + + ```yaml + paths: + /users/me: + get: + # 複数のタグが指定されている + tags: + - users + - admin + ... + ``` ### operationId -v2 だと以下のように記載している。 +API を識別するための一意な文字列を記載する。 -コード生成で利用される項目なので、必須で指定する +* HTTP メソッドとURLパスをアッパーキャメルケースで表現する。 + ただしOpenAPI ドキュメントのエディタとして広く使用されるStoplightが提供する[Linter](https://docs.stoplight.io/docs/spectral/674b27b261c3c-overview)の定義としてケバブケースが標準になっているため、Stoplightを使用する場合はケバブケースで表現しても良い。 -- 原則、`camelCase` の `${HTTPメソッド}${機能物理名}` で記載する(例: getUser, postUser, deleteUser) + 良い例: -* ※ Spotlight Studio による自動生成に準拠すると、ケバブケース + ```yaml + paths: + /users/me: + get: + operationId: get-users-me + ... + /products/{product_id}: + put: + operationId: put-products-product-id + ``` - - [ ] `${HTTPメソッド}-${URLパス1}-${URLパス2}..` 形式で定義 - - [ ] ケバブケースで定義 +### summery -### description +API の操作概要を記載する。 -`必須` -​ +* 機能 ID や機能名があるのであれば記載する。 -- API の機能概要を記載 - ​ + 良い例 -```yaml -description: IDを指定して商品情報を取得する。 -``` + ```yaml + paths: + /users/me: + get: + summary: API-001 ユーザアカウント取得 + ``` + +### description + +APIの振る舞いの詳細や注意点を記載する。 +別途参照させるべき設計書があるのであれば、設計書へのリンクを記載しても良い。 ### parameters -`任意` -​ +API のリクエストパラメータ(パスパラメータ、クエリパラメータ、ヘッダ)を記載する。 -- リクエストパラメータの定義 -- 規約 - - [ ] HTTP メソッドが GET,DELETE 時に利用 - - [ ] パスパラメータ、クエリパラメータ、ヘッダパラメータを指定 - - [ ] パラメータ名はスネークケースで定義 - ​ +* HTTP メソッドが `GET`, `DELETE` の場合にのみ指定する。 +* パスパラメータはスネークケースで表現する。 +* クエリパラメータはスネークケースで表現する。 +* ヘッダはハイフンを区切り文字とするパスカルケースで表現する。 -```yaml -parameters: - - in: query # パラメータ配置 (path, query, header 指定可) - name: product_id # パラメータ名(物理名) - description: プロダクトID # 論理名 - schema: # パラメータスキーマ定義 - type: string - ... -``` ### requestBody -​ -`任意` -​ - -- API のリクエストボディを定義 -- 規約 - - [ ] HTTP メソッドが POST,PUT,PATCH 時に利用 - - [ ] リクエストボディが必須の場合は `requestBody` 直下の `required: true` を指定 - - [ ] スキーマ定義は components として schemas 以下に定義し、 `$ref` で参照 - - [ ] スキーマ名は `Req` をプレフィックスに付与し、`operation_id` をアッパーキャメルケースへ変換の上で定義 \* 例) `operation_id=post-product` => `ReqPostProduct` - ​ +API のリクエストボディを記載する。 +* HTTP メソッドが `POST`, `PUT`, `PATCH` の場合にのみ指定する。 +* リクエストボディが必須の場合は `requestBody` 直下の `required` に `true` を指定する。 +* OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `component` オブジェクトとして任意の名称で定義し `$ref` で参照する。 +スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Req` を付与) +* `schema` オブジェクトの `type` は `object` を指定する。 + ```yaml -operation_id: post-product -requestBody: - required: true - content: - application/json: # media typeを指定 - schema: - $ref: '#/components/schemas/ReqPostProduct' # リクエストボディのスキーマ構造指定 -​ -... -​ +paths: + /products: + post: + operation_id: post-products + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ReqPostProducts' + ... + components: schemas: ReqPostProduct: @@ -242,85 +319,64 @@ components: ... ``` -​ +​### responses -### responses +API のレスポンスを記載する。 -`必須` -​ - -- ステータスコード別の API レスポンススキーマを定義 -- 規約 - - [ ] スキーマ定義は `components.schemas` 以下に定義し、 `$ref` で参照 - - [ ] スキーマ定義の第一階層の type は `object` 必須 - - 設計書の `3.戻り値` 定義の第一階層に `object` の追加必須 ([補足: 設計書の戻り値との整合性](#補足-設計書の戻り値との整合性)参照) - - [ ] スキーマ名は `Res` をプレフィックスに付与し、`operation_id` をアッパーキャメルケースへ変換の上で定義 - - 例) `operation_id=post-product` => `ResPostProduct` - - [ ] 異常系(`4xx`,`5xx`)の定義は共通のため、以下記載の方式で定義 - - [ ] ステータスコード`200`,`201` でレスポンススキーマが定義されている場合は、 example 名が `default` の定義必須 - - [ ] example のケースを追加したい場合は `./examples` ディレクトリ配下にファイルを配置して、`$ref` で参照 +* OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `components` オブジェクトとして任意の名称で定義し `$ref` で参照する。 +スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Res` を付与) +* `schema` オブジェクトの `type` は `object` を指定する。 +* 異常系(`4xx`, `5xx`)の HTTP ステータスコードに対応するレスポンス定義は設計者が個別に定義するのではなく、事前に共通的なレスポンスオブジェクトを定義し `$ref` で参照することが望ましい。 ​ -```yml -operation_id: post-product -responses: - '200': - description: プロダクト一覧 - content: - application/json: - schema: # スキーマ定義 - $ref: '#/components/schemas/ResPostProduct' - examples: # サンプルレスポンス - default: # サンプル名 - value: - ... # サンプル定義 - example-1: - $ref: './examples/post-product.example.1.yaml' - # 以下はデフォルトで指定 - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - # POST の場合のみ利用 - '409': - $ref: '#/components/responses/Conflict' - '422': - $ref: '#/components/responses/UnprocessableEntity' - '500': - $ref: '#/components/responses/InternalServer' - '503': - $ref: '#/components/responses/ServiceUnavaliable' -​ -... -​ +```yaml +paths: + /products: + post: + operation_id: post-products + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ResPostProducts' + examples: + default: + value: + ... + example-1: + $ref: './examples/post-product.example.1.yaml' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '500': + $ref: '#/components/responses/InternalServer' + '503': + $ref: '#/components/responses/ServiceUnavailable' + ... + components: schemas: - ResPostProduct: - type: object - properties: - products: - type: array - items: - $ref: '#/components/schemas/Product' - Product: - type: object - properties: - ... - BasicError: + ResPostProducts: type: object properties: ... responses: BadRequest: - description: リクエストデータ不正 + description: Bad Request content: application/json: schema: - "$ref": "#/components/schemas/BasicError" + ... ... ``` From b051c8961b2b9592abf5bde135c0e52663f65607 Mon Sep 17 00:00:00 2001 From: future-fujita <81731712+future-fujita@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:36:32 +0900 Subject: [PATCH 05/19] feat: add file-component section (#64) feat: add file-component section --- .../OpenAPI_Specification_3.0.3.md | 769 ++++++++++++++---- .../reference/DB_OpenAPI_Mapping_Example.md | 18 + .../reference/divided_files_sample.zip | Bin 0 -> 7267 bytes 3 files changed, 635 insertions(+), 152 deletions(-) create mode 100644 documents/forOpenAPISpecification/reference/DB_OpenAPI_Mapping_Example.md create mode 100644 documents/forOpenAPISpecification/reference/divided_files_sample.zip diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 78cebc96..de977cca 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -559,7 +559,7 @@ tags: ## 値が存在しないという状態の表現 -#### undefined と null +### undefined と null - リクエスト/レスポンスにおいて、ある項目の値が存在しないという状態を表現する場合、① その項目自体を含めず `undefined` とする方法と、② 値に `null` を使用する方法がある。 @@ -617,7 +617,7 @@ tags: - 原則としては、①`undefined` による定義を使用する方が、API 仕様の表現が煩雑にならず、また通信サイズの点からも有利である。 -#### 差分更新 API の場合 +### 差分更新 API の場合 - 差分更新(PATCH)API においては、項目が更新対象外であることと、項目が更新してクリアする対象であることを明確に区別する必要がある。このような場合には、以下のいずれかの方法を採用する。 @@ -633,7 +633,7 @@ tags: - この場合、OpenAPI からの自動生成モデルにはカスタマイズが必要となる。 - カスタムモデルの例 (参照元: [技術ブログ](https://future-architect.github.io/articles/20211028b/#プログラムの観点)) -#### 各言語による表現の違い +### 各言語による表現の違い
Golang @@ -675,144 +675,143 @@ tags: // Deserialize decoder.Decode(&v) fmt.Printf("After decoded: %#v\n", v) + ``` -```` + - シリアライズ後のjsonを見ると、値がセットされない場合には、項目にゼロ値(ポインタ型は`nil`, string型は空文字、int型は`0`)が入っている。 + - 項目がゼロ値の場合に`omitempty` が付与されていると、 項目ごと除外されている(`undefined` となっている)。 + + ```json + { + "not_nullable_string_1": "value", + "not_nullable_string_2": "", + "nullable_string_1": null, + "nullable_string_3": null, + "not_nullable_int_1": 1, + "not_nullable_int_2": 0, + "nullable_int_1": null, + "nullable_int_3": null + } + ``` -- シリアライズ後のjsonを見ると、値がセットされない場合には、項目にゼロ値(ポインタ型は`nil`, string型は空文字、int型は`0`)が入っている。 -- 項目がゼロ値の場合に`omitempty` が付与されていると、 項目ごと除外されている(`undefined` となっている)。 + - デシリアライズ後の構造体を見ると、json の項目が`undefined`であっても `null` であっても、`nil` として保持されている。 -```json - { - "not_nullable_string_1": "value", - "not_nullable_string_2": "", - "nullable_string_1": null, - "nullable_string_3": null, - "not_nullable_int_1": 1, - "not_nullable_int_2": 0, - "nullable_int_1": null, - "nullable_int_3": null - } -```` - -- デシリアライズ後の構造体を見ると、json の項目が`undefined`であっても `null` であっても、`nil` として保持されている。 - -```go - After decoded: - Data{ - NotNullableString1:"value", - NotNullableString2:"", - NullableString1:(*string)(nil), - NullableString2:(*string)(nil), - NullableString3:(*string)(nil), - NullableString4:(*string)(nil), - NotNullableInt1:1, - NotNullableInt2:0, - NullableInt1:(*int64)(nil), - NullableInt2:(*int64)(nil), - NullableInt3:(*int64)(nil), - NullableInt4:(*int64)(nil) - } -``` + ```go + After decoded: + Data{ + NotNullableString1:"value", + NotNullableString2:"", + NullableString1:(*string)(nil), + NullableString2:(*string)(nil), + NullableString3:(*string)(nil), + NullableString4:(*string)(nil), + NotNullableInt1:1, + NotNullableInt2:0, + NullableInt1:(*int64)(nil), + NullableInt2:(*int64)(nil), + NullableInt3:(*int64)(nil), + NullableInt4:(*int64)(nil) + } + ```
Java -- Java の場合、`int` や `double` などのプリミティブ型は `null` になれないため、`nullable` にするためには、それぞれのラッパークラスである参照型(`Integer`, `Double` など)を使用する必要がある。 -- json にシリアライズ後に`null` の項目を保持するか否かは、例えば、[Jackson ライブラリ](https://github.com/FasterXML)を用いて以下のように区別される。 - -```java - public class Data { - public Data(){}; - public Data(String str1, String str2, int notNullableInt){ - this.nullableString1 = str1; - this.nullableString2 = str2; - this.notNullableInt = notNullableInt; - }; - @JsonInclude(JsonInclude.Include.ALWAYS) - private String nullableString1; - - @JsonInclude(JsonInclude.Include.NON_NULL) - private String nullableString2; - - private int notNullableInt; - - // Setters - public void setNullableString1(String nullableString1) { - this.nullableString1 = nullableString1; - } - public void setNullableString2(String nullableString2) { - this.nullableString2 = nullableString2; - } - public void setNotNullableInt(int notNullableInt) { - this.notNullableInt = notNullableInt; - } - // Getters - public String getNullableString1() { - return nullableString1; - } - public String getNullableString2() { - return nullableString2; - } - public int getNotNullableInt() { - return notNullableInt; - } - } -``` + - Java の場合、`int` や `double` などのプリミティブ型は `null` になれないため、`nullable` にするためには、それぞれのラッパークラスである参照型(`Integer`, `Double` など)を使用する必要がある。 + - json にシリアライズ後に`null` の項目を保持するか否かは、例えば、[Jackson ライブラリ](https://github.com/FasterXML)を用いて以下のように区別される。 + + ```java + public class Data { + public Data(){}; + public Data(String str1, String str2, int notNullableInt){ + this.nullableString1 = str1; + this.nullableString2 = str2; + this.notNullableInt = notNullableInt; + }; + @JsonInclude(JsonInclude.Include.ALWAYS) + private String nullableString1; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String nullableString2; + + private int notNullableInt; + + // Setters + public void setNullableString1(String nullableString1) { + this.nullableString1 = nullableString1; + } + public void setNullableString2(String nullableString2) { + this.nullableString2 = nullableString2; + } + public void setNotNullableInt(int notNullableInt) { + this.notNullableInt = notNullableInt; + } + // Getters + public String getNullableString1() { + return nullableString1; + } + public String getNullableString2() { + return nullableString2; + } + public int getNotNullableInt() { + return notNullableInt; + } + } + ``` -```java - // Set nothing to the fields. - Data dataWithNothing = new Data(); - // Set intial values to the fields. - Data dataWithInitialValues = new Data(null,null,0); - // Set values to the fields. - Data dataWithValues = new Data("","",1); - - List dataList = Arrays.asList(dataWithNothing, dataWithInitialValues, dataWithValues); - ObjectMapper mapper = new ObjectMapper(); - for(Data d : dataList){ - // Serialize - String json = mapper.writeValueAsString(d); - System.out.println(json); - - // Deserialize - Data deserialized = mapper.readValue(json, Data.class); - System.out.println(ToStringBuilder.reflectionToString(deserialized, ToStringStyle.SHORT_PREFIX_STYLE)); - } -``` + ```java + // Set nothing to the fields. + Data dataWithNothing = new Data(); + // Set intial values to the fields. + Data dataWithInitialValues = new Data(null,null,0); + // Set values to the fields. + Data dataWithValues = new Data("","",1); + + List dataList = Arrays.asList(dataWithNothing, dataWithInitialValues, dataWithValues); + ObjectMapper mapper = new ObjectMapper(); + for(Data d : dataList){ + // Serialize + String json = mapper.writeValueAsString(d); + System.out.println(json); + + // Deserialize + Data deserialized = mapper.readValue(json, Data.class); + System.out.println(ToStringBuilder.reflectionToString(deserialized, ToStringStyle.SHORT_PREFIX_STYLE)); + } + ``` -- シリアライズ後の json を見ると、参照型`String`の初期値は`null`、プリミティブ型`int`の初期値は`0`となっている。 -- `@JsonInclude(JsonInclude.Include.ALWAYS)` アノテーションを付与した項目は、値が`null`の場合でも項目が保持される。 -- `@JsonInclude(JsonInclude.Include.NON_NULL)` アノテーションを付与した項目は、値が`null`の場合には項目ごと除外されている(`undefined`となっている)。 + - シリアライズ後の json を見ると、参照型`String`の初期値は`null`、プリミティブ型`int`の初期値は`0`となっている。 + - `@JsonInclude(JsonInclude.Include.ALWAYS)` アノテーションを付与した項目は、値が`null`の場合でも項目が保持される。 + - `@JsonInclude(JsonInclude.Include.NON_NULL)` アノテーションを付与した項目は、値が`null`の場合には項目ごと除外されている(`undefined`となっている)。 -```json - { - "nullableString1": null, - "notNullableInt": 0 - } + ```json + { + "nullableString1": null, + "notNullableInt": 0 + } - { - "nullableString1": null, - "notNullableInt": 0 - } + { + "nullableString1": null, + "notNullableInt": 0 + } - { - "nullableString1": "", - "nullableString2": "", - "notNullableInt": 1 - } -``` + { + "nullableString1": "", + "nullableString2": "", + "notNullableInt": 1 + } + ``` -- デシリアライズ後のオブジェクトを見ると、json の項目が`undefined`であっても `null` であっても、`null` として保持されている。 + - デシリアライズ後のオブジェクトを見ると、json の項目が`undefined`であっても `null` であっても、`null` として保持されている。 -```java - Data[nullableString1=,nullableString2=,notNullableInt=0] + ```java + Data[nullableString1=,nullableString2=,notNullableInt=0] - Data[nullableString1=,nullableString2=,notNullableInt=0] + Data[nullableString1=,nullableString2=,notNullableInt=0] - Data[nullableString1=,nullableString2=,notNullableInt=1] -``` + Data[nullableString1=,nullableString2=,notNullableInt=1] + ```
@@ -852,49 +851,515 @@ tags: console.log(deserialized) ``` -- シリアライズ後のjsonを見ると、`undefined`定義した項目は除外されている。 - -```json -{ - "nullable_string1": "value1", - "nullable_string2": "", - "nullable_string3": null, - "nullable_num1": 1, - "nullable_num2": 0, - "nullable_num3": null -} -``` + - シリアライズ後のjsonを見ると、`undefined`定義した項目は除外されている。 -- デシリアライズ後のオブジェクトを見ると、json の項目が`null` の場合にのみ`null` として保持されており、項目のない場合と区別されている。 + ```json + { + "nullable_string1": "value1", + "nullable_string2": "", + "nullable_string3": null, + "nullable_num1": 1, + "nullable_num2": 0, + "nullable_num3": null + } + ``` -```typescript -nullable_string1: "value1"; -nullable_string2: ""; -nullable_string3: null; -nullable_num1: 1; -nullable_num2: 0; -nullable_num3: null; -``` + - デシリアライズ後のオブジェクトを見ると、json の項目が`null` の場合にのみ`null` として保持されており、項目のない場合と区別されている。 + ```typescript + nullable_string1: "value1"; + nullable_string2: ""; + nullable_string3: null; + nullable_num1: 1; + nullable_num2: 0; + nullable_num3: null; + ``` +### 参照リンク + +- `undefined` と `null` の使い方について詳細な解説は、[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい。 +- OpenAPI 定義を DB 定義に対応させることにより、異なる API 間で整合のとれた処理設計をすることがのぞましい。DB 定義と OpenAPI 定義の対応例は、[DB 定義と OpenAPI 定義のマッピング](./reference/DB_OpenAPI_Mapping_Example.md)を参照されたい。 + ## ファイル単位 OpenAPI ドキュメントは単一のファイルで構成することも複数の分割されたファイルで構成することもできるが、**複数のファイルに分割する**ことを推奨する。 理由は下記の通りである。 -- XXX -- XXX +- **APIごとに担当者を分けて設計する場合などに、複数人による編集によって意図しないコンフリクトが発生することを防ぐ。** +- **ファイルの肥大化による、可読性の低下を防ぐ。** + +### 分割方法の選定 + +開発方針やOpenAPIの使用用途に合わせて、都合の良いファイルの分割方法を採用する。例えば、以下のような方法がある。 + +1. APIごとに設計担当者を分けて、それぞれにOpenAPIを編集する場合は、APIの単位で分割する。 +2. テストツールとして [stoplightio/prism](https://github.com/stoplightio/prism)を使用する場合、テストケースごとにデータファイルを作成して、`examples` にファイルパスを指定する。 + +### サンプル説明 + +分割方法1, 2の両方に当てはまる場合のサンプルを用いて説明する。`openapi.yaml` とディレクトリ構成は下の通り。サンプルの全量は [サンプルzip Download](./reference/divided_files_sample.zip)からダウンロード可能。 + +- 機能単位(path, method単位)にディレクトリを作成して、それぞれの定義ファイルを格納する。ディレクトリ名は `{path}_{method}` とすると管理し易い。 +- `components` の `schemas` には、 + - 各APIごとのリクエスト/リスポンスモデルを切り出して記載する(例えば、`ResPetsPetIdGet`)。 + - API間で同じモデルを使用する場合は共通化して記載する(例えば、`Pet`)。 + - 各APIのリクエスト/リスポンスモデルの中で、モデルがネストする場合は、各モデルの単位で書き出す(例えば、`PetDetail`, `Pedigree`)。 + +
+ ファイル分割例: openapi.yaml + + ```yaml + openapi: "3.0.3" + info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + servers: + - url: http://petstore.swagger.io/v1 + paths: + /pets: + get: + $ref: "./pets_get/pets_get.yaml" + post: + $ref: "./pets_post/pets_post.yaml" + /pets/{petId}: + get: + $ref: "./pets-pet-id_get/pets-pet-id_get.yaml" + + components: + schemas: + ResPetsGet: + $ref: "./pets_get/response.yaml" + ReqPetsPost: + $ref: "./pets_post/request.yaml" + ResPetsPetIdGet: + $ref: "./pets-pet-id_get/response.yaml#/ResPetsPetIdGet" + PetDetail: + $ref: "./pets-pet-id_get/response.yaml#/PetDetail" + Pedigree: + $ref: "./pets-pet-id_get/response.yaml#/Pedigree" + Pet: + $ref: "./common/pet.yaml" + Error: + $ref: "./common/error.yaml" + ``` -(相談事項) +
-- 分割のパターンがあるのであれば、いくつかケースに応じてパターン分けして記載する。 -- ファイルの命名についてもここで触れる +
+ ファイル分割例: ディレクトリ構成 + + ```sh + │ openapi.gen.yaml + │ openapi.yaml + │ + ├─common + │ error.yaml + │ pet.yaml + │ + ├─pets-pet-id_get + │ │ pets-pet-id_get.yaml + │ │ response.yaml + │ │ + │ └─examples + │ res_example1.yaml + │ + ├─pets_get + │ │ pets_get.yaml + │ │ response.yaml + │ │ + │ └─examples + │ res_example1.yaml + │ res_example2.yaml + │ + └─pets_post + │ pets_post.yaml + │ request.yaml + │ + └─examples + req_example1.yaml + ``` -#### 参照リンク +
+ +- `openapi.yaml` の `paths` に記載したAPIファイルは以下のように作成する。`schema` にて `openapi.yaml` に指定したキー(`../openapi.yaml#/components/schemas/ResPetsPetIdGet`)を参照する。 +- `examples` には、各APIのテストケースIDをキーとして指定(`ResExample1`)し、`value` に該当するテストケースのデータファイルパスを指定(`./examples/res_example1.yaml`)する。ファイル名は、指定したキーをスネークケースに変換したものを使用するとよい。 + +
+ API別ファイルの記載例: pets-pet-id_get.yaml + + ```yaml + summary: Details for a pet + operationId: get-pets-pet-id + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "../openapi.yaml#/components/schemas/ResPetsPetIdGet" + examples: + ResExample1: + value: + $ref: "./examples/res_example1.yaml" + "404": + description: not found error + content: + application/json: + schema: + $ref: "../openapi.yaml#/components/schemas/Error" + "500": + description: unexpected error + content: + application/json: + schema: + $ref: "../openapi.yaml#/components/schemas/Error" + ``` + +
+ +- `schema` ファイルの例は以下の通り。 + - 複数API間に共通のモデルは `openapi.yaml` に指定したキーを指定する(`../openapi.yaml#/components/schemas/Pet`)。 + - ネストしているモデルは `openapi.yaml` に指定したキーを経由して参照できるようにする(`../openapi.yaml#/components/schemas/PetDetail`, `../openapi.yaml#/components/schemas/Pedigree`)。 + +
+ schemaファイル記載例: pets-pet-id_get/response.yaml + + ```yaml + ResPetsPetIdGet: + required: + - pet + - pet_detail + type: object + properties: + pet: + $ref: "../openapi.yaml#/components/schemas/Pet" + pet_detail: + $ref: "../openapi.yaml#/components/schemas/PetDetail" + + PetDetail: + type: object + properties: + breeder: + type: string + date_of_birth: + type: string + format: date + pedigree: + $ref: "../openapi.yaml#/components/schemas/Pedigree" + + Pedigree: + required: + - registration_no + - date_of_registration + - pedigree_image + type: object + properties: + registration_no: + type: integer + format: int64 + date_of_registration: + type: string + format: date + pedigree_image: + type: string + ``` + +
+ +- OpenAPIの使用用途により、分割ファイルを1つのファイルにまとめる必要がある場合には、例えば[swagger-cli](https://apitools.dev/swagger-cli/)を使用して以下コマンドを実行する。 + + ```bash + swagger-cli bundle openapi.yaml --outfile openapi.gen.yaml --type yaml + ``` + +
+ ファイルBundle後: openapi.gen.yaml + + ```yaml + openapi: 3.0.3 + info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + servers: + - url: 'http://petstore.swagger.io/v1' + paths: + /pets: + get: + summary: List all pets + operationId: get-pets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + maximum: 100 + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/ResPetsGet' + examples: + ResExample1: + value: + - id: 10001 + name: ToyPoodle + category: dog + sub_category: ToyPoodle + age: 1 + sex: male + note: friendly + tag: dog10001 + - id: 10002 + name: Chihuahua + category: dog + sub_category: Chihuahua + age: 1 + sex: female + note: friendly + tag: dog10002 + - id: 10003 + name: Shiba + category: dog + sub_category: Shiba + age: 1 + sex: male + note: friendly + tag: dog10003 + - id: 10004 + name: MiniatureDachshund + category: dog + sub_category: MiniatureDachshund + age: 1 + sex: female + note: friendly + tag: dog10004 + ResExample2: + value: [] + '404': + description: not found error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: Register a pet + operationId: post-pets + tags: + - pets + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ReqPetsPost' + examples: + ReqExample1: + value: + pet: + id: 10005 + name: FrenchBulldog + category: dog + sub_category: FrenchBulldog + age: 1 + sex: male + note: friendly + tag: dog10005 + required: false + responses: + '201': + description: Null response + '404': + description: not found error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/pets/{petId}': + get: + summary: Details for a pet + operationId: get-pets-pet-id + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: '#/components/schemas/ResPetsPetIdGet' + examples: + ResExample1: + value: + pet: + id: 10001 + name: ToyPoodle + category: dog + sub_category: ToyPoodle + age: 1 + sex: male + note: friendly + tag: dog10001 + pet_detail: + breeder: BreederName + date_of_birth: '2023-10-31' + pedigree: + registration_no: 11111111 + date_of_registration: '2023-10-31' + pedigree_image: 9j2wBDAA...8QAPxAAAQQABAMGBAYDAAEDAg + '404': + description: not found error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + components: + schemas: + ResPetsGet: + type: array + maxItems: 100 + items: + $ref: '#/components/schemas/Pet' + ReqPetsPost: + required: + - pet + type: object + properties: + pet: + $ref: '#/components/schemas/Pet' + ResPetsPetIdGet: + required: + - pet + - pet_detail + type: object + properties: + pet: + $ref: '#/components/schemas/Pet' + pet_detail: + $ref: '#/components/schemas/PetDetail' + PetDetail: + type: object + properties: + breeder: + type: string + date_of_birth: + type: string + format: date + pedigree: + $ref: '#/components/schemas/Pedigree' + Pedigree: + required: + - registration_no + - date_of_registration + - pedigree_image + type: object + properties: + registration_no: + type: integer + format: int64 + date_of_registration: + type: string + format: date + pedigree_image: + type: string + Pet: + type: object + required: + - id + - name + - category + - age + - sex + properties: + id: + type: integer + format: int64 + name: + type: string + maxLength: 50 + category: + type: string + maxLength: 10 + sub_category: + type: string + maxLength: 50 + age: + type: integer + format: int32 + sex: + type: string + maxLength: 6 + note: + type: string + maxLength: 200 + tag: + type: string + maxLength: 20 + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + + ``` + +
-- `undefined` と `null` の使い方について詳細な解説は、[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい。 -- OpenAPI 定義を DB 定義に対応させることにより、異なる API 間で整合のとれた処理設計をすることがのぞましい。DB 定義と OpenAPI 定義の対応例は、[DB 定義と OpenAPI 定義のマッピング](./reference/DB_OpenAPI_Mapping_Example.md)を参照されたい。 # 各種ツール、サービスとの統合 diff --git a/documents/forOpenAPISpecification/reference/DB_OpenAPI_Mapping_Example.md b/documents/forOpenAPISpecification/reference/DB_OpenAPI_Mapping_Example.md new file mode 100644 index 00000000..52dfdd31 --- /dev/null +++ b/documents/forOpenAPISpecification/reference/DB_OpenAPI_Mapping_Example.md @@ -0,0 +1,18 @@ +# DB OpenAPI Mapping Example + +|データの種類|DB型|DDL定義|OpenAPI
項目必須/非必須|OpenAPI
Type/その他定義|API
リクエスト/リスポンス|備考| +|:----|:----|:----|:----|:----|:----|:----| +|区分値|varchar|NOT NULL, カラム名 <> ''|required|string/enum|項目必須、空値は許容しない| | +| | |NOT NULL default ''|-|string/enum|項目非必須、空値はundefinedまたは空文字として定義|空値を空文字で定義する場合、enumに空文字を含む必要がある。| +|フラグ|varchar|NOT NULL, カラム名 <> ''|required|string/enum|項目必須、空値は許容しない| | +| | |NOT NULL default ''|-|string/enum|項目非必須、空値はundefinedまたは空文字として定義|空値を空文字で定義する場合、enumに空文字を含む必要がある。| +|数値|integer|NOT NULL|required|integer|項目必須、空値は許容しない| | +| | |-|nullable: true|integer|項目非必須、空値はundefinedまたはnullとして定義| | +|数値(精度有)|numeric|NOT NULL|required|string/正規表現pattern|項目必須、空値は許容しない| | +| | |-|nullable: true|string/正規表現pattern|項目非必須、空値はundefinedまたはnullとして定義| | +|日付/時刻|date / timestamp|NOT NULL|required|string/format指定または正規表現pattern|項目必須、空値は許容しない| | +| | |-|nullable: true|string/format指定または正規表現pattern|項目非必須、空値はundefinedまたはnullとして定義| | +|コード/番号|varchar|NOT NULL, カラム名 <> ''|required|string/正規表現patternや桁数指定|項目必須、空値は許容しない| | +| | |NOT NULL default ''|-|string/正規表現patternや桁数指定|項目非必須、空値はundefinedまたは空文字として定義| | +|名前 / メモ|varchar / text|NOT NULL, カラム名 <> ''|required|string/正規表現patternや桁数指定|項目必須、空値は許容しない| | +| | |NOT NULL default ''|-|string/正規表現patternや桁数指定|項目非必須、空値はundefinedまたは空文字として定義| | diff --git a/documents/forOpenAPISpecification/reference/divided_files_sample.zip b/documents/forOpenAPISpecification/reference/divided_files_sample.zip new file mode 100644 index 0000000000000000000000000000000000000000..6ffeea93ed2a3e5b245ee962a2c7374719c9725c GIT binary patch literal 7267 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 literal 0 HcmV?d00001 From d6afb50a93a7d522e936df82869f08f6fb4d0af6 Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Fri, 1 Dec 2023 15:45:10 +0900 Subject: [PATCH 06/19] component/response (#68) --- .../OpenAPI_Specification_3.0.3.md | 73 +++++++++++++++++-- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index de977cca..50274cbc 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -319,7 +319,7 @@ components: ... ``` -​### responses +### responses API のレスポンスを記載する。 @@ -372,12 +372,7 @@ components: ... responses: BadRequest: - description: Bad Request - content: - application/json: - schema: - ... - ... + ... ``` ### security @@ -460,6 +455,70 @@ components: ... ``` +#### responses(components) + +レスポンスで複数のエンドポイントで横断的に用いるモデルを定義する。例えば、ステータスコード400~500系のエラーモデルがある。 +​ +```yml +components: + schemas: + ProblemDetailError: + type: object + properties: + ... + responses: + BadRequest: + description: 400 Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/ProblemDetailError" + Unauthorized: + description: 401 Unauthorized + content: + application/json: + schema: + "$ref": "#/components/schemas/ProblemDetailError" + ... +``` +​ +もし、正常系のレスポンスの例としてはファイルアップロード・ダウンロードなどが該当する。個別のアプリケーション要件でブレが少ないと複数のエンドポイントで用いられる場合に定義する。 + +```yml +components: + schemas: + SignedURL: + type: object + properties: + signed_url: + type: string + format: uri + expired_at: + type: string + format: date-time + responses: + BlobUpload: + description: BLOB(Binary Large Object) upload using presigned url + content: + application/json: + schema: + "$ref": "#/components/schemas/SignedURL" + BlobDownload: + description: BLOB(Binary Large Object) download using presigned url + content: + application/json: + schema: + "$ref": "#/components/schemas/SignedURL" + ImageBinary: + description: An image + content: + image/*: + schema: + type: string + format: binary + +``` + #### parameters(components) From d66d31a8deb77057a5c5abef40468c7cbb7362dc Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Mon, 4 Dec 2023 13:28:45 +0900 Subject: [PATCH 07/19] requestBodies (#70) --- .../OpenAPI_Specification_3.0.3.md | 100 ++++++++++++------ 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 50274cbc..a89919c9 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -292,31 +292,18 @@ API のリクエストパラメータ(パスパラメータ、クエリパラ API のリクエストボディを記載する。 -* HTTP メソッドが `POST`, `PUT`, `PATCH` の場合にのみ指定する。 -* リクエストボディが必須の場合は `requestBody` 直下の `required` に `true` を指定する。 -* OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `component` オブジェクトとして任意の名称で定義し `$ref` で参照する。 -スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Req` を付与) -* `schema` オブジェクトの `type` は `object` を指定する。 - +* リクエストボディを記載する。仕様の[describing-request-body](https://swagger.io/docs/specification/describing-request-body/)の章にある通り、リクエストボディはPOST、PUT、PATCHで使用され、GET、DELETE、HEADには利用できない +* requestBodyの定義は、components/requestBodiesで宣言し、 `$refs` で参照する +* requestBodyの命名は、 `Req` というプレフィクスと、 `Body` というサフィックスで終える必要がある + ```yaml paths: /products: post: operation_id: post-products requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ReqPostProducts' + $ref: '#/components/requestBodies/ReqPostProductsBody' ... - -components: - schemas: - ReqPostProduct: - type: object - properties: - ... ``` ### responses @@ -336,16 +323,7 @@ paths: operation_id: post-products responses: '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ResPostProducts' - examples: - default: - value: - ... - example-1: - $ref: './examples/post-product.example.1.yaml' + $ref: '#/components/responses/RespPostProducts' '400': $ref: '#/components/responses/BadRequest' '401': @@ -365,12 +343,11 @@ paths: ... components: - schemas: - ResPostProducts: + responses: + RespPostProducts: type: object properties: ... - responses: BadRequest: ... ``` @@ -395,9 +372,14 @@ API 定義で利用する共通のデータモデルを定義 ```yml components: schemas: ... + parameters: ... securitySchemes: ... + requestBodies: responses: ... - parameters: ... + headers: ... + examples: ... + links: ... + callbacks: ... ``` @@ -455,9 +437,32 @@ components: ... ``` +#### requestBodies(components) + +* `requestBody` 直下の `required` は必須で `true` を指定する +* OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `component` オブジェクトとして任意の名称で定義し `$ref` で参照する。 +スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Req` を付与) +* `schema` オブジェクトの `type` は `object` を指定する。 + +```yaml +components: + schemas: + ReqPostProducts: + type: object + properties: + ... + requestBodies: + ReqPostProductsBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ReqPostProducts' +``` + #### responses(components) -レスポンスで複数のエンドポイントで横断的に用いるモデルを定義する。例えば、ステータスコード400~500系のエラーモデルがある。 +レスポンスの先頭には複数のエンドポイントで横断的に用いるモデルを定義する。例えば、ステータスコード400~500系のエラーモデルがある。 ​ ```yml components: @@ -482,7 +487,7 @@ components: ... ``` ​ -もし、正常系のレスポンスの例としてはファイルアップロード・ダウンロードなどが該当する。個別のアプリケーション要件でブレが少ないと複数のエンドポイントで用いられる場合に定義する。 +正常系のレスポンスの例としてはファイルアップロード・ダウンロードなどが該当する。個別のアプリケーション要件でブレが少ないと複数のエンドポイントで用いられる場合に定義する。オブジェクトのスキーマは、schemasに切り出して定義し、コード生成ツールのために型情報を付与させる。 ```yml components: @@ -516,9 +521,34 @@ components: schema: type: string format: binary - ``` +それらの後に、paths登場順にエンドポイント固有のレスポンスを定義する。レスポンスオブジェクトのスキーマは、schemasに切り出して定義する。 + +```yml +components: + schemas: + RespPostProductsSchema: + type: object + properties: + product_id: + type: string + ... + responses: + ... + RespPostProducts: + description: 商品登録の応答 + content: + application/json: + schema: + "$ref": "#/components/schemas/RespPostProductsSchema" + examples: + default: + value: + ... + example-1: + $ref: './examples/post-product.example.1.yaml' +``` #### parameters(components) From af8b1af07ee67106102cf7d9ca076edcd73b29a6 Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Fri, 15 Dec 2023 15:07:37 +0900 Subject: [PATCH 08/19] Add Callback, Links (#72) * Add Callback, Links Chapter --- .../OpenAPI_Specification_3.0.3.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index a89919c9..8052d642 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -589,6 +589,33 @@ securitySchemes: description: 'Bearer トークン認証' ``` +### links + +[links](https://swagger.io/docs/specification/links/) は OpenAPI 3.0 の新機能の1つで、あるAPIレスポンスの値を用いて、別のAPIを呼び出す方法を明示できるセクションである。 + +興味深い機能であり、APIのセマンティクスを伝えるのに有用であるが、本規約では記載しないことを推奨とする。 + +理由は下記の通りである。 + +- 業務システムでは、業務フローを抑えておけば、API操作フローの理解はそこまで難しくないことが多い + - 逆に、API同士の関係だけを示すだけでは業務モデリング図とのダブルメンテナンスになったり、中途半端になりうる +- [OAS 3.0 Support Backlog](https://github.com/swagger-api/swagger-ui/issues/3641) にあるように、2023/12/15時点ではSwagger-UIが対応していない + - linksを書いたと言って、APIドキュメントに影響しない + +### callbacks + +[callbacks](https://swagger.io/docs/specification/callbacks/) は OpenAPI 3.0 の新機能の1つで、APIサーバ側が指定されたコールバックURLを呼び出すという仕組みである。 + +仕様書には、ECショップで購入のたびにマネージャーに通知を送るといった、何かしらの処理をトリガーにコールバックURLを呼び出す例が示されている。 + +利便性は高い仕様だが、本規約では記載しないことを推奨とする。 + +理由は下記の通りである。 + +- コールバックURL呼び出しの、エラーハンドリングが難しい +- 業務システムでは欠損が許されない、または将来的に許されなくなる可能性があり、その場合にこの機能に頼ると想定以上の追加作業が発生する + +コールバックのような仕組みを実現するには、別途キューイングのメッセージサービスの利用などを検討する。 ## security From 1bd8334505aa9c5e4d286b366854e7e87ac29d78 Mon Sep 17 00:00:00 2001 From: Hiroki Takeda Date: Fri, 22 Dec 2023 13:14:44 +0900 Subject: [PATCH 09/19] Add parameters section (#71) --- .../OpenAPI_Specification_3.0.3.md | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 8052d642..2df147b9 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -550,29 +550,63 @@ components: $ref: './examples/post-product.example.1.yaml' ``` -#### parameters(components) +#### parameters -API 共通で利用するクエリパラメータを定義 +API 共通で利用するパラメータ(パスパラメータ、クエリパラメータ、ヘッダ, Cookie)を記載する。 + +##### パスパラメータ + +* API 全体で利用されるパスパラメータが必要なケースが想定されないため、原則定義しない。 +特定リソースの操作(例えば更新と削除)を行う際のリソースIDはパスパラメータとして再利用できるが、コンフリクトを避けるため原則共通化は行わない。 + +##### クエリパラメータ + +* API 全体で利用可能な共通のクエリパラメータを定義する (例: 検索数のlimit, offset) +* 命名は クエリパラメータ名に `Query` というプレフィクスを付与する形式を推奨する。 -* 規約 - * [ ] API 全体で利用可能な共通のクエリパラメータを定義 (例: 検索数のlimit,offset) ```yml +paths: + get: + /products: + parameters: + - $ref: '#/components/parameters/QueryLimit' + parameters: - limit: - name: limit - in: query - required: false - schema: - type: integer - description: 検索数上限 - -# 利用方法 + QueryLimit: + name: limit + in: query + required: false + schema: + type: integer + description: 検索数上限 +``` + +##### ヘッダパラメータ + +* 原則 `headers` を利用し、パラメータとしては定義しない。 + +##### Cookie パラメータ + +* API 全体で利用可能な共通のCookieパラメータを定義する。(例: CSRF用のトークン) +* 命名は Cookie パラメータ名に `Cookie` というプレフィクスを付与する形式を推奨する。 +* Cookie 認証を定義する場合は、`APIKey` を利用すること。 + +```yml paths: get: /products: parameters: - - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/CookieCSRFToken' + +parameters: + CookieCSRFToken: + name: csrftoken + in: cookie + required: true + schema: + type: string + description: CSRFトークン ``` ### securitySchemes From e31ab76aadf1ff6bc9e2a3e581611af8c623f28e Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Fri, 22 Dec 2023 15:38:48 +0900 Subject: [PATCH 10/19] Add validation at OpenAPISpec (#74) --- .../OpenAPI_Specification_2.0.md | 176 ++++++++++-------- .../OpenAPI_Specification_3.0.3.md | 136 +++++++++++++- 2 files changed, 231 insertions(+), 81 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md index 0740a0bc..918e9134 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md @@ -680,97 +680,113 @@ definitions: ## バリデーションについて +OpenAPI 定義を記載するにあたり、バリデーションをどこまで厳密に定義すべきかという議論はよく行いがちである。 + リクエストパラメータの各項目に対して、必須・型・桁・区分値・日付・正規表現のチェックが行える。レスポンスで用いるモデルについても同様に設定でき、`enum`, `pattern` 以外は API の利用者(クライアント)側の DB 設計などに必要な型桁情報を渡すのに有用であるため、できる限り詳しく指定する。 -- 必須(`required`) - - 必須パラメータのみ `required: true` を定義する -- デフォルト値(`default`) +### 必須 - - パラメータにデフォルト値がある場合は定義する +必須パラメータのみ `required: true` を定義する - ```yaml - # ex. enum - name: limit - type: number - format: integer - minimum: 1 - maximum: 100 - default: 20 - description: 検索結果の項目数上限(1~100が指定可能) - ``` +### デフォルト値 - - API 公開後に、default 値を変更してはならない(API の互換性が崩れるため)。もし変更する場合は、API のバージョンを上げること - -- 型(`type`) - - `string(文字列)`, `number(数値)`, `integer(整数値)`, `boolean(真偽値)` `array(配列)`, `file(ファイル)` のうちどれか指定する -- フォーマット(`format`) は以下の型の詳細情報を示すもので、可能な限り設定する - - `integer` (整数) - - `int32`, `int64` - - `number` (数値) - - `float`, `double` - - `string` (バイナリ) - - `byte`: Base64 でエンコードされた文字列 - - `binary`: バイト配列 - - `string` (日付) - - `date`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) full-date - - 項目名は `_on` を接尾辞につけることを推奨とする - - `date-time`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date-time - - 項目名は `_at` を接尾辞につけることを推奨とする - - `string` (その他) - - `password`: Swagger UI で入力が隠される - - その他、 `email`, `uuid` など Open API 仕様に存在しない任意のフォーマットを独自のドキュメント生成などのために記載しても良い -- 桁 - - 文字列 - - 最大桁数:`maxLength` - - 最小桁数:`minLength` - - 数値または整数値 - - 最小値(境界値を含む):`minimum` - - 最大値(境界値を含む):`maximum` - - 境界値を含まない場合のみ`exclusiveMinimum: true`または`exclusiveMaximum: true`を定義する。minimum, maximum で代用できる場合は利用しない - - 配列: - - 最大要素数:`maxItems` - - 最小要素数:`minItems` - - `required: true`の場合は原則として`minItems: 1`を定義する - - `uniqueItems` は必須で指定する(通常は一意であるべき) - - API 公開後に、レスポンスの `maxLength` を以前より大きい値に変更してはならい - - レスポンスの `maxLength` など API 利用者側システムの DB の ERD 定義のインプットになる事が多いため。もし行う場合は API のバージョンを上げることや、連携先に桁数変更の旨を調整するなどの考慮を行う -- 区分値(`enum`) - - - `description`に区分値の論理名を記載する +パラメータにデフォルト値がある場合は`default` を定義する。 - ```yaml - name: gender - type: string - enum: ["0", "1", "2", "9"] - description: | - 性別 - 0: 不明 - 1: 男 - 2: 女 - 9: 適用不能 - ``` +```yaml +# ex. enum +name: limit +type: number +format: integer +minimum: 1 +maximum: 100 +default: 20 +description: 検索結果の項目数上限(1~100が指定可能) +``` + +【注意】API 公開後に、default 値を変更してはならない(API の互換性が崩れるため)。もし変更する場合は、API のバージョンを上げること。 + + +### 型・フォーマット + +型(`type`)は `string(文字列)`, `number(数値)`, `integer(整数値)`, `boolean(真偽値)` `array(配列)`, `file(ファイル)` のうちどれか指定する. + +フォーマット(`format`) は以下の型の詳細情報を示すもので、可能な限り設定する。 + +- `integer` (整数) + - `int32`, `int64` +- `number` (数値) + - `float`, `double` +- `string` (バイナリ) + - `byte`: Base64 でエンコードされた文字列 + - `binary`: バイト配列 +- `string` (日付) + - `date`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) full-date(例: 2023-07-21) + - 項目名は `_on` を接尾辞につけることを推奨とする + - `date-time`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date-time(例: 2023-07-21T17:32:28Z) + - 項目名は `_at` を接尾辞につけることを推奨とする +- `string` (その他) + - `password`: Swagger UI で入力が隠される + - その他、 `email`, `uuid` など Open API 仕様に存在しない任意のフォーマットを独自のドキュメント生成などのために記載しても良い + +### 桁 - - **固定値** の場合も enum を 1 つだけ指定して表現する。この場合もレスポンスで利用する場合は指定しない +データ型によって、利用できる桁を指定する項目が異なる。可能な限り設定する。 - ```yaml - name: file_layout - type: string - enum: ["json"] - description: ファイルレイアウト - ``` +- 文字列 + - 最大桁数:`maxLength` + - 最小桁数:`minLength` +- 数値または整数値 + - 最小値(境界値を含む):`minimum` + - 最大値(境界値を含む):`maximum` + - 境界値を含まない場合のみ`exclusiveMinimum: true`または`exclusiveMaximum: true`を定義する。minimum, maximum で代用できる場合は利用しない +- 配列: + - 最大要素数:`maxItems` + - 最小要素数:`minItems` + - `required: true`の場合は原則として`minItems: 1`を定義する + - `uniqueItems` は必須で指定する(通常は一意であるべき) -- その他 +【注意】API 公開後に、レスポンスの `maxLength` を以前より大きい値に変更してはならない。レスポンスの `maxLength` など API 利用者側システムの DB の ERD 定義のインプットになる事が多いため。もし行う場合は API のバージョンを上げることや、連携先に桁数変更の旨を調整するなどの考慮を行う。 - - 正規表現で表現できる文字列は`pattern`を利用して定義する。桁や区分値で代替できる場合は、`pattern` を用いない - - 例: +### 区分値 - ```yaml - remind_time: - type: string - description: リマインド時刻。(hh:mm)形式 - example: 23:59 - pattern: "^(2[0-3]|[01][0-9]):([0-5][0-9])$" - ``` +区分値の場合は `enum` 属性を利用し、`description`には区分値の論理名を記載する。 + +```yaml +name: gender +type: string +enum: ["0", "1", "2", "9"] +description: | + 性別 + 0: 不明 + 1: 男 + 2: 女 + 9: 適用不能 +``` + +### 固定値 + +**固定値** の場合も enum を 1 つだけ指定して表現する。この場合もレスポンスで利用する場合は指定しない + +```yaml +name: file_layout +type: string +enum: ["json"] +description: ファイルレイアウト +``` + +### その他(正規表現) + +正規表現で表現できる文字列は`pattern`を利用して定義する。桁や区分値で代替できる場合は、`pattern` を用いない + +例: + +```yaml +remind_time: + type: string + description: リマインド時刻。(hh:mm)形式 + example: 23:59 + pattern: "^(2[0-3]|[01][0-9]):([0-5][0-9])$" +``` ## ファイルアップロード diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 2df147b9..34dec3cd 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -705,7 +705,141 @@ tags: - 要素規約が「どのように書くか」に焦点を当てているのに対し、そもそも「何を書くか」といった部分については、切り出して要素規約から refer させる形の方がスッキリするかも。要素規約に入れられるならそれでよし。描きながらバランス見て。 -## バリデーションをどこまで厳密に定義するか +## バリデーションについて + +OpenAPI 定義を記載するにあたり、バリデーションをどこまで厳密に定義すべきかという議論はよく行いがちである。 + +リクエストパラメータの各項目に対して、必須・型・桁・区分値・日付・正規表現のチェックが行える。レスポンスで用いるモデルについても同様に設定でき、`enum`, `pattern` 以外は API の利用者(クライアント)側の DB 設計などに必要な型桁情報を渡すのに有用であるため、できる限り詳しく指定する。 + +### 必須 + +必須パラメータのみ `required: true` を定義する + +### デフォルト値 + +パラメータにデフォルト値がある場合は`default` を定義する。 + +```yaml +# ex. enum +name: limit +type: number +format: integer +minimum: 1 +maximum: 100 +default: 20 +description: 検索結果の項目数上限(1~100が指定可能) +``` + +【注意】API 公開後に、default 値を変更してはならない(API の互換性が崩れるため)。もし変更する場合は、API のバージョンを上げること。 + + +### 型・フォーマット + +型(`type`)は `string(文字列)`, `number(数値)`, `integer(整数値)`, `boolean(真偽値)` `array(配列)` のうちどれか指定する. + +フォーマット(`format`) は以下の型の詳細情報を示すもので、可能な限り設定する。 + +- `integer` (整数) + - `int32`, `int64` +- `number` (数値) + - `float`, `double` +- `string` (バイナリ) + - `byte`: Base64 でエンコードされた文字列 + - `binary`: バイト配列 +- `string` (日付) + - `date`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) full-date(例: 2023-07-21) + - 項目名は `_on` を接尾辞につけることを推奨とする + - `date-time`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date-time(例: 2023-07-21T17:32:28Z) + - 項目名は `_at` を接尾辞につけることを推奨とする +- `string` (その他) + - `password`: Swagger UI で入力が隠される + - その他、 `email`, `uuid` など Open API 仕様に存在しない任意のフォーマットを独自のドキュメント生成などのために記載しても良い + +OpenAPI 3.0では 2.0 に存在した `file` type は存在しない。もし同等の指定をしたい場合は、以下の様に指定する。 + +```yaml +type: string +format: binary # binary file contents +``` + +### 桁 + +データ型によって、利用できる桁を指定する項目が異なる。可能な限り設定する。 + +- 文字列 + - 最大桁数:`maxLength` + - 最小桁数:`minLength` +- 数値または整数値 + - 最小値(境界値を含む):`minimum` + - 最大値(境界値を含む):`maximum` + - 境界値を含まない場合のみ`exclusiveMinimum: true`または`exclusiveMaximum: true`を定義する。minimum, maximum で代用できる場合は利用しない +- 配列: + - 最大要素数:`maxItems` + - 最小要素数:`minItems` + - `required: true`の場合は原則として`minItems: 1`を定義する + - `uniqueItems` は必須で指定する(通常は一意であるべき) + +【注意】API 公開後に、レスポンスの `maxLength` を以前より大きい値に変更してはならない。レスポンスの `maxLength` など API 利用者側システムの DB の ERD 定義のインプットになる事が多いため。もし行う場合は API のバージョンを上げることや、連携先に桁数変更の旨を調整するなどの考慮を行う。 + +### 区分値 + +区分値の場合は `enum` 属性を利用し、`description`には区分値の論理名を記載する。 + +```yaml +name: gender +type: string +enum: ["0", "1", "2", "9"] +description: | + 性別 + 0: 不明 + 1: 男 + 2: 女 + 9: 適用不能 +``` + +OpenAPI 3.0 では区分値の再利用ができるため、横断的に用いる区分値はcomponents側で定義する。 + +```yaml +paths: + /products: + get: + parameters: + - in: query + name: gender + required: true + schema: + $ref: '#/components/schemas/Gender' +components: + schemas: + Gender: + type: string + enum: ["0", "1", "2", "9"] +``` + +### 固定値 + +**固定値** の場合も enum を 1 つだけ指定して表現する。この場合もレスポンスで利用する場合は指定しない + +```yaml +name: file_layout +type: string +enum: ["json"] +description: ファイルレイアウト +``` + +### その他(正規表現) + +正規表現で表現できる文字列は`pattern`を利用して定義する。桁や区分値で代替できる場合は、`pattern` を用いない + +例: + +```yaml +remind_time: + type: string + description: リマインド時刻。(hh:mm)形式 + example: 23:59 + pattern: "^(2[0-3]|[01][0-9]):([0-5][0-9])$" +``` ## 値が存在しないという状態の表現 From 6f722e51f71dfd8938f820df4b9a0ce9666ac527 Mon Sep 17 00:00:00 2001 From: Hiroki Takeda Date: Mon, 25 Dec 2023 10:24:27 +0900 Subject: [PATCH 11/19] Add headers section (#73) --- .../OpenAPI_Specification_3.0.3.md | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 34dec3cd..8b5cf03d 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -584,7 +584,25 @@ parameters: ##### ヘッダパラメータ -* 原則 `headers` を利用し、パラメータとしては定義しない。 +* API 全体で利用可能な共通のリクエストヘッダを定義する。 +* 命名は ヘッダ名に `Header` というプレフィクスを付与する形式を推奨する。 + +```yml +paths: + post: + /products: + parameters: + - $ref: '#/components/parameters/HeaderContentType' +... +components: + parameters: + HeaderContentType: + name: Content-Type + in: header + schema: + type: string + required: true +``` ##### Cookie パラメータ @@ -598,15 +616,41 @@ paths: /products: parameters: - $ref: '#/components/parameters/CookieCSRFToken' +... +components: + parameters: + CookieCSRFToken: + name: csrftoken + in: cookie + required: true + schema: + type: string + description: CSRFトークン +``` -parameters: - CookieCSRFToken: - name: csrftoken - in: cookie - required: true - schema: - type: string - description: CSRFトークン +### headers + +API 共通で利用するレスポンスヘッダを記載する。 + +* 命名は ヘッダ名からハイフンを除去した形式を推奨する。 + +```yml +paths: + get: + /products: + responses: + '200': + headers: + ContentType: + $ref: '#/components/headers/ContentType' +... + +components: + headers: + ContentType: + description: the original media type of the resource + schema: + type: string ``` ### securitySchemes From 7077614bc88a9407f44fabe54b6bedf9da8f9b5f Mon Sep 17 00:00:00 2001 From: miyazaki2314 Date: Thu, 28 Dec 2023 14:58:36 +0900 Subject: [PATCH 12/19] add components/schema --- .../OpenAPI_Specification_3.0.3.md | 109 +++++++++++------- 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 8b5cf03d..9fe3f314 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -385,56 +385,81 @@ components: ### schemas -* API 定義で共通で利用するスキーマを定義 +* API定義共通で利用するスキーマを定義する +* schemasに定義する項目はリソースやエラー等のドメインオブジェクトのみとし、リクエストパラメータやレスポンスパラメータは`parameter`や`requestBody`、`responses`に記載する。 + * paths.requestBodyから直接参照されるリクエストパラメータオブジェクトは`requestBodies`に定義する。 + * pathsから直接参照されるレスポンスパラメータは`responses`に定義する。また400や500などのエラーレスポンスも`responses`に定義する。 + * HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータに相当するものは`parameter`に定義する。 + * 上記いずれにも該当しないuserやidなどのリソース、エラーを示すオブジェクトは`schemas`に定義する。 * 規約 - * [ ] リソース名はアッパーキャメルケースで定義 - * [ ] リソース名は単数形で定義 - * [ ] `type` に複数の型定義の指定不可 - * [ ] `type: null`は原則として利用しない - * [ ] `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない - * [ ] schemas 以下は [リクエストボディ](#リクエストボディ)、[レスポンス](#レスポンス)、[リソース](#リソース) の順に定義 + * リソース名はアッパーキャメルケースで定義 + * リソース名は単数形で定義 + * `type` に複数の型定義の指定不可 + * `type: null`は原則として利用しない // そうなの?? + * `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない ```yaml -Pagination: - type: object - properties: - total_counts: - type: integer - offset: - type: integer - limit: - type: integer - required: - - total_counts - - offset - - limit -``` - -スキーマ定義のモデルは以下3種類 - -* [リクエストボディ](#リクエストボディ) -* [レスポンス](#レスポンス) -* [リソース](#リソース) - -```yaml -# ソート順は以下の通り components: schemas: - ReqGetProduct: - type: object - ... - ReqPostProduct: - type: object - ... - ResGetProduct: + # リソースを示すオブジェクト + Product: type: object - ... - ResPostProduct: + properties: + ... + User: type: object - ... - Product: + properties: + # エラーを示すオブジェクト + ProblemDetailError: type: object - ... + properties: + ... + # リクエストパラメータやレスポンスパラメータはrequestBodies、もしくはresponsesに記載する。 + # ReqPostProductsBodyParam: + # type: object + # properties: + # ... + parameter: + # HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータ定義 + QueryLimit: + name: limit + in: query + required: false + schema: + type: integer + description: 検索数上限 + requestBodies: + # 各API定義(paths.requestBody)から参照されるレスポンス定義 + ReqPostProductsBody: + required: true + content: + application/json: + schema: + type: object + properties: + product: + $ref: '#/components/schemas/Product' + ... + responses: + # 各API定義(paths)から参照されるレスポンス定義 + RespPostProducts: + description: 商品登録の応答 + content: + application/json: + schema: + type: object + properties: + product: + $ref: '#/components/schemas/Product' + ... + # 共通で使用するエラーレスポンス定義 + BadRequest: + description: 400 Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/ProblemDetailError" + ``` #### requestBodies(components) From c7342f1dc42d04af3f2589f29aa5dbc83d2e30d0 Mon Sep 17 00:00:00 2001 From: Junki Mano Date: Thu, 28 Dec 2023 16:47:01 +0900 Subject: [PATCH 13/19] opentelemetry (#78) --- .../OpenAPI_Specification_2.0.md | 9 +++++ .../OpenAPI_Specification_3.0.3.md | 34 +++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md index 918e9134..8975a2eb 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md @@ -854,6 +854,15 @@ CORS(Cross-Origin Resource Sharing)のために、options メソッドの追 [^1]: https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/enable-cors-for-resource-using-swagger-importer-tool.html +## OpenTelemetry Traceparent HTTP Header + +OpenOpenTelemetryで用いるられる[traceparent](https://www.w3.org/TR/trace-context/) のリクエストヘッダーはOpenAPIで **原則不要** とする。 + +理由は以下である。 + +- OpenTelemetryが定めるヘッダー類は、API横断的に設定されるべきものであり、ミドルウェアやフレームワーク側などでの一律の制御を推奨するため +- 記載することにより、OpenOpenTelemetryに対応していることを明記し開発者に周知できるメリットより、各アプリ開発者が生成されたコードで悩んだり、誤解されることを回避したいため + ## API のバージョン管理 Swagger 定義で以下の変更を行う場合は、利用するコード生成の動作によってはクライアントにとって互換性を失う破壊的変更であることがあるため、変更は調整の上で行うか、バージョンを上げることを考える。 diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 8b5cf03d..4a5b6e8c 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -745,9 +745,31 @@ tags: # 設計上のポイント -(相談事項) +## CORS + +CORS(Cross-Origin Resource Sharing)のために、options メソッドの追記は **原則不要** とする。 + +理由は以下である。 + +- サーバ側 + - options メソッド対応は、API 使用ではなく実装レベルの機能横断的な処理(Java における Servlet Filter や Spring の Interceptor、Go における Middleware など)で行うことが大半であり、コード生成が不要 +- クライアント側 + - options メソッドを用いるのはクライアントがブラウザであり、クライアントのアプリケーションコードが明示的にアクセスしないため、コード生成が不要 +- 使用面として + - ` Access-Control-Allow-Origin` がどのような値を返すか、呼び出し元によって動的な値を返したい場合があり、記載が困難なケースがある + +ただし、Amazon API Gateway のようなサービスを利用する場合は、options メソッドの記載が必須である場合は除く[^1]。 + +[^1]: https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/enable-cors-for-resource-using-swagger-importer-tool.html + +## OpenTelemetry Traceparent HTTP Header -- 要素規約が「どのように書くか」に焦点を当てているのに対し、そもそも「何を書くか」といった部分については、切り出して要素規約から refer させる形の方がスッキリするかも。要素規約に入れられるならそれでよし。描きながらバランス見て。 +OpenOpenTelemetryで用いるられる[traceparent](https://www.w3.org/TR/trace-context/) のリクエストヘッダーはOpenAPIで **原則不要** とする。 + +理由は以下である。 + +- OpenTelemetryが定めるヘッダー類は、API横断的に設定されるべきものであり、ミドルウェアやフレームワーク側などでの一律の制御を推奨するため +- 記載することにより、OpenOpenTelemetryに対応していることを明記し開発者に周知できるメリットより、各アプリ開発者が生成されたコードで悩んだり、誤解されることを回避したいため ## バリデーションについて @@ -1689,14 +1711,6 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 -# 各種ツール、サービスとの統合 - -特定のツール、サービスに依存する拡張系 - -## oapi-codegen - -## Amazon API Gateway - --- # License From b6b07cd456f1b90afb916cc7a28ee4a7528c0825 Mon Sep 17 00:00:00 2001 From: future-fujita <81731712+future-fujita@users.noreply.github.com> Date: Thu, 28 Dec 2023 17:08:25 +0900 Subject: [PATCH 14/19] docs: add externalDocs section (#75) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fujita Haruka(藤田 春佳) --- .../OpenAPI_Specification_3.0.3.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 4a5b6e8c..c7b2e661 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -741,7 +741,21 @@ tags: ## externalDocs +Schema定義, Paths配下の各API定義, OASのトップ階層などで、参照情報としてのURLを指定し表示が可能。ただし、`description` にてリンクURLを記載する方が、複数リンクを指定可能であるなど自由度が高く使いやすい。そのため、参照先URLリンクの記載には、`externalDocs` ではなく `description` の利用を推奨する。 +```yaml +# 推奨 +info: + description: |- + Some useful links: + - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) + - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) + +# 特別な場合を除き非推奨 +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +``` # 設計上のポイント From 157789b40f586ac7ce48dc68a1ea7df38cb88d03 Mon Sep 17 00:00:00 2001 From: future-fujita <81731712+future-fujita@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:10:35 +0900 Subject: [PATCH 15/19] =?UTF-8?q?fix:=20=E6=8C=87=E6=91=98=E4=BA=8B?= =?UTF-8?q?=E9=A0=85=E3=81=AE=E5=8F=8D=E6=98=A0=20=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fujita Haruka(藤田 春佳) --- .../OpenAPI_Specification_3.0.3.md | 208 +++++++++--------- .../reference/divided_files_sample.zip | Bin 7267 -> 6716 bytes 2 files changed, 100 insertions(+), 108 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index c7b2e661..fbb7094e 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -1282,28 +1282,31 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 name: MIT servers: - url: http://petstore.swagger.io/v1 + tags: + - name: pets + description: Everything about your Pets paths: /pets: get: - $ref: "./pets_get/pets_get.yaml" + $ref: "./pets_get/pets_get.yaml#/operation" post: - $ref: "./pets_post/pets_post.yaml" + $ref: "./pets_post/pets_post.yaml#/operation" /pets/{petId}: get: - $ref: "./pets-pet-id_get/pets-pet-id_get.yaml" + $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/operation" components: schemas: ResPetsGet: - $ref: "./pets_get/response.yaml" + $ref: "./pets_get/pets_get.yaml#/components/schemas/ResPetsGet" ReqPetsPost: - $ref: "./pets_post/request.yaml" + $ref: "./pets_post/pets_post.yaml#/components/schemas/ReqPetsPost" ResPetsPetIdGet: - $ref: "./pets-pet-id_get/response.yaml#/ResPetsPetIdGet" + $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/ResPetsPetIdGet" PetDetail: - $ref: "./pets-pet-id_get/response.yaml#/PetDetail" + $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/PetDetail" Pedigree: - $ref: "./pets-pet-id_get/response.yaml#/Pedigree" + $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/Pedigree" Pet: $ref: "./common/pet.yaml" Error: @@ -1316,132 +1319,118 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 ファイル分割例: ディレクトリ構成 ```sh - │ openapi.gen.yaml - │ openapi.yaml + ├─openapi.gen.yaml + ├─openapi.yaml │ ├─common - │ error.yaml - │ pet.yaml + │ ├─error.yaml + │ └─pet.yaml │ ├─pets-pet-id_get - │ │ pets-pet-id_get.yaml - │ │ response.yaml - │ │ + │ ├─pets-pet-id_get.yaml │ └─examples - │ res_example1.yaml + │ └─res_example1.yaml │ ├─pets_get - │ │ pets_get.yaml - │ │ response.yaml - │ │ + │ ├─pets_get.yaml │ └─examples - │ res_example1.yaml - │ res_example2.yaml + │ ├─res_example1.yaml + │ └─res_example2.yaml │ └─pets_post - │ pets_post.yaml - │ request.yaml - │ + ├─pets_post.yaml └─examples - req_example1.yaml + └─req_example1.yaml ``` -- `openapi.yaml` の `paths` に記載したAPIファイルは以下のように作成する。`schema` にて `openapi.yaml` に指定したキー(`../openapi.yaml#/components/schemas/ResPetsPetIdGet`)を参照する。 +- `openapi.yaml` の `paths` に記載したAPIファイルは以下のように作成する。 +- 複数API間に共通のモデルについては `openapi.yaml` に指定したキー(`../openapi.yaml#/components/schemas/Pet`)を参照する。 - `examples` には、各APIのテストケースIDをキーとして指定(`ResExample1`)し、`value` に該当するテストケースのデータファイルパスを指定(`./examples/res_example1.yaml`)する。ファイル名は、指定したキーをスネークケースに変換したものを使用するとよい。
API別ファイルの記載例: pets-pet-id_get.yaml ```yaml - summary: Details for a pet - operationId: get-pets-pet-id - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - "200": - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "../openapi.yaml#/components/schemas/ResPetsPetIdGet" - examples: - ResExample1: - value: - $ref: "./examples/res_example1.yaml" - "404": - description: not found error - content: - application/json: - schema: - $ref: "../openapi.yaml#/components/schemas/Error" - "500": - description: unexpected error - content: - application/json: - schema: - $ref: "../openapi.yaml#/components/schemas/Error" - ``` - -
- -- `schema` ファイルの例は以下の通り。 - - 複数API間に共通のモデルは `openapi.yaml` に指定したキーを指定する(`../openapi.yaml#/components/schemas/Pet`)。 - - ネストしているモデルは `openapi.yaml` に指定したキーを経由して参照できるようにする(`../openapi.yaml#/components/schemas/PetDetail`, `../openapi.yaml#/components/schemas/Pedigree`)。 - -
- schemaファイル記載例: pets-pet-id_get/response.yaml + operation: + summary: Details for a pet + operationId: get-pets-pet-id + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/ResPetsPetIdGet" + examples: + ResExample1: + value: + $ref: "./examples/res_example1.yaml" + "404": + description: not found error + content: + application/json: + schema: + $ref: "../openapi.yaml#/components/schemas/Error" + "500": + description: unexpected error + content: + application/json: + schema: + $ref: "../openapi.yaml#/components/schemas/Error" - ```yaml - ResPetsPetIdGet: - required: - - pet - - pet_detail - type: object - properties: - pet: - $ref: "../openapi.yaml#/components/schemas/Pet" - pet_detail: - $ref: "../openapi.yaml#/components/schemas/PetDetail" - - PetDetail: - type: object - properties: - breeder: - type: string - date_of_birth: - type: string - format: date - pedigree: - $ref: "../openapi.yaml#/components/schemas/Pedigree" - - Pedigree: - required: - - registration_no - - date_of_registration - - pedigree_image - type: object - properties: - registration_no: - type: integer - format: int64 - date_of_registration: - type: string - format: date - pedigree_image: - type: string + components: + schemas: + ResPetsPetIdGet: + required: + - pet + - pet_detail + type: object + properties: + pet: + $ref: "../openapi.yaml#/components/schemas/Pet" + pet_detail: + $ref: "#/components/schemas/PetDetail" + PetDetail: + type: object + properties: + breeder: + type: string + date_of_birth: + type: string + format: date + pedigree: + $ref: "#/components/schemas/Pedigree" + Pedigree: + required: + - registration_no + - date_of_registration + - pedigree_image + type: object + properties: + registration_no: + type: integer + format: int64 + date_of_registration: + type: string + format: date + pedigree_image: + type: string ```
+ - OpenAPIの使用用途により、分割ファイルを1つのファイルにまとめる必要がある場合には、例えば[swagger-cli](https://apitools.dev/swagger-cli/)を使用して以下コマンドを実行する。 ```bash @@ -1460,6 +1449,9 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 name: MIT servers: - url: 'http://petstore.swagger.io/v1' + tags: + - name: pets + description: Everything about your Pets paths: /pets: get: diff --git a/documents/forOpenAPISpecification/reference/divided_files_sample.zip b/documents/forOpenAPISpecification/reference/divided_files_sample.zip index 6ffeea93ed2a3e5b245ee962a2c7374719c9725c..439036138aa93945ecb996145c87b8b8362f16ba 100644 GIT binary patch delta 3527 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 Gl>Y^8e?EZ# delta 4054 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 ogUfrF{{iq Date: Fri, 12 Jan 2024 15:06:48 +0900 Subject: [PATCH 16/19] RFC9205 (#80) --- .../forOpenAPISpecification/API_Design.md | 4 +- .../OpenAPI_Specification_2.0.md | 10 +-- .../OpenAPI_Specification_3.0.3.md | 66 +++++++++++++++---- 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/documents/forOpenAPISpecification/API_Design.md b/documents/forOpenAPISpecification/API_Design.md index f2317d12..f93fd98a 100644 --- a/documents/forOpenAPISpecification/API_Design.md +++ b/documents/forOpenAPISpecification/API_Design.md @@ -8,9 +8,9 @@ OpenAPI Specification 規約を利用するに当たり、想定する Web API ## HTTP ステータス -原則として[RFC 7231](https://tools.ietf.org/html/rfc7231#section-6)で定義されているレスポンスステータスコードを利用します。 +[RFC 7231](https://tools.ietf.org/html/rfc7231#section-6)で定義されているレスポンスステータスコードを利用します。 -ユースケース別に利用すべき HTTP ステータスコードを記載します。 +[RFC9205](https://datatracker.ietf.org/doc/html/rfc9205)([日本語訳](https://tex2e.github.io/rfc-translater/html/rfc9205.html#section-4.6))の方針に原則則る。ユースケース別に利用すべき HTTP ステータスコードを記載します。 ### 共通 diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md index 8975a2eb..6e0a8571 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_2.0.md @@ -17,19 +17,15 @@ meta: [OpenAPI Specification 2.0(Swagger, OAS2)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md)定義についてのコーディング規約をまとめます。より新しいバージョンとして OAS 3.0.3 規約(作成中)がありますので、ご注意ください。 -## 適用箇所 +本規約の[前提条件](prerequisite.md)に従い作成されています。ToC向けのLSUDs(Large Set of Unknown Developers)なWeb APIにはマッチしない可能性があります。 -本規約は以下の[前提条件](prerequisite.md)で作られたものである。 - -## Web API 自体の設計について - -[API 設計標準](API_Design.md) に準じる。 +Web API自体の設計については範囲外としますが、[API 設計標準](API_Design.md)に利用するステータスコードなどは記載しています。 ## ファイルフォーマット [ファイルフォーマット規約](file_standards.md)に準じる。 -# 要素規約 +## 要素規約 Swagger の基本構造は以下の、swagger・info・host・basePath・schemes・paths・definitions から構成される。 diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index fbb7094e..b5f6962a 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -17,19 +17,15 @@ meta: [OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則った API ドキュメントを記述する際のコーディング規約をまとめます。古いバージョンとして[OpenAPI Specification 2.0 の規約](OpenAPI_Specification_2.0.md)がありますので、v2 をご利用の方はこちらをご参照ください。 -## 適用箇所 +本規約の[前提条件](prerequisite.md)に従い作成されています。ToC向けのLSUDs(Large Set of Unknown Developers)なWeb APIにはマッチしない可能性があります。 -本規約は以下の[前提条件](prerequisite.md)で作られたものである。 - -## Web API 自体の設計について - -[API 設計標準](API_Design.md) に準じる。 +Web API自体の設計については範囲外としますが、[API 設計標準](API_Design.md)に利用するステータスコードなどは記載しています。 ## ファイルフォーマット [ファイルフォーマット規約](file_standards.md)に準じる。 -# OpenAPI ドキュメントの構成要素 +## OpenAPI ドキュメントの構成要素 OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ、ルートオブジェクトは下記の要素で構成される。 各種規約を読み進めるにあたってあらかじめ大まかに理解しておくことを推奨する。 @@ -46,13 +42,6 @@ OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ | tags | | 各種 API をグルーピングするためのタグ | | externalDocs | | 追加の外部ドキュメント | -## コンポーネント化 - -(相談事項) - -- どの単位でコンポーネント化をするか、については個別の規約に入る前に全体として触れておいた方がいいと思う。 -- 結構難しいネタ - # 要素規約 先述した OpenAPI ドキュメントを構成する要素別に具体的なコーディング規約を記載する。 @@ -759,6 +748,55 @@ externalDocs: # 設計上のポイント +## ファイルアップロード + +Web API におけるファイルアップロードのよく利用される実装手段は、大きく分けて以下の 3 手法に分類できます + +1. ファイルのコンテンツを Base64 などにエンコードして、JSON の項目として設定し、リクエストボディで送る + - メリット: 通常の JSON を扱うのとほぼ変わらないため楽。サムネイルなど限定されたユースケースの場合に向く + - デメリット: 巨大なファイルを扱う場合などサーバリソース負荷が懸念。Base64 に変換する分 CPU 負荷は余計にかかる。ペイロードが膨れるためモバイルなどのクライアントでは帯域利用での懸念がある +2. multipart/form-data ファイルを送信する + - メリット: ファイルを Base64 に変換するといった作業が不要 + - デメリット: ブラウザ以外のクライアントにとって手間がかかる +3. アップロード用に用いる、オブジェクトストレージの Signed URL を発行し、クライアントから直接ファイルをアップロードしてもらう + - 次の流れを想定(Signed URL を取得 -> ファイルアップロード -> ファイルに紐づかせるキーや属性情報などを登録) + - Amazon API Gateway を利用する場合は、2023 年 6 月時点で[ペイロード上限が 10MB](https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html)、[AWS Lambda でもペイロード制限がある](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html#api-requests)ため、許容するファイルサイズによってはこの手法一択となる + - メリット: オブジェクトストレージの可用性・信頼性を享受できる + - デメリット: アップロードするために複数の API エンドポイント呼び出しが必要なため、煩雑である + - 2023 年 6 月に AWS ブログでこの方式について解説した記事が出たので、詳細は参照ください。 + - [https://aws.amazon.com/jp/blogs/news/large-size-files-transferring-by-serverless-s3presignedurl-and-clientside-javascript/](https://aws.amazon.com/jp/blogs/news/large-size-files-transferring-by-serverless-s3presignedurl-and-clientside-javascript/) + +本規約でファイルアップロードについて上記の 3. Signed URL を推奨する。API 呼び出しとしては次のようなフローとする。 + +```mermaid +sequenceDiagram +participant A as クライアント +participant B as Web APIサーバ +participant C as オブジェクトストレージ + +A->>B: ①アップロード先URL取得 + B->>C: Signed URL発行 + C-->>B: Signed URL + B-->>A: アップロードURL、受付ID(加えて、アップロードで指定したいHTTP Methodや必要なリクエストヘッダーがあれば応答の項目に追加する) + +A->>C: ②ファイルアップロード + +A->>B: ③ファイルアップロード完了(受付ID、キー、属性) + B-->>A: 受付完了 +``` + +フローの ①、② はアプリケーション固有の紐づけルールにおいて Web API を設計すれば良いため、本規約で YAML の設定例は記載しない。フロー ② については Signed URL を用いたアップロードであり、アプリケーションの Web API 定義を書く必要はない。もし、監査ログなどのガバナンス上、直接オブジェクトストレージへの書き込みを許容されないケースは、B で Signed URL に相当する書き込み先を提供し、B を経由してファイルをアップロードする。 + +上記どちらのケースも OpenAPI 定義としてはシンプルであるため、記述例は割愛する。 + +## ファイルダウンロード + +ファイルアップロードと同様、オブジェクトストレージの Signed URL 経由を経由してのダウンロードさせる手法を推奨する。Web API としてはオブジェクトストレージにダウンロード用のファイルを書き込み、クライアントが取得するための Signed URL をレスポンスの JSON 項目に渡す方式である。 + +もし、サムネイルやアイコン画像など、ファイル容量がごく小さい場合は Base64 にエンコードして JSON に埋め込んで渡しても良い。線引をどこに設置するかは本規約で定義しない。 + +どちらのケースも OpenAPI 定義としてはシンプルであるため、記述例は割愛する。 + ## CORS CORS(Cross-Origin Resource Sharing)のために、options メソッドの追記は **原則不要** とする。 From 22d9b21b2a5d36bfbb2e98f1845fe17ebdfeb627 Mon Sep 17 00:00:00 2001 From: shotashota Date: Fri, 19 Jan 2024 16:24:23 +0900 Subject: [PATCH 17/19] fix --- .../OpenAPI_Specification_3.0.3.md | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 9fe3f314..5b38b7d6 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -386,16 +386,22 @@ components: ### schemas * API定義共通で利用するスキーマを定義する -* schemasに定義する項目はリソースやエラー等のドメインオブジェクトのみとし、リクエストパラメータやレスポンスパラメータは`parameter`や`requestBody`、`responses`に記載する。 - * paths.requestBodyから直接参照されるリクエストパラメータオブジェクトは`requestBodies`に定義する。 - * pathsから直接参照されるレスポンスパラメータは`responses`に定義する。また400や500などのエラーレスポンスも`responses`に定義する。 +* schemasに定義する項目はリソースやエラー等のドメインオブジェクトのみとし、リクエストパラメータやレスポンスパラメータは`parameter`や`requestBodies`、`responses`に記載する。 + * `paths.requestBodies`から直接参照されるリクエストパラメータオブジェクトは`requestBodies`に定義する。 + * `paths`から直接参照されるレスポンスパラメータは`responses`に定義する。また400や500などのエラーレスポンスも`responses`に定義する。 * HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータに相当するものは`parameter`に定義する。 + * レスポンスヘッダーは`headers`にて定義する。 * 上記いずれにも該当しないuserやidなどのリソース、エラーを示すオブジェクトは`schemas`に定義する。 + * 各APIのリクエストレスポンスオブジェクトは可能な限り、`parameter`,`requestBodies`,`responses`に定義する方針とし、API固有のオブジェクト(所謂`ReqXXX`、`ResXXX`等)は`schemas`には定義しない。 + * ただし、オブジェクトがネストしてしまう場合はAPI固有のオブジェクトであっても`schemas`に定義する。 + ※定義するオブジェクトの`properties`配下に更に`type: object`が定義されしまう(ネストしてしまう)と生成ツールによってはうまく型が生成されないため。 + * 規約 * リソース名はアッパーキャメルケースで定義 * リソース名は単数形で定義 * `type` に複数の型定義の指定不可 - * `type: null`は原則として利用しない // そうなの?? + * `type: null`は原則として利用せず、undefinedを利用する。 + [差分更新APIの場合](#差分更新-API-の場合)にあるとおり、空更新を行う場合は空文字を利用する。 * `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない ```yaml @@ -472,7 +478,7 @@ components: ```yaml components: schemas: - ReqPostProducts: + Product: type: object properties: ... @@ -482,7 +488,7 @@ components: content: application/json: schema: - $ref: '#/components/schemas/ReqPostProducts' + $ref: '#/components/schemas/Product' ``` #### responses(components) @@ -553,7 +559,7 @@ components: ```yml components: schemas: - RespPostProductsSchema: + Product: type: object properties: product_id: @@ -566,7 +572,7 @@ components: content: application/json: schema: - "$ref": "#/components/schemas/RespPostProductsSchema" + "$ref": "#/components/schemas/Product" examples: default: value: From 9470e2f91b6db5614d1410ee65b5844f392b54bc Mon Sep 17 00:00:00 2001 From: shotashota Date: Sat, 20 Jan 2024 21:21:15 +0900 Subject: [PATCH 18/19] fix reference (#82) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * referenceを修正 * openapi.yamlにshcemasを定義する理由を追記 --- .../OpenAPI_Specification_3.0.3.md | 537 +++++++++--------- .../reference/divided_files_sample.zip | Bin 6716 -> 7022 bytes 2 files changed, 257 insertions(+), 280 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index e40f5762..ab0f3076 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -1335,9 +1335,9 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 - 機能単位(path, method単位)にディレクトリを作成して、それぞれの定義ファイルを格納する。ディレクトリ名は `{path}_{method}` とすると管理し易い。 - `components` の `schemas` には、 - - 各APIごとのリクエスト/リスポンスモデルを切り出して記載する(例えば、`ResPetsPetIdGet`)。 - API間で同じモデルを使用する場合は共通化して記載する(例えば、`Pet`)。 - 各APIのリクエスト/リスポンスモデルの中で、モデルがネストする場合は、各モデルの単位で書き出す(例えば、`PetDetail`, `Pedigree`)。 + - ※schemasのモデルの中身は別ファイルに定義が可能だが、大本のopenapi.yamlにも命名のみ定義が必要。openapi.yamlの定義が無いとswaggerUIで確認した際にschemas定義が見えなくなってしまう。
ファイル分割例: openapi.yaml @@ -1363,15 +1363,8 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 /pets/{petId}: get: $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/operation" - components: schemas: - ResPetsGet: - $ref: "./pets_get/pets_get.yaml#/components/schemas/ResPetsGet" - ReqPetsPost: - $ref: "./pets_post/pets_post.yaml#/components/schemas/ReqPetsPost" - ResPetsPetIdGet: - $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/ResPetsPetIdGet" PetDetail: $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/PetDetail" Pedigree: @@ -1423,8 +1416,8 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 ```yaml operation: - summary: Details for a pet operationId: get-pets-pet-id + summary: Details for a pet tags: - pets parameters: @@ -1440,7 +1433,7 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 content: application/json: schema: - $ref: "#/components/schemas/ResPetsPetIdGet" + $ref: "#/components/responses/ResPetsPetIdGet" examples: ResExample1: value: @@ -1457,19 +1450,8 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 application/json: schema: $ref: "../openapi.yaml#/components/schemas/Error" - components: schemas: - ResPetsPetIdGet: - required: - - pet - - pet_detail - type: object - properties: - pet: - $ref: "../openapi.yaml#/components/schemas/Pet" - pet_detail: - $ref: "#/components/schemas/PetDetail" PetDetail: type: object properties: @@ -1495,6 +1477,17 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 format: date pedigree_image: type: string + responses: + ResPetsPetIdGet: + required: + - pet + - pet_detail + type: object + properties: + pet: + $ref: "../common/pet.yaml" + pet_detail: + $ref: "#/components/schemas/PetDetail" ```
@@ -1510,276 +1503,260 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数 ファイルBundle後: openapi.gen.yaml ```yaml - openapi: 3.0.3 - info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT - servers: - - url: 'http://petstore.swagger.io/v1' - tags: - - name: pets - description: Everything about your Pets - paths: - /pets: - get: - summary: List all pets - operationId: get-pets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - maximum: 100 - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: '#/components/schemas/ResPetsGet' - examples: - ResExample1: - value: - - id: 10001 - name: ToyPoodle - category: dog - sub_category: ToyPoodle - age: 1 - sex: male - note: friendly - tag: dog10001 - - id: 10002 - name: Chihuahua - category: dog - sub_category: Chihuahua - age: 1 - sex: female - note: friendly - tag: dog10002 - - id: 10003 - name: Shiba - category: dog - sub_category: Shiba - age: 1 - sex: male - note: friendly - tag: dog10003 - - id: 10004 - name: MiniatureDachshund - category: dog - sub_category: MiniatureDachshund - age: 1 - sex: female - note: friendly - tag: dog10004 - ResExample2: - value: [] - '404': - description: not found error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - post: - summary: Register a pet - operationId: post-pets - tags: - - pets - requestBody: + openapi: 3.0.3 + info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + servers: + - url: 'http://petstore.swagger.io/v1' + tags: + - name: pets + description: Everything about your Pets + paths: + /pets: + get: + summary: List all pets + operationId: get-pets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + maximum: 100 + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string content: application/json: schema: - $ref: '#/components/schemas/ReqPetsPost' + type: array + maxItems: 100 + items: + type: object + required: + - id + - name + - category + - age + - sex + properties: + id: + type: integer + format: int64 + name: + type: string + maxLength: 50 + category: + type: string + maxLength: 10 + sub_category: + type: string + maxLength: 50 + age: + type: integer + format: int32 + sex: + type: string + maxLength: 6 + note: + type: string + maxLength: 200 + tag: + type: string + maxLength: 20 examples: - ReqExample1: + ResExample1: value: - pet: - id: 10005 - name: FrenchBulldog + - id: 10001 + name: ToyPoodle category: dog - sub_category: FrenchBulldog + sub_category: ToyPoodle age: 1 sex: male note: friendly - tag: dog10005 - required: false - responses: - '201': - description: Null response - '404': - description: not found error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '/pets/{petId}': - get: - summary: Details for a pet - operationId: get-pets-pet-id - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve + tag: dog10001 + - id: 10002 + name: Chihuahua + category: dog + sub_category: Chihuahua + age: 1 + sex: female + note: friendly + tag: dog10002 + - id: 10003 + name: Shiba + category: dog + sub_category: Shiba + age: 1 + sex: male + note: friendly + tag: dog10003 + - id: 10004 + name: MiniatureDachshund + category: dog + sub_category: MiniatureDachshund + age: 1 + sex: female + note: friendly + tag: dog10004 + ResExample2: + value: [] + '404': + description: not found error + content: + application/json: + schema: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + '500': + description: unexpected error + content: + application/json: + schema: + $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema' + post: + summary: Register a pet + operationId: post-pets + tags: + - pets + requestBody: + content: + application/json: schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: '#/components/schemas/ResPetsPetIdGet' - examples: - ResExample1: - value: - pet: - id: 10001 - name: ToyPoodle - category: dog - sub_category: ToyPoodle - age: 1 - sex: male - note: friendly - tag: dog10001 - pet_detail: - breeder: BreederName - date_of_birth: '2023-10-31' - pedigree: - registration_no: 11111111 - date_of_registration: '2023-10-31' - pedigree_image: 9j2wBDAA...8QAPxAAAQQABAMGBAYDAAEDAg - '404': - description: not found error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - components: - schemas: - ResPetsGet: - type: array - maxItems: 100 - items: - $ref: '#/components/schemas/Pet' - ReqPetsPost: - required: - - pet - type: object - properties: - pet: - $ref: '#/components/schemas/Pet' - ResPetsPetIdGet: - required: - - pet - - pet_detail - type: object - properties: - pet: - $ref: '#/components/schemas/Pet' - pet_detail: - $ref: '#/components/schemas/PetDetail' - PetDetail: - type: object - properties: - breeder: - type: string - date_of_birth: - type: string - format: date - pedigree: - $ref: '#/components/schemas/Pedigree' - Pedigree: - required: - - registration_no - - date_of_registration - - pedigree_image - type: object - properties: - registration_no: - type: integer - format: int64 - date_of_registration: - type: string - format: date - pedigree_image: - type: string - Pet: - type: object - required: - - id - - name - - category - - age - - sex - properties: - id: - type: integer - format: int64 - name: - type: string - maxLength: 50 - category: - type: string - maxLength: 10 - sub_category: - type: string - maxLength: 50 - age: - type: integer - format: int32 - sex: - type: string - maxLength: 6 - note: - type: string - maxLength: 200 - tag: - type: string - maxLength: 20 - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: + required: + - pet + type: object + properties: + pet: + $ref: '#/paths/~1pets/get/responses/200/content/application~1json/schema/items' + examples: + ReqExample1: + value: + pet: + id: 10005 + name: FrenchBulldog + category: dog + sub_category: FrenchBulldog + age: 1 + sex: male + note: friendly + tag: dog10005 + required: false + responses: + '201': + description: Null response + '404': + description: not found error + content: + application/json: + schema: + $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema' + '500': + description: unexpected error + content: + application/json: + schema: + $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema' + '/pets/{petId}': + get: + summary: Details for a pet + operationId: get-pets-pet-id + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + required: + - pet + - pet_detail + type: object + properties: + pet: + $ref: '#/paths/~1pets/get/responses/200/content/application~1json/schema/items' + pet_detail: + type: object + properties: + breeder: + type: string + date_of_birth: + type: string + format: date + pedigree: + required: + - registration_no + - date_of_registration + - pedigree_image + type: object + properties: + registration_no: + type: integer + format: int64 + date_of_registration: + type: string + format: date + pedigree_image: + type: string + examples: + ResExample1: + value: + pet: + id: 10001 + name: ToyPoodle + category: dog + sub_category: ToyPoodle + age: 1 + sex: male + note: friendly + tag: dog10001 + pet_detail: + breeder: BreederName + date_of_birth: '2023-10-31' + pedigree: + registration_no: 11111111 + date_of_registration: '2023-10-31' + pedigree_image: 9j2wBDAA...8QAPxAAAQQABAMGBAYDAAEDAg + '404': + description: not found error + content: + application/json: + schema: + $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema' + '500': + description: unexpected error + content: + application/json: + schema: + $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema' ``` diff --git a/documents/forOpenAPISpecification/reference/divided_files_sample.zip b/documents/forOpenAPISpecification/reference/divided_files_sample.zip index 439036138aa93945ecb996145c87b8b8362f16ba..4e07320c18794517af7fe4fa504f9aed392ca4c3 100644 GIT binary patch literal 7022 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 From 7c5e37502a7204a9974250cd86a1be0bab895e48 Mon Sep 17 00:00:00 2001 From: Hiroki Takeda Date: Sat, 20 Jan 2024 22:01:08 +0900 Subject: [PATCH 19/19] Feature/format (#81) * Format openapi, info, servers and paths sections * Format tags, security and externalDocs --------- Co-authored-by: shotashota --- .../OpenAPI_Specification_3.0.3.md | 292 ++++++++++-------- 1 file changed, 167 insertions(+), 125 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index ab0f3076..1448664b 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -15,20 +15,20 @@ meta: # はじめに -[OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則った API ドキュメントを記述する際のコーディング規約をまとめます。古いバージョンとして[OpenAPI Specification 2.0 の規約](OpenAPI_Specification_2.0.md)がありますので、v2 をご利用の方はこちらをご参照ください。 +本ドキュメントは [OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則った API ドキュメントを記述する際のコーディング規約をまとめている。 +旧バージョンである[OpenAPI Specification 2.0 の規約](OpenAPI_Specification_2.0.md)も存在するため、v2を使用している場合はそちらを参照されたい。 -本規約の[前提条件](prerequisite.md)に従い作成されています。ToC向けのLSUDs(Large Set of Unknown Developers)なWeb APIにはマッチしない可能性があります。 - -Web API自体の設計については範囲外としますが、[API 設計標準](API_Design.md)に利用するステータスコードなどは記載しています。 +本規約は[前提条件](prerequisite.md)に基づいて作成されており、ToC向けの LSUDs(Large Set of Unknown Developers)向けの Web API には適合しない場合もあるのでご留意いただきたい。 +Web APIの設計自体はこの規約の範囲外であるが、[API 設計標準](API_Design.md) にステータスコード等の標準を記載しているため、必要に応じて参考にされたい。 ## ファイルフォーマット -[ファイルフォーマット規約](file_standards.md)に準じる。 +[ファイルフォーマット規約](file_standards.md)に従う。 ## OpenAPI ドキュメントの構成要素 -OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ、ルートオブジェクトは下記の要素で構成される。 -各種規約を読み進めるにあたってあらかじめ大まかに理解しておくことを推奨する。 +OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ、ルートオブジェクトは以下の要素で構成される。 +各種規約を理解する上で、これらの要素を大まかに把握しておくことが重要である。 各オブジェクトの詳細については[公式ドキュメント](https://spec.openapis.org/oas/v3.0.3#openapi-object)を参照されたい。 | フィールド名 | 必須 | 説明 | @@ -49,7 +49,7 @@ OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ ## openapi OpenAPI ドキュメントが使用する OpenAPI 仕様のセマンティックバージョン番号を記載する。 -本規約は`3.0.3`を対象としているため、`3.0.3`とする。 +本規約はバージョン`3.0.3`を対象としているため、`3.0.3`とする。 良い例: @@ -77,33 +77,53 @@ openapi: 3.0 | contact | | 連絡先情報 | | license | | ライセンス情報 | -### title +### info > title + +WebAPI の総称を記載する。 -WebAPI の総称を記載する。システム名やサービス名 + API のような命名とすることを推奨する。 -例. `X System API` +- システム名やサービス名 + API のような命名を推奨する。 -### description + 良い例: + + ```yaml + info: + title: X System API + ``` + +### info > description Web API が提供する機能の概要・想定する利用者やユースケース・制約などを記載する。 -### version +### info > version この API 仕様のドキュメントのバージョンを記載する。アプリケーションのバージョン(git tag やリリースで管理するようなバージョン)とは別である。 -本規約の推奨は `major.minor` 形式である。 `0.1 `固定で開発を進め、サービスのリリース時に `1.0` とし、その後の項目やオプション、パスの追加ごとに `1.1` などインクリメントしていく。もし他チームへのドキュメントの頻繁な共有が必要だれば、`1.0` のかわりに `2023.03.26` といった形式も許容する。 +- `major.minor` 形式を推奨する。 +`0.1 `固定で開発を進め、サービスのリリース時に `1.0` とし、その後の項目やオプション、パスの追加ごとにマイナーバージョンをインクリメントしていく。 + + 良い例: + + ```yaml + info: + version: 1.0 + ``` + +- もし他チームへの API ドキュメントの頻繁な共有が必要であれば、`major.minor` の代わりに `YYYY.MM.DD` の日付形式も許容する。 + + 良い例: + + ```yaml + info: + version: 2023.03.26 + ``` ## servers Web API を提供するサーバの情報を記載する。 -`url`, `description` を必須項目とする。 - -| フィールド名 | 必須 | 記載内容 | -| ------------ | :--: | ---------- | -| url | ○ | 対象の URL | -| description | ○ | 説明 | -| variables | | なし | -ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。 ただし LSUDs 向けの Web API 開発においては本番環境の URL を不用意に公開したくないケースが多く、記載は避けるべきである。 +- `url`, `description` を必須項目とする。 +- ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。 +- SSKDs 向けの Web API 開発においては本番環境の URL を不用意に公開したくないケースが多く、記載は避けるべきである。 良い例: @@ -129,9 +149,9 @@ servers: API の利用可能なエンドポイントと操作方法を記載する。 -* API ごとに機能IDを定義している場合、`paths` 配下の各パスは機能 ID の昇順に定義する。 -* URLパスが複数の単語からなる場合、ケバブケースで表現する。 -* HTTP メソッドは `GET`, `POST`, `PUT`, `PATCH`, `DELETE` の順に定義する。 +- API ごとに機能IDを定義している場合、`paths` 配下の各パスは機能 ID の昇順に定義する。 +- URL パスが複数の単語からなる場合、ケバブケースで表現する。 +- HTTP メソッドは `GET`, `POST`, `PUT`, `PATCH`, `DELETE` の順に定義する。 良い例: @@ -155,25 +175,25 @@ API の利用可能なエンドポイントと操作方法を記載する。 ... ``` -* HTTPメソッドの配下に定義されるオペレーションオブジェクトは、下記の項目を必須項目とする。 +- HTTPメソッドの配下に定義されるオペレーションオブジェクトは、下記の項目を必須項目とする。 -| フィールド名 | 必須 | 記載内容 | -| ------------ | :--: | ---------------------------------------- | -| tags | ○ | API の論理的なグループ | -| operationId | ○ | API の利用可能なエンドポイントと操作方法 | -| summary | ○ | API の操作概要 | -| description | | API の振る舞いの詳細や注意点を記載する。 | -| parameters | | API のリクエストパラメータ | -| requestBody | | API のリクエストボディ | -| response | ○ | API のレスポンス | -| security | | | + | フィールド名 | 必須 | 記載内容 | + | ------------ | :--: | ---------------------------------------- | + | tags | ○ | API の論理的なグループ | + | operationId | ○ | API の利用可能なエンドポイントと操作方法 | + | summary | ○ | API の操作概要 | + | description | | API の振る舞いの詳細や注意点を記載する。 | + | parameters | | API のリクエストパラメータ | + | requestBody | | API のリクエストボディ | + | response | ○ | API のレスポンス | + | security | | API のセキュリティ機構 | -### tags +### paths > tags API の論理的なグループを指定する。 -* タグオブジェクトとして事前定義したタグの中から選択すること。 +- タグオブジェクトとして事前定義したタグの中から選択すること。 良い例: @@ -201,7 +221,7 @@ API の論理的なグループを指定する。 tags: [] ``` -* 1 API につき 1つのタグを指定すること。 +- 1 API につき 1つのタグを指定すること。 良い例: @@ -227,11 +247,11 @@ API の論理的なグループを指定する。 ... ``` -### operationId +### paths > operationId API を識別するための一意な文字列を記載する。 -* HTTP メソッドとURLパスをアッパーキャメルケースで表現する。 +- HTTP メソッドとURLパスをアッパーキャメルケースで表現する。 ただしOpenAPI ドキュメントのエディタとして広く使用されるStoplightが提供する[Linter](https://docs.stoplight.io/docs/spectral/674b27b261c3c-overview)の定義としてケバブケースが標準になっているため、Stoplightを使用する場合はケバブケースで表現しても良い。 良い例: @@ -245,15 +265,26 @@ API を識別するための一意な文字列を記載する。 /products/{product_id}: put: operationId: put-products-product-id + ... ``` -### summery + 良い例: + + ```yaml + paths: + /users/me: + get: + operationId: get_users_me + ... + ``` + +### paths > summary API の操作概要を記載する。 -* 機能 ID や機能名があるのであれば記載する。 +- 機能 ID や機能名があるのであれば記載する。 - 良い例 + 良い例: ```yaml paths: @@ -262,28 +293,28 @@ API の操作概要を記載する。 summary: API-001 ユーザアカウント取得 ``` -### description +### paths > description APIの振る舞いの詳細や注意点を記載する。 別途参照させるべき設計書があるのであれば、設計書へのリンクを記載しても良い。 -### parameters +### paths > parameters API のリクエストパラメータ(パスパラメータ、クエリパラメータ、ヘッダ)を記載する。 -* HTTP メソッドが `GET`, `DELETE` の場合にのみ指定する。 -* パスパラメータはスネークケースで表現する。 -* クエリパラメータはスネークケースで表現する。 -* ヘッダはハイフンを区切り文字とするパスカルケースで表現する。 +- HTTP メソッドが `GET`, `DELETE` の場合にのみ指定する。 +- パスパラメータはスネークケースで表現する。 +- クエリパラメータはスネークケースで表現する。 +- ヘッダはハイフンを区切り文字とするパスカルケースで表現する。 - -### requestBody +### paths > requestBody API のリクエストボディを記載する。 -* リクエストボディを記載する。仕様の[describing-request-body](https://swagger.io/docs/specification/describing-request-body/)の章にある通り、リクエストボディはPOST、PUT、PATCHで使用され、GET、DELETE、HEADには利用できない -* requestBodyの定義は、components/requestBodiesで宣言し、 `$refs` で参照する -* requestBodyの命名は、 `Req` というプレフィクスと、 `Body` というサフィックスで終える必要がある +- リクエストボディを記載する。 + 標準仕様の [describing-request-body](https://swagger.io/docs/specification/describing-request-body/) の章にも記載がある通り、リクエストボディは `POST`、`PUT`、`PATCH` で使用され、`GET`、`DELETE`、`HEAD` には使用できない。 +- requestBodyの定義は、`components/requestBodies` で宣言し、`$refs` で参照する。 +- requestBodyの命名は、`Req` というプレフィクスと、`Body` というサフィックスで終える必要がある。 ```yaml paths: @@ -295,14 +326,14 @@ paths: ... ``` -### responses +### paths > responses API のレスポンスを記載する。 -* OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `components` オブジェクトとして任意の名称で定義し `$ref` で参照する。 -スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Res` を付与) -* `schema` オブジェクトの `type` は `object` を指定する。 -* 異常系(`4xx`, `5xx`)の HTTP ステータスコードに対応するレスポンス定義は設計者が個別に定義するのではなく、事前に共通的なレスポンスオブジェクトを定義し `$ref` で参照することが望ましい。 +- OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `components` オブジェクトとして任意の名称で定義し `$ref` で参照する。 +- スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Res` を付与) +- `schema` オブジェクトの `type` は `object` を指定する。 +- 異常系(`4xx`, `5xx`)の HTTP ステータスコードに対応するレスポンス定義は設計者が個別に定義するのではなく、事前に共通的なレスポンスオブジェクトを定義し `$ref` で参照することが望ましい。 ​ ```yaml @@ -341,16 +372,19 @@ components: ... ``` -### security +### paths > security -APIレベルの認証方式の設定だが、ルートレベルのsecurityで定義済みであるため、通常設定しない。 +APIの認証方式を記載する。 -ヘルスチェックのような認証を通す必要がないAPIのみ、上書きで定義する。 +- 通常はルートレベルの `security` でAPI共通的な認証方式を設定し、個々のAPIで個別に設定は行わない。 +- ヘルスチェックのような認証を通す必要がないAPIのみ、上書きで定義する。 -```yml -# 認証しない場合のみ個別で定義する -security: [] -``` + 良い例; + + ```yaml + # 認証しない場合のみ個別で定義する + security: [] + ``` ## components @@ -358,7 +392,7 @@ security: [] API 定義で利用する共通のデータモデルを定義 ​ -```yml +```yaml components: schemas: ... parameters: ... @@ -374,24 +408,24 @@ components: ### schemas -* API定義共通で利用するスキーマを定義する -* schemasに定義する項目はリソースやエラー等のドメインオブジェクトのみとし、リクエストパラメータやレスポンスパラメータは`parameter`や`requestBodies`、`responses`に記載する。 - * `paths.requestBodies`から直接参照されるリクエストパラメータオブジェクトは`requestBodies`に定義する。 - * `paths`から直接参照されるレスポンスパラメータは`responses`に定義する。また400や500などのエラーレスポンスも`responses`に定義する。 - * HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータに相当するものは`parameter`に定義する。 - * レスポンスヘッダーは`headers`にて定義する。 - * 上記いずれにも該当しないuserやidなどのリソース、エラーを示すオブジェクトは`schemas`に定義する。 - * 各APIのリクエストレスポンスオブジェクトは可能な限り、`parameter`,`requestBodies`,`responses`に定義する方針とし、API固有のオブジェクト(所謂`ReqXXX`、`ResXXX`等)は`schemas`には定義しない。 - * ただし、オブジェクトがネストしてしまう場合はAPI固有のオブジェクトであっても`schemas`に定義する。 +- API定義共通で利用するスキーマを定義する。 +- schemasに定義する項目はリソースやエラー等のドメインオブジェクトのみとし、リクエストパラメータやレスポンスパラメータは`parameter`や`requestBodies`、`responses`に記載する。 + - `paths.requestBodies`から直接参照されるリクエストパラメータオブジェクトは`requestBodies`に定義する。 + - `paths`から直接参照されるレスポンスパラメータは`responses`に定義する。また400や500などのエラーレスポンスも`responses`に定義する。 + - HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータに相当するものは`parameter`に定義する。 + - レスポンスヘッダーは`headers`にて定義する。 + - 上記いずれにも該当しないuserやidなどのリソース、エラーを示すオブジェクトは`schemas`に定義する。 + - 各APIのリクエストレスポンスオブジェクトは可能な限り、`parameter`,`requestBodies`,`responses`に定義する方針とし、API固有のオブジェクト(所謂`ReqXXX`、`ResXXX`等)は`schemas`には定義しない。 + - ただし、オブジェクトがネストしてしまう場合はAPI固有のオブジェクトであっても`schemas`に定義する。 ※定義するオブジェクトの`properties`配下に更に`type: object`が定義されしまう(ネストしてしまう)と生成ツールによってはうまく型が生成されないため。 -* 規約 - * リソース名はアッパーキャメルケースで定義 - * リソース名は単数形で定義 - * `type` に複数の型定義の指定不可 - * `type: null`は原則として利用せず、undefinedを利用する。 +- 規約 + - リソース名はアッパーキャメルケースで定義する。 + - リソース名は単数形で定義する。 + - `type` に複数の型定義の指定不可。 + - `type: null`は原則として利用せず、undefinedを利用する。 [差分更新APIの場合](#差分更新-API-の場合)にあるとおり、空更新を行う場合は空文字を利用する。 - * `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない + - `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない。 ```yaml components: @@ -459,10 +493,10 @@ components: #### requestBodies(components) -* `requestBody` 直下の `required` は必須で `true` を指定する -* OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `component` オブジェクトとして任意の名称で定義し `$ref` で参照する。 +- `requestBody` 直下の `required` は必須で `true` を指定する +- OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `component` オブジェクトとして任意の名称で定義し `$ref` で参照する。 スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Req` を付与) -* `schema` オブジェクトの `type` は `object` を指定する。 +- `schema` オブジェクトの `type` は `object` を指定する。 ```yaml components: @@ -484,7 +518,7 @@ components: レスポンスの先頭には複数のエンドポイントで横断的に用いるモデルを定義する。例えば、ステータスコード400~500系のエラーモデルがある。 ​ -```yml +```yaml components: schemas: ProblemDetailError: @@ -509,7 +543,7 @@ components: ​ 正常系のレスポンスの例としてはファイルアップロード・ダウンロードなどが該当する。個別のアプリケーション要件でブレが少ないと複数のエンドポイントで用いられる場合に定義する。オブジェクトのスキーマは、schemasに切り出して定義し、コード生成ツールのために型情報を付与させる。 -```yml +```yaml components: schemas: SignedURL: @@ -545,7 +579,7 @@ components: それらの後に、paths登場順にエンドポイント固有のレスポンスを定義する。レスポンスオブジェクトのスキーマは、schemasに切り出して定義する。 -```yml +```yaml components: schemas: Product: @@ -576,16 +610,16 @@ API 共通で利用するパラメータ(パスパラメータ、クエリパ ##### パスパラメータ -* API 全体で利用されるパスパラメータが必要なケースが想定されないため、原則定義しない。 +- API 全体で利用されるパスパラメータが必要なケースが想定されないため、原則定義しない。 特定リソースの操作(例えば更新と削除)を行う際のリソースIDはパスパラメータとして再利用できるが、コンフリクトを避けるため原則共通化は行わない。 ##### クエリパラメータ -* API 全体で利用可能な共通のクエリパラメータを定義する (例: 検索数のlimit, offset) -* 命名は クエリパラメータ名に `Query` というプレフィクスを付与する形式を推奨する。 +- API 全体で利用可能な共通のクエリパラメータを定義する (例: 検索数のlimit, offset) +- 命名は クエリパラメータ名に `Query` というプレフィクスを付与する形式を推奨する。 -```yml +```yaml paths: get: /products: @@ -604,10 +638,10 @@ parameters: ##### ヘッダパラメータ -* API 全体で利用可能な共通のリクエストヘッダを定義する。 -* 命名は ヘッダ名に `Header` というプレフィクスを付与する形式を推奨する。 +- API 全体で利用可能な共通のリクエストヘッダを定義する。 +- 命名は ヘッダ名に `Header` というプレフィクスを付与する形式を推奨する。 -```yml +```yaml paths: post: /products: @@ -626,11 +660,11 @@ components: ##### Cookie パラメータ -* API 全体で利用可能な共通のCookieパラメータを定義する。(例: CSRF用のトークン) -* 命名は Cookie パラメータ名に `Cookie` というプレフィクスを付与する形式を推奨する。 -* Cookie 認証を定義する場合は、`APIKey` を利用すること。 +- API 全体で利用可能な共通のCookieパラメータを定義する。(例: CSRF用のトークン) +- 命名は Cookie パラメータ名に `Cookie` というプレフィクスを付与する形式を推奨する。 +- Cookie 認証を定義する場合は、`APIKey` を利用すること。 -```yml +```yaml paths: get: /products: @@ -652,9 +686,9 @@ components: API 共通で利用するレスポンスヘッダを記載する。 -* 命名は ヘッダ名からハイフンを除去した形式を推奨する。 +- 命名は ヘッダ名からハイフンを除去した形式を推奨する。 -```yml +```yaml paths: get: /products: @@ -677,7 +711,7 @@ components: 標準で用いるAPI認証の定義を行う。 -```yml +```yaml # Bearer トークによる認証 securitySchemes: BearerAuth: @@ -717,54 +751,54 @@ securitySchemes: ## security -ルートレベルのsecurityを定義すると、全APIに共通で適用される。 +全APIに共通で適用されるセキュリティ設定を定義する。 +業務システムの Web API において 認証が全く存在しないケースは考えにくいため、本規約ではルートレベルで認証を設定し、個々のAPIへの適応漏れをなくす。 -業務システムのWeb APIで認証が全く存在しないことは考えにくいため、本規約ではルートレベルで認証を設定し、漏れをなくす。 +良い例: -```yml -# 認証設定方法 (デフォルトで設定済みの為不要) +```yaml security: - Bearer: [] ``` -ヘルスチェックなどAPI種別によって認証が不要な場合がある。それらに対しては個別に、認証情報を上書き定義する。 - ## tags -タグを用いて、API 操作をグループ化することができる。ドキュメントやツールにとって非常に重要であるため、 **必須** で指定する。 +API を論理的にグループ化するためのタグを定義する。ドキュメントやツールにとって重要であるため、 **必須** で指定する。 -- Swagger UI(HTML ドキュメント)の順序を制御できる - - 未指定の場合は、登場順で生成されてしまう -- 命名は、 **単数形** で、小文字かつ半角スペース区切り で記載する - - コード生成で利用され、Go ではパッケージ名や TypeScript の Class 単位となるため、シンプルな命名にする - - HTML ドキュメントで参照する場合の可読性を上げるため、単語を半角スペース区切りとする -- タグごとに `description` も必須で記載する +- `name`, `description` を必須項目とする。 +- **単数形** で、小文字かつ半角スペース区切りで記載する。 + 半角スペース区切りで記載する理由は HTML ドキュメントで参照する場合の可読性を上げるためである。 +- コード生成で利用される(Go においてはパッケージ、 TypeScriptにおいてはクラスに相当する)ため、シンプルな命名にする。 + +良い例: ```yaml -# NG tags: - name: product description: 製品 - - name: store - description: 店舗 - name: user account description: ユーザーアカウント +``` -# NG +悪い例: + +```yaml tags: - name: products - - name: stores + description: 製品 - name: user_account - - name: UserAccount + description: ユーザーアカウント ``` - ## externalDocs -Schema定義, Paths配下の各API定義, OASのトップ階層などで、参照情報としてのURLを指定し表示が可能。ただし、`description` にてリンクURLを記載する方が、複数リンクを指定可能であるなど自由度が高く使いやすい。そのため、参照先URLリンクの記載には、`externalDocs` ではなく `description` の利用を推奨する。 +参照情報としてのURLの記載が可能。 +ただし、`description` にて参考情報となるURLを記載する方が、複数リンクを指定可能であるなど自由度が高く使いやすい。そのため `externalDocs` は利用せず `description` の利用を推奨する。 + + +良い例: ```yaml -# 推奨 info: description: |- Some useful links: @@ -777,6 +811,14 @@ externalDocs: url: http://swagger.io ``` +悪い例: + +```yaml +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +``` + # 設計上のポイント ## ファイルアップロード