Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:

env:
PNPM_VERSION: 10.23.0
SWAG_VERSION: v1.16.6

concurrency:
group: ${{ github.ref }}
Expand Down Expand Up @@ -42,5 +43,13 @@ jobs:
- name: Install pnpm dependencies
run: pnpm install

- name: Install swag
run: go install github.com/swaggo/swag/cmd/swag@${{ env.SWAG_VERSION }}

- name: Check OpenAPI docs are up to date
run: |
swag init -g main.go --parseDependency --parseInternal
git diff --exit-code docs/

- name: Run tests
run: pnpm run test:all
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
firestore-debug.log
firebase-debug.log
ui-debug.log
.DS_Store
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
- `just test` or `npm run test:unit` — unit tests only.
- `just test-integration` or `npm test` — integration tests via emulator.
- `just test-all` or `npm run test:all` — unit + integration.
- OpenAPI docs:
- `just generate-docs` — regenerate `docs/` from swagger annotations (requires `swag` CLI: `go install github.com/swaggo/swag/cmd/swag@latest`).
- Generated files (`docs/docs.go`, `docs/swagger.json`, `docs/swagger.yaml`) must be committed; CI verifies they are in sync.
- The spec is served at runtime via `GET /openapi.yaml`.
- Docker:
- `just build` then `just run` (maps port and sets envs).

Expand Down
240 changes: 240 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs

import "github.com/swaggo/swag"

const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/openapi.yaml": {
"get": {
"description": "Returns the OpenAPI specification for this API in YAML format.",
"produces": [
"application/x-yaml"
],
"tags": [
"openapi"
],
"summary": "Download OpenAPI spec",
"responses": {
"200": {
"description": "OpenAPI spec in YAML format",
"schema": {
"type": "string"
}
}
}
}
},
"/v1/currencies": {
"get": {
"description": "Returns all available currencies with their human-readable names and currency signs.",
"produces": [
"application/json"
],
"tags": [
"currencies"
],
"summary": "Get currencies with names and signs",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.CurrenciesRecord"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/utils.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/utils.Error"
}
}
}
}
},
"/v1/rates/latest": {
"get": {
"description": "Get the latest currency exchange rates, optionally filtered by base currency and target symbols.",
"produces": [
"application/json"
],
"tags": [
"rates"
],
"summary": "Get latest exchange rates",
"parameters": [
{
"type": "string",
"description": "Base currency code (default: EUR)",
"name": "base",
"in": "query"
},
{
"type": "string",
"description": "Comma-separated list of target currency symbols",
"name": "symbols",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.ExchangeRateRecord"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/utils.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/utils.Error"
}
}
}
}
},
"/v1/rates/symbols": {
"get": {
"description": "Returns a list of all available currency symbols.",
"produces": [
"application/json"
],
"tags": [
"rates"
],
"summary": "Get available currency symbols",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.SymbolsRecord"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/utils.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/utils.Error"
}
}
}
}
}
},
"definitions": {
"handlers.CurrenciesRecord": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.NamedSymbol"
}
},
"date": {
"type": "string"
}
}
},
"handlers.ExchangeRateRecord": {
"type": "object",
"properties": {
"base": {
"type": "string"
},
"date": {
"type": "string"
},
"rates": {
"type": "object",
"additionalProperties": {
"type": "number",
"format": "float64"
}
}
}
},
"handlers.NamedSymbol": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"sign": {
"type": "string"
},
"symbol": {
"type": "string"
}
}
},
"handlers.SymbolsRecord": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"symbols": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"utils.Error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"status": {
"type": "integer"
}
}
}
}
}`

// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "localhost:8000",
BasePath: "/",
Schemes: []string{},
Title: "Forex API",
Description: "API for fetching currency exchange rates.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}

func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}
6 changes: 6 additions & 0 deletions docs/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package docs

import _ "embed"

//go:embed swagger.yaml
var SwaggerYAML []byte
Loading
Loading