spectral で OpenAPI の required フィールドを検証するカスタム関数を作成する - Diary

Diary

日々学んだことをアウトプットする場として初めてみました

spectral で OpenAPI の required フィールドを検証するカスタム関数を作成する

spectral は OpenAPI、AsyncAPI、JSON Schema などの API ドキュメントを検証するためのツールです。

今回は spectral を使用して OpenAPI の required フィールドを検証するカスタム関数を作成する方法について解説します。

【今回防ぎたいケース】

components:
  schemas:
    Me:
      type: object
      required:
        ...
        # typo により properties に存在しないフィールドが指定されてしまっている。
        #
        # build-in rulesets を extends した
        # 『['spectral:oas', 'spectral:asyncapi', 'spectral:arazzo']』
        # では防げない。
        - bithday
      properties:
        ...
        birthday:
          type: string
          format: date
          description: 誕生日。

[目次]

環境

本記事の内容は spectral v6 系での動作を想定しています。

# 動作確認した 2024/10/13 時点での spectral のバージョン。
$ npx @stoplight/spectral-cli --version
6.13.1

custom functions の作成方法

spectral では、カスタム関数を作成して独自の検証が可能です。

今回は『OpenAPI で required フィールドに記載があるが、properties にその値が存在しない』というケースを検証をする関数を作成してみます。

作成が必要なファイルの全貌

プロジェクトのルートディレクトリに spectral-functions フォルダを作成し、その中に validateRequiredProperties.js ファイルを作成します。

.
├── .spectral.yaml
├── examples
│   └── get-me.json
├── openapi.yml
└── spectral-functions
    └── validateRequiredProperties.js

各ファイル、それぞれ以下のような内容を記載しています。

.spectral.yaml

---
# Custom ruleset for the Spectral linter.
#
# ref: https://meta.stoplight.io/docs/spectral/01baf06bdd05a-create-a-ruleset
extends: ['spectral:oas', 'spectral:asyncapi', 'spectral:arazzo']
# ref: ./spectral-functions/
functions: [validateRequiredProperties]
functionsDir: './spectral-functions'
rules:
  invalid-required-field:
    description: 'Required fields must exist in the properties.'
    message: "The required field '{{value}}' does not exist in the properties."
    severity: error
    given: '$.components.schemas.*'
    then:
      function: 'validateRequiredProperties'

examples/get-me.json

{
  "name": "Test User",
  "mail": "l.user@example.com",
  "birthday": "2000-01-01"
}

openapi.yml

openapi: 3.0.0

info:
  version: 0.1.0
  title: sample API
  description: sample
  contact:
    name: sample team
servers:
  - url: 'http://localhost:8080'
    description: 開発環境
tags:
  - name: User
    description: ユーザー情報に関するエンドポイント。

paths:
  /me:
    get:
      summary: ユーザー情報取得
      operationId: getMe
      description: ログインユーザーの情報を取得する。
      tags:
        - User
      responses:
        '200':
          description: ユーザー情報を返す。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Me'
              example:
                $ref: './examples/get-me.json'

components:
  schemas:
    Me:
      type: object
      required:
        - name
        - mail
        - birthday
      properties:
        name:
          type: string
          description: ユーザー名。
        mail:
          type: string
          description: メールアドレス。
        birthday:
          type: string
          format: date
          description: 誕生日。

spectral-functions/validateRequiredProperties.js

export default (input, opts, context) => {
  const { required, properties } = input;

  if (!required || !properties) {
    return;
  }

  const propertyNames = Object.keys(properties);
  const results = [];

  required.forEach((prop, index) => {
    if (!propertyNames.includes(prop)) {
      results.push({
        message: `The required field '${prop}' does not exist in the properties.`,
        path: [...context.path, 'required', index],
      });
    }
  });

  return results;
};

.spectral.yaml

.spectral.yaml は spectral に関する設定を管理するためのファイルで、今回は functions, functionsDir, rules の 3 つを変更しました。

validateRequiredProperties.js の実装

カスタム関数は『input, options, context』の3つの引数を取ります

ドキュメントに思ったより情報が書いてあったのと, console.log で出力を確かめながら実装できるため、ここでの説明は割愛します。