diff --git a/.github/workflows/openapi-ci.yml b/.github/workflows/openapi-ci.yml new file mode 100644 index 00000000..b4307bf7 --- /dev/null +++ b/.github/workflows/openapi-ci.yml @@ -0,0 +1,22 @@ +name: openapi-ci + +on: + pull_request: + # Only run the workflow when OpenAPI definition files in docs/api change + paths: + - 'docs/api/openapi.yaml' + - 'docs/api/*.yaml' + +jobs: + build: + name: Run Spectral + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v3 + + - name: Run Spectral + uses: stoplightio/spectral-action@latest + with: + # Lint all YAML files under docs/api (this repo uses `docs/api/openapi.yaml`) + file_glob: 'docs/api/*.yaml' diff --git a/.spectral.yaml b/.spectral.yaml new file mode 100644 index 00000000..d47c47d3 --- /dev/null +++ b/.spectral.yaml @@ -0,0 +1 @@ +extends: ["spectral:oas"] diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index bf4749fc..90ea00c6 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,17 +1,27 @@ -openapi: 3.0.3 +# yaml-language-server: $schema=https://spec.openapis.org/oas/3.1/schema/2025-02-13 +openapi: 3.1.1 info: title: Lamp Control API description: | A simple API for controlling lamps, demonstrating CRUD operations. version: 1.0.0 + contact: + name: API Support + url: https://www.example.com/support servers: - url: /v1 +tags: + - name: lamps + description: Operations related to lamps + paths: /lamps: get: summary: List all lamps + description: Retrieve a list of all lamps. + tags: [lamps] operationId: listLamps parameters: - in: query @@ -37,6 +47,48 @@ paths: nullable: true hasMore: type: boolean + examples: + lamps_list: + summary: List of lamps + description: Example response showing multiple lamps with pagination + value: + data: + - id: "046b6c7f-0b8a-43b9-b35d-6489e6daee91" + status: true + createdAt: "2024-01-15T09:30:00Z" + updatedAt: "2024-01-15T14:22:30Z" + - id: "7c9e2a5b-8f1d-4e3c-9b2a-1d5f8c7e4b6a" + status: false + createdAt: "2024-01-15T10:15:45Z" + updatedAt: "2024-01-15T10:15:45Z" + nextCursor: "eyJpZCI6IjdjOWUyYTViLThmMWQtNGUzYy05YjJhLTFkNWY4YzdlNGI2YSJ9" + hasMore: true + empty_list: + summary: Empty list of lamps + description: Example response when no lamps exist + value: + data: [] + nextCursor: null + hasMore: false + links: + GetLampById: + operationId: getLamp + parameters: + lampId: '$response.body#/data/0/id' + description: > + The `id` value of any lamp in the list can be used as the `lampId` parameter in `GET /lamps/{lampId}`. + UpdateLamp: + operationId: updateLamp + parameters: + lampId: '$response.body#/data/0/id' + description: > + The `id` value of any lamp in the list can be used as the `lampId` parameter in `PUT /lamps/{lampId}`. + DeleteLamp: + operationId: deleteLamp + parameters: + lampId: '$response.body#/data/0/id' + description: > + The `id` value of any lamp in the list can be used as the `lampId` parameter in `DELETE /lamps/{lampId}`. '304': description: Not Modified '400': @@ -50,6 +102,8 @@ paths: message: "Invalid query parameter 'pageSize'" post: summary: Create a new lamp + description: Create a new lamp. + tags: [lamps] operationId: createLamp requestBody: required: true @@ -57,6 +111,17 @@ paths: application/json: schema: $ref: '#/components/schemas/LampCreate' + examples: + create_lamp_on: + summary: Create lamp that is turned on + description: Create a new lamp with status set to on/true + value: + status: true + create_lamp_off: + summary: Create lamp that is turned off + description: Create a new lamp with status set to off/false + value: + status: false responses: '201': description: Lamp created successfully @@ -64,6 +129,42 @@ paths: application/json: schema: $ref: '#/components/schemas/Lamp' + examples: + created_lamp_on: + summary: Created lamp turned on + description: Successfully created lamp with status on + value: + id: "046b6c7f-0b8a-43b9-b35d-6489e6daee91" + status: true + createdAt: "2024-01-15T09:30:00Z" + updatedAt: "2024-01-15T09:30:00Z" + created_lamp_off: + summary: Created lamp turned off + description: Successfully created lamp with status off + value: + id: "7c9e2a5b-8f1d-4e3c-9b2a-1d5f8c7e4b6a" + status: false + createdAt: "2024-01-15T10:15:45Z" + updatedAt: "2024-01-15T10:15:45Z" + links: + GetLampById: + operationId: getLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used as the `lampId` parameter in `GET /lamps/{lampId}`. + UpdateLamp: + operationId: updateLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used as the `lampId` parameter in `PUT /lamps/{lampId}`. + DeleteLamp: + operationId: deleteLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used as the `lampId` parameter in `DELETE /lamps/{lampId}`. '400': description: Invalid request data content: @@ -78,6 +179,8 @@ paths: /lamps/{lampId}: get: summary: Get a specific lamp + description: Retrieve a specific lamp. + tags: [lamps] operationId: getLamp parameters: - name: lampId @@ -92,6 +195,36 @@ paths: application/json: schema: $ref: '#/components/schemas/Lamp' + examples: + lamp_on: + summary: Lamp that is turned on + description: Example of a lamp with status on/true + value: + id: "046b6c7f-0b8a-43b9-b35d-6489e6daee91" + status: true + createdAt: "2024-01-15T09:30:00Z" + updatedAt: "2024-01-15T14:22:30Z" + lamp_off: + summary: Lamp that is turned off + description: Example of a lamp with status off/false + value: + id: "7c9e2a5b-8f1d-4e3c-9b2a-1d5f8c7e4b6a" + status: false + createdAt: "2024-01-15T10:15:45Z" + updatedAt: "2024-01-15T10:15:45Z" + links: + UpdateLamp: + operationId: updateLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used to update the lamp using `PUT /lamps/{lampId}`. + DeleteLamp: + operationId: deleteLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used to delete the lamp using `DELETE /lamps/{lampId}`. '304': description: Not Modified '400': @@ -108,6 +241,8 @@ paths: description: Lamp not found put: summary: Update a lamp's status + description: Update a lamp's status. + tags: [lamps] operationId: updateLamp parameters: - name: lampId @@ -121,6 +256,17 @@ paths: application/json: schema: $ref: '#/components/schemas/LampUpdate' + examples: + turn_on: + summary: Turn lamp on + description: Update lamp status to on/true + value: + status: true + turn_off: + summary: Turn lamp off + description: Update lamp status to off/false + value: + status: false responses: '200': description: Lamp updated successfully @@ -128,6 +274,36 @@ paths: application/json: schema: $ref: '#/components/schemas/Lamp' + examples: + updated_to_on: + summary: Lamp turned on + description: Lamp successfully updated to on status + value: + id: "046b6c7f-0b8a-43b9-b35d-6489e6daee91" + status: true + createdAt: "2024-01-15T09:30:00Z" + updatedAt: "2024-01-15T16:45:20Z" + updated_to_off: + summary: Lamp turned off + description: Lamp successfully updated to off status + value: + id: "046b6c7f-0b8a-43b9-b35d-6489e6daee91" + status: false + createdAt: "2024-01-15T09:30:00Z" + updatedAt: "2024-01-15T16:45:20Z" + links: + GetLampById: + operationId: getLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used to get the updated lamp details using `GET /lamps/{lampId}`. + DeleteLamp: + operationId: deleteLamp + parameters: + lampId: '$response.body#/id' + description: > + The `id` value returned in the response can be used to delete the lamp using `DELETE /lamps/{lampId}`. '400': description: Invalid request data or lamp ID format content: @@ -142,6 +318,8 @@ paths: description: Lamp not found delete: summary: Delete a lamp + description: Delete a lamp. + tags: [lamps] operationId: deleteLamp parameters: - name: lampId