0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spectralを利用して、OpenAPI, SwaggerのコードをLintする

Posted at

Spectralを利用して、OpenAPI, SwaggerのコードをLintする

Spectralは、
OpenAPI(v2, v3)やAsyncAPIのlinterです。

Custom Ruleの生成が比較的簡単にできる点、swagger-uiを利用している場合にinvalidケースなどに気づきにくいことが多々あり、利用するメリットがあるかと思います。
Typespecなどを利用している時に、噛み合わせが悪いコードをlintで制約を追加したりしてバグを予防したりすることができます。

0. Before do

0-0. Project Structure

想定しているディレクトリ構成とファイルの中身

tree . -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── openapi3
│   ├── generated
│   │   └── @typespec
│   │       └── openapi3
│   │           └── openapi.yaml
├── package-lock.json
├── package.json
compose.yaml
compose.yaml
services:
  http_schema_container:
    container_name: http_schema_container
    build:
      context: .
      dockerfile: dockerfile
    tty: true
    restart: always
    volumes:
      - .:/usr/src/app
dockerfile
FROM node:23-alpine3.19

WORKDIR /usr/src/app
openapi3/generated/@typespec/openapi3/openapi.yaml
openapi3/generated/@typespec/openapi3/openapi.yaml
openapi: 3.0.0
info:
  title: (title)
  version: 0.0.0
tags: []
paths:
  /pets:
    get:
      operationId: Pets_list
      parameters:
        - name: filter
          in: query
          required: true
          schema:
            type: string
          explode: false
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Pet'
    post:
      operationId: Pets_create
      parameters: []
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Pet'
  /pets/{id}:
    get:
      operationId: Pets_read
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
  /stores:
    get:
      operationId: Stores_list
      parameters:
        - name: filter
          in: query
          required: true
          schema:
            type: string
          explode: false
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Store'
  /stores/{id}:
    get:
      operationId: Stores_read
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Store'
components:
  schemas:
    Address:
      type: object
      required:
        - street
        - city
      properties:
        street:
          type: string
        city:
          type: string
    Pet:
      type: object
      required:
        - name
        - age
      properties:
        name:
          type: string
        age:
          type: integer
          format: int32
    Store:
      type: object
      required:
        - name
        - address
      properties:
        name:
          type: string
        address:
          $ref: '#/components/schemas/Address'

0-1. typespec を利用して、OpenAPIのコードを生成する

tree . -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── openapi3
│   ├── generated
│   │   └── @typespec
│   │       └── openapi3
│   │           └── openapi.yaml
│   ├── main.tsp
│   └── tspconfig.yaml
├── package-lock.json
├── package.json
openapi3/main.tsp
openapi3/main.tsp
import "@typespec/http";

using TypeSpec.Http;

model Pet {
  name: string;
  age: int32;
}

model Store {
  name: string;
  address: Address;
}

model Address {
  street: string;
  city: string;
}

@route("/pets")
interface Pets {
  list(@query filter: string): Pet[];
  create(@body pet: Pet): Pet;
  read(@path id: string): Pet;
}

@route("/stores")
interface Stores {
  list(@query filter: string): Store[];
  read(@path id: string): Store;
}

openapi3/tspconfig.yaml
openapi3/tspconfig.yaml
warn-as-error: true                           # Treat warnings as errors
output-dir: "{cwd}/openapi3/generated"       # Configure the base output directory for all emitters
emit:
  - "@typespec/openapi3"

1. Spectralの依存関係を設定する

% npm install @stoplight/spectral-cli
% echo 'extends: ["spectral:oas"]' > .spectral.yaml
package.json
"scripts": {
    "cli:spectral": "spectral"
}
tree . -I node_modules
.
├── .spectral.yaml
├── README.md
├── compose.yaml
├── dockerfile
├── openapi3
│   ├── generated
│   │   └── @typespec
│   │       └── openapi3
│   │           └── openapi.yaml
├── package-lock.json
├── package.json

2. SpectralのLintを実施する

package.json
"scripts": {
    "lint:openapi": "spectral lint ./openapi3/generated/@typespec/openapi3/openapi.yaml"
}
%  npm run lint:openapi
> app@0.1.0 lint:openapi
> spectral lint ./openapi3/generated/@typespec/openapi3/openapi.yaml

/usr/src/app/openapi3/generated/@typespec/openapi3/openapi.yaml
  1:1   warning  oas3-api-servers       OpenAPI "servers" must be present and non-empty array.
  2:6   warning  info-contact           Info object must have "contact" object.                        info
  2:6   warning  info-description       Info "description" must be present and non-empty string.       info
  8:9   warning  operation-description  Operation "description" must be present and non-empty string.  paths./pets.get
  8:9   warning  operation-tags         Operation must have non-empty "tags" array.                    paths./pets.get
 26:10  warning  operation-description  Operation "description" must be present and non-empty string.  paths./pets.post
 26:10  warning  operation-tags         Operation must have non-empty "tags" array.                    paths./pets.post
  43:9  warning  operation-description  Operation "description" must be present and non-empty string.  paths./pets/{id}.get
  43:9  warning  operation-tags         Operation must have non-empty "tags" array.                    paths./pets/{id}.get
  59:9  warning  operation-description  Operation "description" must be present and non-empty string.  paths./stores.get
  59:9  warning  operation-tags         Operation must have non-empty "tags" array.                    paths./stores.get
  78:9  warning  operation-description  Operation "description" must be present and non-empty string.  paths./stores/{id}.get
  78:9  warning  operation-tags         Operation must have non-empty "tags" array.                    paths./stores/{id}.get

✖ 13 problems (0 errors, 13 warnings, 0 infos, 0 hints)
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?