diff --git a/db/README.md b/db/README.md index 941ddd9..435dd67 100644 --- a/db/README.md +++ b/db/README.md @@ -18,5 +18,13 @@ cp .env.template .env docker compose up -d ``` +## Development + +### Swagger Documentation + +```bash +swag init +``` + ## Traefik A traefik config file is available on [traefik.yml](./traefik.yml). It can be used with an OIDC provider ([Authentik](https://github.com/goauthentik/authentik) in our case) to control access to the database explorer. diff --git a/db/src/docs/docs.go b/db/src/docs/docs.go new file mode 100644 index 0000000..0ca231c --- /dev/null +++ b/db/src/docs/docs.go @@ -0,0 +1,159 @@ +// 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": { + "/rooms": { + "get": { + "description": "Get a list of all unique rooms from the measurement", + "produces": [ + "application/json" + ], + "tags": [ + "rooms" + ], + "summary": "Get all unique rooms", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/rooms/{room-id}/current": { + "get": { + "description": "Get the latest record for a specific room", + "produces": [ + "application/json" + ], + "tags": [ + "rooms" + ], + "summary": "Get current data for a room", + "parameters": [ + { + "type": "string", + "description": "Room ID", + "name": "room-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/rooms/{room-id}/history": { + "get": { + "description": "Get history for a specific room", + "produces": [ + "application/json" + ], + "tags": [ + "rooms" + ], + "summary": "Get history for a room", + "parameters": [ + { + "type": "string", + "description": "Room ID", + "name": "room-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "doc.db.e.kb28.ch", + BasePath: "/api/v1", + Schemes: []string{}, + Title: "Gateway API", + Description: "This is a gateway API for IoT data.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/db/src/docs/swagger.json b/db/src/docs/swagger.json new file mode 100644 index 0000000..383510c --- /dev/null +++ b/db/src/docs/swagger.json @@ -0,0 +1,135 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a gateway API for IoT data.", + "title": "Gateway API", + "contact": {}, + "version": "1.0" + }, + "host": "doc.db.e.kb28.ch", + "basePath": "/api/v1", + "paths": { + "/rooms": { + "get": { + "description": "Get a list of all unique rooms from the measurement", + "produces": [ + "application/json" + ], + "tags": [ + "rooms" + ], + "summary": "Get all unique rooms", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/rooms/{room-id}/current": { + "get": { + "description": "Get the latest record for a specific room", + "produces": [ + "application/json" + ], + "tags": [ + "rooms" + ], + "summary": "Get current data for a room", + "parameters": [ + { + "type": "string", + "description": "Room ID", + "name": "room-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/rooms/{room-id}/history": { + "get": { + "description": "Get history for a specific room", + "produces": [ + "application/json" + ], + "tags": [ + "rooms" + ], + "summary": "Get history for a room", + "parameters": [ + { + "type": "string", + "description": "Room ID", + "name": "room-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/db/src/docs/swagger.yaml b/db/src/docs/swagger.yaml new file mode 100644 index 0000000..62ff953 --- /dev/null +++ b/db/src/docs/swagger.yaml @@ -0,0 +1,90 @@ +basePath: /api/v1 +host: doc.db.e.kb28.ch +info: + contact: {} + description: This is a gateway API for IoT data. + title: Gateway API + version: "1.0" +paths: + /rooms: + get: + description: Get a list of all unique rooms from the measurement + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + summary: Get all unique rooms + tags: + - rooms + /rooms/{room-id}/current: + get: + description: Get the latest record for a specific room + parameters: + - description: Room ID + in: path + name: room-id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + summary: Get current data for a room + tags: + - rooms + /rooms/{room-id}/history: + get: + description: Get history for a specific room + parameters: + - description: Room ID + in: path + name: room-id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: + type: string + type: object + summary: Get history for a room + tags: + - rooms +swagger: "2.0" diff --git a/db/src/go.mod b/db/src/go.mod index a080fe6..d70e67f 100644 --- a/db/src/go.mod +++ b/db/src/go.mod @@ -11,6 +11,9 @@ require ( ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/apache/arrow-go/v18 v18.5.1 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.15.0 // indirect @@ -19,6 +22,10 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect @@ -27,10 +34,12 @@ require ( github.com/google/flatbuffers v25.12.19+incompatible // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/influxdata/line-protocol/v2 v2.2.1 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -40,6 +49,9 @@ require ( github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/swaggo/files v1.0.1 // indirect + github.com/swaggo/gin-swagger v1.6.1 // indirect + github.com/swaggo/swag v1.16.4 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect @@ -58,5 +70,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.79.1 // indirect google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/db/src/main.go b/db/src/main.go index f6762da..b87e8b8 100644 --- a/db/src/main.go +++ b/db/src/main.go @@ -93,6 +93,11 @@ func influxConnection() *influx.InfluxGateway { return gateway } +// @title Gateway API +// @version 1.0 +// @description This is a gateway API for IoT data. +// @host doc.db.e.kb28.ch +// @BasePath /api/v1 func main() { // Load mapping configuration mappingPath := os.Getenv("MAPPING_CONFIG_PATH") diff --git a/db/src/rest/rest.go b/db/src/rest/rest.go index f8bf2bc..21e594f 100644 --- a/db/src/rest/rest.go +++ b/db/src/rest/rest.go @@ -6,7 +6,11 @@ import ( "gateway/influx" "net/http" + _ "gateway/docs" + "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) type RestGateway struct { @@ -33,6 +37,8 @@ func (g *RestGateway) setupRoutes() { v1.GET("/rooms/:room-id/current", g.getRoomCurrent) v1.GET("/rooms/:room-id/history", g.getRoomHistory) } + + g.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) } func (g *RestGateway) Run(addr string) error { @@ -40,6 +46,14 @@ func (g *RestGateway) Run(addr string) error { } // GET /api/v1/rooms +// getRooms godoc +// @Summary Get all unique rooms +// @Description Get a list of all unique rooms from the measurement +// @Tags rooms +// @Produce json +// @Success 200 {array} string +// @Failure 500 {object} map[string]string +// @Router /rooms [get] func (g *RestGateway) getRooms(c *gin.Context) { // Query unique rooms from the measurement query := fmt.Sprintf(`SELECT DISTINCT("room") FROM "%s"`, g.measurementName) @@ -68,6 +82,16 @@ func (g *RestGateway) getRooms(c *gin.Context) { } // GET /api/v1/rooms/{room-id}/current +// getRoomCurrent godoc +// @Summary Get current data for a room +// @Description Get the latest record for a specific room +// @Tags rooms +// @Produce json +// @Param room-id path string true "Room ID" +// @Success 200 {object} map[string]any +// @Failure 404 {object} map[string]string +// @Failure 500 {object} map[string]string +// @Router /rooms/{room-id}/current [get] func (g *RestGateway) getRoomCurrent(c *gin.Context) { roomID := c.Param("room-id") @@ -95,6 +119,15 @@ func (g *RestGateway) getRoomCurrent(c *gin.Context) { } // GET /api/v1/rooms/{room-id}/history +// getRoomHistory godoc +// @Summary Get history for a room +// @Description Get history for a specific room +// @Tags rooms +// @Produce json +// @Param room-id path string true "Room ID" +// @Success 200 {array} map[string]any +// @Failure 500 {object} map[string]string +// @Router /rooms/{room-id}/history [get] func (g *RestGateway) getRoomHistory(c *gin.Context) { roomID := c.Param("room-id") diff --git a/db/traefik.yml b/db/traefik.yml index 1eaed3c..339fb13 100644 --- a/db/traefik.yml +++ b/db/traefik.yml @@ -1,4 +1,9 @@ http: + middlewares: + pi-db-doc-redirect: + redirectRegex: + regex: "^https://doc.db.e.kb28.ch/$" + replacement: "https://doc.db.e.kb28.ch/swagger/index.html" # middlewares: # oidc-auth-pi-db: # plugin: @@ -33,6 +38,15 @@ http: service: pi-mqtt-management tls: certResolver: letsencrypt + pi-db-doc: + rule: "Host(`doc.db.e.kb28.ch`)" + entryPoints: + - websecure + service: pi-db-doc + tls: + certResolver: letsencrypt + middlewares: + - pi-db-doc-redirect services: pi-db-ui: @@ -50,6 +64,11 @@ http: servers: - url: "http://192.168.42.211:15672" passHostHeader: true + pi-db-doc: + loadBalancer: + servers: + - url: "http://192.168.42.211:8080" + passHostHeader: true tcp: routers: