typespecでOpenAPI v2のファイルを作成

# typespecでSwagger v2のファイルを作成する

[typespec](https://typespec.io/)を利用して、Swagger v2(openAPI)のschemaを生成する取り組み。

## what is typespec?

[typespec](https://typespec.io/)

> Describe your data up front and generate schemas, API specifications, client / server code, docs, and more.

schemaやcode生成のための micro soft teamが開発している言語。

global install した方が初期作業は何かと便利なので、dockerを利用して環境整備します。

## 0. setup project

```
$ mkdir schema
$ cd schema
$ touch dockerfile compose.yaml
$ tree .
.
├── README.md
├── compose.yaml
├── dockerfile
```

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

```terminal
% docker exec -it XXX sh
```

あとは、dockerの中でもさもさやっていきます

## 1. Install depenedency

[typespec Installation](https://typespec.io/docs)とおなじように、作業を進めていきます。

empty projectでいくと同じ環境を作成しやすくなります。

```terminal
% npm install -g @typespec/compiler
% tsp init

Folder '/usr/src/app' is not empty. Are you sure you want to initialize a new project here? … yes
✔ Please select a template › Empty project	min compiler ver: 0.61.2
Create an empty project.
✔ Project name … app
✔ Do you want to generate a .gitignore file? … yes
```

```terminal
% tsp install
% tree . -L 2 -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── tspconfig.yaml
├── package-lock.json
└── package.json
```

## 2. Swagger v2を作成できる環境を作成する

### 2-1. とりあえず目指す環境

[6. Complete Example and Generate OpenApi 2.0 spec](https://azure.github.io/typespec-azure/docs/getstarted/azure-resource-manager/step05)を参考に作成していきます。

build対象になる、`main.tsp`を作成していきます。

```terminal
% touch main.tsp
```

```main.tsp
import "@typespec/http";

using TypeSpec.Http;

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

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

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

```terminal
% tree . -L 2 -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── main.tsp
├── tspconfig.yaml
├── package-lock.json
└── package.json
```


## 2-2. @azure-tools の環境を準備する

[@azure-tools/typespec-autorest](https://github.com/Azure/typespec-azure)をinstallして、scriptを記載していきます。

```terminal
% npm install @azure-tools/typespec-autorest
```

```package.json
...
script: {
  "build:http": "tsp compile .",
  "setup:vsc": "tsp code install",
  "format:check": "tsp format --check \"**/*.tsp\"",
  "format": "tsp format \"**/*.tsp\""
}
```

```tspconfig.yaml
warn-as-error: true
output-dir: "{cwd}/generated"
emit:
  - "@azure-tools/typespec-autorest"
```

## 2-3. buildしてみる

```terminal
% npm run build

> app@0.1.0 build:http
> tsp compile .

TypeSpec compiler v0.61.2

Compilation completed successfully.

% tree . -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── generated
│   └── @azure-tools
│       └── typespec-autorest
│           └── openapi.json
├── main.tsp
├── tspconfig.yaml
├── package-lock.json
└── package.json
```

```generated/@azure-tools/typespec-autorest/openapi.json
{
  "swagger": "2.0",
  "info": {
    "title": "(title)",
    "version": "0000-00-00",
    "x-typespec-generated": [
      {
        "emitter": "@azure-tools/typespec-autorest"
      }
    ]
  },
  "schemes": [
    "https"
  ],
  "produces": [
    "application/json"
  ],
  "consumes": [
    "application/json"
  ],
  "tags": [],
  "paths": {
    "/stores": {
      "get": {
        "operationId": "Stores_List",
        "parameters": [
          {
            "name": "filter",
            "in": "query",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "The request has succeeded.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/Store"
              },
              "x-ms-identifiers": []
            }
          }
        }
      }
    },
    "/stores/{name}": {
      "get": {
        "operationId": "Stores_Read",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "The request has succeeded.",
            "schema": {
              "$ref": "#/definitions/Store"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "Address": {
      "type": "object",
      "properties": {
        "street": {
          "type": "string"
        },
        "city": {
          "type": "string"
        }
      },
      "required": [
        "street",
        "city"
      ]
    },
    "Store": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "address": {
          "$ref": "#/definitions/Address"
        }
      },
      "required": [
        "name",
        "address"
      ]
    }
  },
  "parameters": {}
}

```