From 308fe1fdaf9f8876e0e85432881e5b76710b2c24 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 9 Mar 2022 11:22:46 +0100 Subject: [PATCH 01/11] feat: Add api doc example with swaggo/swag * based on 2dba85dba3afad0175b2b1e68445d63fe2d7c089 * build with `make api-docs` * Swagger UI available at `/api/docs/index.html` --- .gitignore | 2 + Makefile | 8 +++ api/docs/docs.go | 181 +++++++++++++++++++++++++++++++++++++++++++++++ api/v1/api.go | 12 ++++ api/v1/shows.go | 24 +++++++ cmd/tank/web.go | 13 ++-- go.mod | 11 +-- go.sum | 168 ++++++++++++++++++++++++++++++++++++------- 8 files changed, 384 insertions(+), 35 deletions(-) create mode 100644 api/docs/docs.go diff --git a/.gitignore b/.gitignore index 5f8cf36..03fcb41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /tank +/tank.yaml /vendor/ /cover.out /coverage.html +/api/docs/swagger.* \ No newline at end of file diff --git a/Makefile b/Makefile index 4e7b452..c2f9662 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,11 @@ ifdef GOROOT GOCMD = $(GOROOT)/bin/go endif +SWAG := swag +ifdef GOPATH +SWAG = $(GOPATH)/bin/swag +endif + EXECUTEABLE := tank all: build @@ -35,6 +40,9 @@ format: ui: $(GOCMD) generate ./ui +api-docs: + $(SWAG) init -d api/v1/,./ -g api.go -o api/docs + build: ui $(GOCMD) build -o $(EXECUTEABLE) ./cmd/tank diff --git a/api/docs/docs.go b/api/docs/docs.go new file mode 100644 index 0000000..f927422 --- /dev/null +++ b/api/docs/docs.go @@ -0,0 +1,181 @@ +// Package docs GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "url": "https://gitlab.servus.at/autoradio/tank" + }, + "license": { + "name": "GPL 3", + "url": "https://www.gnu.org/licenses/gpl" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/shows": { + "get": { + "description": "Lists all existing shows", + "produces": [ + "application/json" + ], + "summary": "List shows", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ShowsListing" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{id}": { + "post": { + "description": "Creates a new show", + "produces": [ + "application/json" + ], + "summary": "Create show", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.Show" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Deletes a show", + "produces": [ + "application/json" + ], + "summary": "Delete show", + "responses": { + "204": { + "description": "" + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "api_v1.ErrorResponse": { + "type": "object", + "properties": { + "detail": {}, + "error": { + "type": "string" + } + } + }, + "api_v1.ShowsListing": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/store.Show" + } + } + } + }, + "gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse": { + "type": "object", + "properties": { + "detail": {}, + "error": { + "type": "string" + } + } + }, + "gitlab.servus.at_autoradio_tank_api_v1.ShowsListing": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/store.Show" + } + } + } + }, + "store.Show": { + "type": "object", + "properties": { + "created": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updated": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "AURA Tank API", + Description: "Import & Playlist Daemon", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/api/v1/api.go b/api/v1/api.go index ea49aa8..4dd3c39 100644 --- a/api/v1/api.go +++ b/api/v1/api.go @@ -28,6 +28,18 @@ import ( "gitlab.servus.at/autoradio/tank/store" ) +// @title AURA Tank API +// @version 1.0 +// @description Import & Playlist Daemon +// @contact.url https://gitlab.servus.at/autoradio/tank + +// @license.name GPL 3 +// @license.url https://www.gnu.org/licenses/gpl + +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name Authorization + type API struct { store *store.Store importer *importer.Importer diff --git a/api/v1/shows.go b/api/v1/shows.go index b4d11e6..b3c0eed 100644 --- a/api/v1/shows.go +++ b/api/v1/shows.go @@ -26,6 +26,14 @@ import ( "gitlab.servus.at/autoradio/tank/store" ) +// ListShows returns a list of all shows that are accessible to the current session. +// If authentication is disabled a least of all existing shows is returned. +// @Summary List shows +// @Description Lists all existing shows +// @Produce json +// @Success 200 {object} ShowsListing +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows [get] func (api *API) ListShows(c *gin.Context) { offset, limit, ok := getPaginationParameter(c) if !ok { @@ -72,6 +80,14 @@ func (api *API) ListShows(c *gin.Context) { c.JSON(http.StatusOK, ShowsListing{Shows: shows}) } +// CreateShow creates a new show. +// @Summary Create show +// @Description Creates a new show +// @Produce json +// @Success 200 {object} store.Show +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{id} [post] func (api *API) CreateShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -97,6 +113,14 @@ func (api *API) CreateShow(c *gin.Context) { c.JSON(http.StatusCreated, show) } +// DeleteShow deletes a show. +// @Summary Delete show +// @Description Deletes a show +// @Produce json +// @Success 204 +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{id} [delete] func (api *API) DeleteShow(c *gin.Context) { showID := c.Param("show-id") if authorized, s := authorizeRequestForShow(c, showID); !authorized { diff --git a/cmd/tank/web.go b/cmd/tank/web.go index 163dcf8..512b6cd 100644 --- a/cmd/tank/web.go +++ b/cmd/tank/web.go @@ -30,6 +30,9 @@ import ( "github.com/gin-gonic/gin" cors "github.com/rs/cors/wrapper/gin" + "github.com/swaggo/gin-swagger" + "github.com/swaggo/gin-swagger/swaggerFiles" + _ "gitlab.servus.at/autoradio/tank/api/docs" apiV1 "gitlab.servus.at/autoradio/tank/api/v1" "gitlab.servus.at/autoradio/tank/auth" "gitlab.servus.at/autoradio/tank/importer" @@ -38,10 +41,11 @@ import ( ) const ( - WebUIPathPrefix = "/ui/" - WebAuthPrefix = "/auth/" - WebAPIv1Prefix = "/api/v1/" - HealthzEndpoint = "/healthz" + WebUIPathPrefix = "/ui/" + WebAuthPrefix = "/auth/" + WebAPIDocsPrefix = "/api/docs/" + WebAPIv1Prefix = "/api/v1/" + HealthzEndpoint = "/healthz" ) func apache2CombinedLogger(param gin.LogFormatterParams) string { @@ -161,6 +165,7 @@ func runWeb(ln net.Listener, st *store.Store, im *importer.Importer, conf WebCon apiV1.InstallHTTPHandler(r.Group(WebAPIv1Prefix), st, im, infoLog, errLog, dbgLog) auth.InstallHTTPHandler(r.Group(WebAuthPrefix)) r.GET(HealthzEndpoint, func(c *gin.Context) { healthzHandler(c, st, im) }) + r.GET(WebAPIDocsPrefix+"*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) srv := &http.Server{ Handler: r, diff --git a/go.mod b/go.mod index 7aade99..6670a06 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,16 @@ require ( github.com/codegangsta/cli v1.20.0 github.com/coreos/go-oidc v2.0.0+incompatible github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 - github.com/gin-gonic/gin v1.4.0 + github.com/gin-gonic/gin v1.7.7 github.com/jinzhu/gorm v1.9.8 - github.com/kr/pretty v0.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/rs/cors v1.6.0 - github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect - github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd // indirect + github.com/swaggo/gin-swagger v1.4.1 + github.com/swaggo/swag v1.8.0 golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 gopkg.in/gormigrate.v1 v1.5.0 gopkg.in/square/go-jose.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) diff --git a/go.sum b/go.sum index 2dbbbb0..75eecf9 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,16 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= +github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -17,6 +25,9 @@ github.com/coreos/go-oidc v2.0.0+incompatible h1:+RStIopZ8wooMx+Vs5Bt8zMXxV1ABl5 github.com/coreos/go-oidc v2.0.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -30,12 +41,36 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k= +github.com/gin-contrib/gzip v0.0.3/go.mod h1:YxxswVZIqOvcHEQpsSn+QF5guQtO1dCfy0shBPy4jFc= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -47,15 +82,17 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -72,9 +109,12 @@ github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns= github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -84,24 +124,44 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= +github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI= +github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -118,18 +178,35 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/gin-swagger v1.4.1 h1:F2vJndw+Q+ZBOlsC6CaodqXJV3ZOf6hpg/4Y6MEx5BM= +github.com/swaggo/gin-swagger v1.4.1/go.mod h1:hmJ1vPn+XjUvnbzjCdUAxVqgraxELxk8x5zAsjCE5mg= +github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU= +github.com/swaggo/swag v1.8.0 h1:80NNhvpJcuItNpBDqgJwDuKlMmaZ/OATOzhG3bhcM3w= +github.com/swaggo/swag v1.8.0/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -137,10 +214,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90Pveol golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -150,8 +233,13 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 h1:jIOcLT9BZzyJ9ce+IwwZ+aF9yeCqzrR+NrD68a/SHKw= @@ -160,21 +248,46 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -190,11 +303,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/gormigrate.v1 v1.5.0 h1:M667uzFRcnBf5cNAcSyYyNdJTJ1KQnUTmc+mjCLfPfw= gopkg.in/gormigrate.v1 v1.5.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw= gopkg.in/square/go-jose.v2 v2.3.0 h1:nLzhkFyl5bkblqYBoiWJUt5JkWOzmiaBtCxdJAqJd3U= @@ -204,8 +315,13 @@ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA= -gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -- GitLab From 467251439d91f59f1d1195de0781c4bef56873b5 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 9 Mar 2022 12:15:24 +0100 Subject: [PATCH 02/11] feat: Document show endpoints --- Makefile | 4 +++- api/docs/docs.go | 57 ++++++++++++++++++++++++++++++++++++++++-------- api/v1/api.go | 22 ++++++++++--------- api/v1/shows.go | 47 +++++++++++++++++++++------------------ 4 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index c2f9662..d11c9ff 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ SWAG := swag ifdef GOPATH SWAG = $(GOPATH)/bin/swag endif +SWAG_ARGS := -d api/v1/,./ -g api.go EXECUTEABLE := tank @@ -41,7 +42,8 @@ ui: $(GOCMD) generate ./ui api-docs: - $(SWAG) init -d api/v1/,./ -g api.go -o api/docs + $(SWAG) fmt $(SWAG_ARGS) + $(SWAG) init $(SWAG_ARGS) -o api/docs build: ui $(GOCMD) build -o $(EXECUTEABLE) ./cmd/tank diff --git a/api/docs/docs.go b/api/docs/docs.go index f927422..7ffa929 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -11,11 +11,12 @@ const docTemplate = `{ "description": "{{escape .Description}}", "title": "{{.Title}}", "contact": { + "name": "API Support", "url": "https://gitlab.servus.at/autoradio/tank" }, "license": { - "name": "GPL 3", - "url": "https://www.gnu.org/licenses/gpl" + "name": "AGPLv3", + "url": "https://www.gnu.org/licenses/agpl-3.0" }, "version": "{{.Version}}" }, @@ -29,29 +30,58 @@ const docTemplate = `{ "application/json" ], "summary": "List shows", + "parameters": [ + { + "type": "integer", + "description": "Limit number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Start listing from offset", + "name": "offset", + "in": "query" + } + ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ShowsListing" + "$ref": "#/definitions/api_v1.ShowsListing" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + "$ref": "#/definitions/api_v1.ErrorResponse" } } } } }, - "/api/v1/shows/{id}": { + "/api/v1/shows/{name}": { "post": { "description": "Creates a new show", "produces": [ "application/json" ], "summary": "Create show", + "parameters": [ + { + "type": "string", + "description": "Name of the show to be created", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "If given, all files and playlists will be copied from the show", + "name": "clone-from", + "in": "query" + } + ], "responses": { "200": { "description": "OK", @@ -62,13 +92,13 @@ const docTemplate = `{ "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + "$ref": "#/definitions/api_v1.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + "$ref": "#/definitions/api_v1.ErrorResponse" } } } @@ -79,6 +109,15 @@ const docTemplate = `{ "application/json" ], "summary": "Delete show", + "parameters": [ + { + "type": "string", + "description": "Name of the show to be deleted", + "name": "name", + "in": "path", + "required": true + } + ], "responses": { "204": { "description": "" @@ -86,13 +125,13 @@ const docTemplate = `{ "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + "$ref": "#/definitions/api_v1.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse" + "$ref": "#/definitions/api_v1.ErrorResponse" } } } diff --git a/api/v1/api.go b/api/v1/api.go index 4dd3c39..c546f09 100644 --- a/api/v1/api.go +++ b/api/v1/api.go @@ -1,6 +1,6 @@ // // tank, Import and Playlist Daemon for Aura project -// Copyright (C) 2017-2020 Christian Pointner +// Copyright (C) 2017-2020 Christian Pointner // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -28,17 +28,19 @@ import ( "gitlab.servus.at/autoradio/tank/store" ) -// @title AURA Tank API -// @version 1.0 -// @description Import & Playlist Daemon -// @contact.url https://gitlab.servus.at/autoradio/tank +// @title AURA Tank API +// @version 1.0 +// @description Import & Playlist Daemon -// @license.name GPL 3 -// @license.url https://www.gnu.org/licenses/gpl +// @contact.name API Support +// @contact.url https://gitlab.servus.at/autoradio/tank -// @securityDefinitions.apikey ApiKeyAuth -// @in header -// @name Authorization +// @license.name AGPLv3 +// @license.url https://www.gnu.org/licenses/agpl-3.0 + +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name Authorization type API struct { store *store.Store diff --git a/api/v1/shows.go b/api/v1/shows.go index b3c0eed..9662213 100644 --- a/api/v1/shows.go +++ b/api/v1/shows.go @@ -27,13 +27,15 @@ import ( ) // ListShows returns a list of all shows that are accessible to the current session. -// If authentication is disabled a least of all existing shows is returned. -// @Summary List shows -// @Description Lists all existing shows -// @Produce json -// @Success 200 {object} ShowsListing -// @Failure 500 {object} ErrorResponse -// @Router /api/v1/shows [get] +// If authentication is disabled a list of all existing shows is returned. +// @Summary List shows +// @Description Lists all existing shows +// @Produce json +// @Param limit query int false "Limit number of results" +// @Param offset query int false "Start listing from offset" +// @Success 200 {object} ShowsListing +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows [get] func (api *API) ListShows(c *gin.Context) { offset, limit, ok := getPaginationParameter(c) if !ok { @@ -81,13 +83,15 @@ func (api *API) ListShows(c *gin.Context) { } // CreateShow creates a new show. -// @Summary Create show -// @Description Creates a new show -// @Produce json -// @Success 200 {object} store.Show -// @Failure 403 {object} ErrorResponse -// @Failure 500 {object} ErrorResponse -// @Router /api/v1/shows/{id} [post] +// @Summary Create show +// @Description Creates a new show +// @Produce json +// @Param name path string true "Name of the show to be created" +// @Param clone-from query string false "If given, all files and playlists will be copied from the show" +// @Success 200 {object} store.Show +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name} [post] func (api *API) CreateShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -114,13 +118,14 @@ func (api *API) CreateShow(c *gin.Context) { } // DeleteShow deletes a show. -// @Summary Delete show -// @Description Deletes a show -// @Produce json -// @Success 204 -// @Failure 403 {object} ErrorResponse -// @Failure 500 {object} ErrorResponse -// @Router /api/v1/shows/{id} [delete] +// @Summary Delete show +// @Description Deletes a show +// @Produce json +// @Param name path string true "Name of the show to be deleted" +// @Success 204 +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name} [delete] func (api *API) DeleteShow(c *gin.Context) { showID := c.Param("show-id") if authorized, s := authorizeRequestForShow(c, showID); !authorized { -- GitLab From cd10e239f27f3e3a82736aeb00e127c8cdd0327f Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 10 Mar 2022 09:10:25 +0100 Subject: [PATCH 03/11] fix: Add missing dependency --- go.mod | 2 ++ go.sum | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index 6670a06..c50f10c 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ require ( github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/rs/cors v1.6.0 + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect + github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect github.com/swaggo/gin-swagger v1.4.1 github.com/swaggo/swag v1.8.0 golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 diff --git a/go.sum b/go.sum index 75eecf9..2360d8b 100644 --- a/go.sum +++ b/go.sum @@ -180,8 +180,12 @@ github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -- GitLab From dba4d34a19fe6e904aa74d83b55746d36fb3cee7 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 10 Mar 2022 12:31:04 +0100 Subject: [PATCH 04/11] feat: Add API doc for files endpoints --- Makefile | 8 +- api/docs/docs.go | 810 +++++++++++++++++++++++++++++++++++++++-- api/v1/files.go | 80 ++++ api/v1/files_import.go | 35 ++ api/v1/shows.go | 8 +- 5 files changed, 913 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index d11c9ff..7405651 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ SWAG := swag ifdef GOPATH SWAG = $(GOPATH)/bin/swag endif -SWAG_ARGS := -d api/v1/,./ -g api.go +SWAG_ARGS := -d api/v1/ -g api.go EXECUTEABLE := tank @@ -41,9 +41,11 @@ format: ui: $(GOCMD) generate ./ui -api-docs: +fmt-api-docs: $(SWAG) fmt $(SWAG_ARGS) - $(SWAG) init $(SWAG_ARGS) -o api/docs + +api-docs: + $(SWAG) init $(SWAG_ARGS) --pd -o api/docs build: ui $(GOCMD) build -o $(EXECUTEABLE) ./cmd/tank diff --git a/api/docs/docs.go b/api/docs/docs.go index 7ffa929..5273fc4 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -48,13 +48,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api_v1.ShowsListing" + "$ref": "#/definitions/v1.ShowsListing" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api_v1.ErrorResponse" + "$ref": "#/definitions/v1.ErrorResponse" } } } @@ -92,13 +92,13 @@ const docTemplate = `{ "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api_v1.ErrorResponse" + "$ref": "#/definitions/v1.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api_v1.ErrorResponse" + "$ref": "#/definitions/v1.ErrorResponse" } } } @@ -120,18 +120,546 @@ const docTemplate = `{ ], "responses": { "204": { - "description": "" + "description": "No Content" }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api_v1.ErrorResponse" + "$ref": "#/definitions/v1.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api_v1.ErrorResponse" + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/files": { + "get": { + "description": "Lists files of show", + "produces": [ + "application/json" + ], + "summary": "List files", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Limit number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Start listing from offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.FilesListing" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "post": { + "description": "Adds a file and starts import", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Add file", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "description": "URI of the file", + "name": "file", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.FileCreateRequest" + } + }, + { + "type": "string", + "description": "running|done - If given, return not before import has the given state", + "name": "wait-for", + "in": "query" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/store.File" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/files/{id}": { + "get": { + "description": "Retrieves file object.", + "produces": [ + "application/json" + ], + "summary": "Retrieve file", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.File" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Removes a file.", + "summary": "Delete file", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "patch": { + "description": "Updates file metadata.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Update file", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "File metadata", + "name": "metadata", + "in": "body", + "schema": { + "$ref": "#/definitions/store.FileMetadata" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.File" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/files/{id}/import": { + "get": { + "description": "Retrieves import status of the file.", + "produces": [ + "application/json" + ], + "summary": "Retrieve import status", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "running|done - If given, return not before import has the given state", + "name": "wait-for", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/importer.Job" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "404": { + "description": "No job for this file", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Cancels import of file.", + "summary": "Cancel file import", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "404": { + "description": "No job for this file", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/files/{id}/logs": { + "get": { + "description": "Retrieves import logs of the file.", + "produces": [ + "application/json" + ], + "summary": "Retrieve import logs", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.FileImportLogs" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/files/{id}/usage": { + "get": { + "description": "Lists playlists referring to the file.", + "produces": [ + "application/json" + ], + "summary": "List referring playlists", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.FileUsageListing" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/imports": { + "get": { + "description": "Lists all running and pending imports", + "produces": [ + "application/json" + ], + "summary": "List imports", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Limit number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Start listing from offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.JobsListing" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" } } } @@ -139,43 +667,206 @@ const docTemplate = `{ } }, "definitions": { - "api_v1.ErrorResponse": { + "importer.Job": { "type": "object", "properties": { - "detail": {}, - "error": { + "created": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "progress": { + "type": "integer" + }, + "ref-id": { + "type": "string" + }, + "show": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/importer.SourceURL" + }, + "started": { + "type": "integer" + }, + "state": { + "type": "integer" + }, + "user": { "type": "string" } } }, - "api_v1.ShowsListing": { + "importer.SourceURL": { "type": "object", "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/definitions/store.Show" - } + "forceQuery": { + "description": "append a query ('?') even if RawQuery is empty", + "type": "boolean" + }, + "fragment": { + "description": "fragment for references, without '#'", + "type": "string" + }, + "host": { + "description": "host or host:port", + "type": "string" + }, + "opaque": { + "description": "encoded opaque data", + "type": "string" + }, + "path": { + "description": "path (relative paths may omit leading slash)", + "type": "string" + }, + "rawFragment": { + "description": "encoded fragment hint (see EscapedFragment method)", + "type": "string" + }, + "rawPath": { + "description": "encoded path hint (see EscapedPath method)", + "type": "string" + }, + "rawQuery": { + "description": "encoded query values, without '?'", + "type": "string" + }, + "scheme": { + "type": "string" + }, + "user": { + "description": "username and password information", + "$ref": "#/definitions/url.Userinfo" } } }, - "gitlab.servus.at_autoradio_tank_api_v1.ErrorResponse": { + "store.File": { + "type": "object", + "properties": { + "created": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/store.FileMetadata" + }, + "show": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "source": { + "$ref": "#/definitions/store.FileSource" + }, + "updated": { + "type": "string" + } + } + }, + "store.FileMetadata": { + "type": "object", + "properties": { + "album": { + "type": "string" + }, + "artist": { + "description": "actually a full-text index would be nice here...", + "type": "string" + }, + "isrc": { + "type": "string" + }, + "organization": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "store.FileSource": { + "type": "object", + "properties": { + "hash": { + "type": "string" + }, + "import": { + "$ref": "#/definitions/store.Import" + }, + "uri": { + "type": "string" + } + } + }, + "store.Import": { "type": "object", "properties": { - "detail": {}, "error": { "type": "string" + }, + "state": { + "type": "integer" } } }, - "gitlab.servus.at_autoradio_tank_api_v1.ShowsListing": { + "store.ImportLogs": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/store.Log" + } + }, + "store.Log": { + "type": "object" + }, + "store.Playlist": { "type": "object", "properties": { - "results": { + "created": { + "type": "string" + }, + "description": { + "type": "string" + }, + "entries": { "type": "array", "items": { - "$ref": "#/definitions/store.Show" + "$ref": "#/definitions/store.PlaylistEntry" } + }, + "id": { + "type": "integer" + }, + "playout-mode": { + "type": "string" + }, + "show": { + "type": "string" + }, + "updated": { + "type": "string" + } + } + }, + "store.PlaylistEntry": { + "type": "object", + "properties": { + "duration": { + "type": "integer" + }, + "file": { + "$ref": "#/definitions/store.File" + }, + "uri": { + "type": "string" } } }, @@ -192,6 +883,83 @@ const docTemplate = `{ "type": "string" } } + }, + "url.Userinfo": { + "type": "object" + }, + "v1.ErrorResponse": { + "type": "object", + "properties": { + "detail": {}, + "error": { + "type": "string" + } + } + }, + "v1.FileCreateRequest": { + "type": "object", + "properties": { + "source-uri": { + "type": "string" + } + } + }, + "v1.FileImportLogs": { + "type": "object", + "properties": { + "results": { + "$ref": "#/definitions/store.ImportLogs" + } + } + }, + "v1.FileUsageListing": { + "type": "object", + "properties": { + "results": { + "type": "object", + "properties": { + "playlists": { + "type": "array", + "items": { + "$ref": "#/definitions/store.Playlist" + } + } + } + } + } + }, + "v1.FilesListing": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/store.File" + } + } + } + }, + "v1.JobsListing": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/importer.Job" + } + } + } + }, + "v1.ShowsListing": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/store.Show" + } + } + } } }, "securityDefinitions": { diff --git a/api/v1/files.go b/api/v1/files.go index 86bc386..c257032 100644 --- a/api/v1/files.go +++ b/api/v1/files.go @@ -27,6 +27,17 @@ import ( "gitlab.servus.at/autoradio/tank/store" ) +// ListFilesOfShow returns a list of all files of the show. +// @Summary List files +// @Description Lists files of show +// @Produce json +// @Param name path string true "Name of the show" +// @Param limit query int false "Limit number of results" +// @Param offset query int false "Start listing from offset" +// @Success 200 {object} FilesListing +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files [get] func (api *API) ListFilesOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -45,6 +56,19 @@ func (api *API) ListFilesOfShow(c *gin.Context) { c.JSON(http.StatusOK, FilesListing{files}) } +// CreateFileForShow adds a file and starts import. +// @Summary Add file +// @Description Adds a file and starts import +// @Accept json +// @Produce json +// @Param name path string true "Name of the show" +// @Param file body FileCreateRequest true "URI of the file" +// @Param wait-for query string false "running|done - If given, return not before import has the given state" +// @Success 201 {object} store.File +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files [post] func (api *API) CreateFileForShow(c *gin.Context) { showID := c.Param("show-id") authorized, sess := authorizeRequestForShow(c, showID) @@ -131,6 +155,17 @@ create_file_response: c.JSON(status, errResp) } +// ReadFileOfShow retrieves file object. +// @Summary Retrieve file +// @Description Retrieves file object. +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 200 {object} store.File +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id} [get] func (api *API) ReadFileOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -150,6 +185,19 @@ func (api *API) ReadFileOfShow(c *gin.Context) { c.JSON(http.StatusOK, file) } +// PatchFileOfShow updates file metadata. +// @Summary Update file +// @Description Updates file metadata. +// @Accept json +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Param metadata body store.FileMetadata false "File metadata" +// @Success 200 {object} store.File +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id} [patch] func (api *API) PatchFileOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -174,6 +222,16 @@ func (api *API) PatchFileOfShow(c *gin.Context) { c.JSON(http.StatusOK, file) } +// DeleteFileOfShow removes a file. +// @Summary Delete file +// @Description Removes a file. +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 204 {object} nil +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id} [delete] func (api *API) DeleteFileOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -195,6 +253,17 @@ func (api *API) DeleteFileOfShow(c *gin.Context) { c.JSON(http.StatusNoContent, nil) } +// ReadUsageOfFile lists playlists referring to the file. +// @Summary List referring playlists +// @Description Lists playlists referring to the file. +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 200 {object} FileUsageListing +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/usage [get] func (api *API) ReadUsageOfFile(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -214,6 +283,17 @@ func (api *API) ReadUsageOfFile(c *gin.Context) { c.JSON(http.StatusOK, result) } +// ReadLogsOfFile retrieves import logs of the file. +// @Summary Retrieve import logs +// @Description Retrieves import logs of the file. +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 200 {object} FileImportLogs +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/logs [get] func (api *API) ReadLogsOfFile(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { diff --git a/api/v1/files_import.go b/api/v1/files_import.go index b350703..63e4bc2 100644 --- a/api/v1/files_import.go +++ b/api/v1/files_import.go @@ -24,6 +24,17 @@ import ( "github.com/gin-gonic/gin" ) +// ListImportsOfShow returns a list of all running and pending imports for files belonging to this show. +// @Summary List imports +// @Description Lists all running and pending imports +// @Produce json +// @Param name path string true "Name of the show" +// @Param limit query int false "Limit number of results" +// @Param offset query int false "Start listing from offset" +// @Success 200 {object} JobsListing +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/imports [get] func (api *API) ListImportsOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -42,6 +53,19 @@ func (api *API) ListImportsOfShow(c *gin.Context) { c.JSON(http.StatusOK, JobsListing{jobs}) } +// ReadImportOfFile retrieves import status of the file. +// @Summary Retrieve import status +// @Description Retrieves import status of the file. +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Param wait-for query string false "running|done - If given, return not before import has the given state" +// @Success 200 {object} importer.Job +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse "No job for this file" +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/import [get] func (api *API) ReadImportOfFile(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -80,6 +104,17 @@ func (api *API) ReadImportOfFile(c *gin.Context) { c.JSON(http.StatusOK, job) } +// CancelImportOfFile cancels import of file. +// @Summary Cancel file import +// @Description Cancels import of file. +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 204 {object} nil +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse "No job for this file" +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/import [delete] func (api *API) CancelImportOfFile(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { diff --git a/api/v1/shows.go b/api/v1/shows.go index 9662213..1fb97d1 100644 --- a/api/v1/shows.go +++ b/api/v1/shows.go @@ -121,10 +121,10 @@ func (api *API) CreateShow(c *gin.Context) { // @Summary Delete show // @Description Deletes a show // @Produce json -// @Param name path string true "Name of the show to be deleted" -// @Success 204 -// @Failure 403 {object} ErrorResponse -// @Failure 500 {object} ErrorResponse +// @Param name path string true "Name of the show to be deleted" +// @Success 204 {object} nil +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name} [delete] func (api *API) DeleteShow(c *gin.Context) { showID := c.Param("show-id") -- GitLab From b1141910c02b9f91239dbaff2013dc60dd8b69ad Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 14 Mar 2022 11:30:59 +0100 Subject: [PATCH 05/11] feat: Add API doc for uploads and playlists --- api/docs/docs.go | 519 +++++++++++++++++++++++++++++++++++++++++ api/v1/files_upload.go | 38 +++ api/v1/playlists.go | 77 ++++++ 3 files changed, 634 insertions(+) diff --git a/api/docs/docs.go b/api/docs/docs.go index 5273fc4..5ef5f88 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -23,6 +23,93 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/v1/playlists": { + "get": { + "description": "Lists all playlists.", + "produces": [ + "application/json" + ], + "summary": "List playlists", + "parameters": [ + { + "type": "integer", + "description": "Limit number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Start listing from offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.PlaylistsListing" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/playlists/{id}": { + "get": { + "description": "Retrieves a playlist.", + "produces": [ + "application/json" + ], + "summary": "Retrieve playlist", + "parameters": [ + { + "type": "integer", + "description": "ID of the playlist", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.Playlist" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, "/api/v1/shows": { "get": { "description": "Lists all existing shows", @@ -564,6 +651,168 @@ const docTemplate = `{ } } }, + "/api/v1/shows/{name}/files/{id}/upload": { + "get": { + "description": "Uploads file content. Only available if file was created using SourceURI set to upload://-type and file import state is running.", + "summary": "Upload file content", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "A unique identifier for the uploaded file", + "name": "flowIdentifier", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "put": { + "description": "Uploads file content. Only available if file was created using SourceURI set to upload://-type and file import state is running.", + "consumes": [ + "application/octet-stream" + ], + "produces": [ + "application/json" + ], + "summary": "Upload file content", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "post": { + "description": "Uploads file content via flow.js. Only available if file was created using SourceURI set to upload://-type and file import state is running.", + "consumes": [ + "multipart/form-data" + ], + "summary": "Upload file content", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the file", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, "/api/v1/shows/{name}/files/{id}/usage": { "get": { "description": "Lists playlists referring to the file.", @@ -664,6 +913,265 @@ const docTemplate = `{ } } } + }, + "/api/v1/shows/{name}/playlists": { + "get": { + "description": "Lists playlists of show.", + "produces": [ + "application/json" + ], + "summary": "List playlists", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Limit number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Start listing from offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.PlaylistsListing" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "post": { + "description": "Creates a new playlist for the show.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Create playlist", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "description": "Playlist data", + "name": "playlist", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/store.Playlist" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/store.Playlist" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } + }, + "/api/v1/shows/{name}/playlists/{id}": { + "get": { + "description": "Retrieves a playlist of a show.", + "produces": [ + "application/json" + ], + "summary": "Retrieve playlist", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the playlist", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.Playlist" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "put": { + "description": "Updates a playlist of a show.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Update playlist", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the playlist", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Playlist data", + "name": "playlist", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/store.Playlist" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.Playlist" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Deletes a playlist of a show.", + "summary": "Delete playlist", + "parameters": [ + { + "type": "string", + "description": "Name of the show", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the playlist", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + } + } + } } }, "definitions": { @@ -950,6 +1458,17 @@ const docTemplate = `{ } } }, + "v1.PlaylistsListing": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/definitions/store.Playlist" + } + } + } + }, "v1.ShowsListing": { "type": "object", "properties": { diff --git a/api/v1/files_upload.go b/api/v1/files_upload.go index ab374a4..832d035 100644 --- a/api/v1/files_upload.go +++ b/api/v1/files_upload.go @@ -37,6 +37,18 @@ import ( //******* simple binary http upload +// UploadFileSimple uploads file content. +// @Summary Upload file content +// @Description Uploads file content. Only available if file was created using SourceURI set to upload://-type and file import state is running. +// @Accept octet-stream +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 200 +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/upload [put] func (api *API) UploadFileSimple(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -364,6 +376,18 @@ func getFlowJSParameterFromQuery(r *http.Request) (id string, chunk, totalChunks return } +// UploadFileFlowJS uploads file content via flow.js. +// @Summary Upload file content +// @Description Uploads file content via flow.js. Only available if file was created using SourceURI set to upload://-type and file import state is running. +// @Accept mpfd +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Success 200 {object} nil +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 409 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/upload [post] func (api *API) UploadFileFlowJS(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -429,6 +453,20 @@ func (api *API) UploadFileFlowJS(c *gin.Context) { c.JSON(http.StatusOK, nil) } +// TestFileFlowJS uploads file content. +// @Summary Upload file content +// @Description Uploads file content. Only available if file was created using SourceURI set to upload://-type and file import state is running. +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the file" +// @Param flowIdentifier query string true "A unique identifier for the uploaded file" +// TODO: document all flow.js params +// @Success 200 {object} nil +// @Success 204 {object} nil +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 409 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/files/{id}/upload [get] func (api *API) TestFileFlowJS(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { diff --git a/api/v1/playlists.go b/api/v1/playlists.go index 8b243bf..738960a 100644 --- a/api/v1/playlists.go +++ b/api/v1/playlists.go @@ -26,6 +26,17 @@ import ( "gitlab.servus.at/autoradio/tank/store" ) +// ListPlaylistsOfShow lists playlists. +// @Summary List playlists +// @Description Lists playlists of show. +// @Produce json +// @Param name path string true "Name of the show" +// @Param limit query int false "Limit number of results" +// @Param offset query int false "Start listing from offset" +// @Success 200 {object} PlaylistsListing +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/playlists [get] func (api *API) ListPlaylistsOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -44,6 +55,18 @@ func (api *API) ListPlaylistsOfShow(c *gin.Context) { c.JSON(http.StatusOK, PlaylistsListing{playlists}) } +// CreatePlaylistForShow creates a new playlist. +// @Summary Create playlist +// @Description Creates a new playlist for the show. +// @Accept json +// @Produce json +// @Param name path string true "Name of the show" +// @Param playlist body store.Playlist true "Playlist data" +// @Success 201 {object} store.Playlist +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/playlists [post] func (api *API) CreatePlaylistForShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -63,6 +86,17 @@ func (api *API) CreatePlaylistForShow(c *gin.Context) { c.JSON(http.StatusCreated, playlist) } +// ReadPlaylistOfShow retrieves a playlist of a show. +// @Summary Retrieve playlist +// @Description Retrieves a playlist of a show. +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the playlist" +// @Success 200 {object} store.Playlist +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/playlists/{id} [get] func (api *API) ReadPlaylistOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -82,6 +116,19 @@ func (api *API) ReadPlaylistOfShow(c *gin.Context) { c.JSON(http.StatusOK, playlist) } +// UpdatePlaylistOfShow updates a playlist of a show. +// @Summary Update playlist +// @Description Updates a playlist of a show. +// @Accept json +// @Produce json +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the playlist" +// @Param playlist body store.Playlist true "Playlist data" +// @Success 200 {object} store.Playlist +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/playlists/{id} [put] func (api *API) UpdatePlaylistOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -105,6 +152,16 @@ func (api *API) UpdatePlaylistOfShow(c *gin.Context) { c.JSON(http.StatusOK, playlist) } +// DeletePlaylistOfShow deletes a playlist of a show. +// @Summary Delete playlist +// @Description Deletes a playlist of a show. +// @Param name path string true "Name of the show" +// @Param id path int true "ID of the playlist" +// @Success 204 {object} nil +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/shows/{name}/playlists/{id} [delete] func (api *API) DeletePlaylistOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequestForShow(c, showID); !authorized { @@ -125,6 +182,16 @@ func (api *API) DeletePlaylistOfShow(c *gin.Context) { // global +// ListPlaylists lists all playlists. +// @Summary List playlists +// @Description Lists all playlists. +// @Produce json +// @Param limit query int false "Limit number of results" +// @Param offset query int false "Start listing from offset" +// @Success 200 {object} PlaylistsListing +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/playlists [get] func (api *API) ListPlaylists(c *gin.Context) { if authorized, _ := authorizeRequestAllShows(c); !authorized { return @@ -142,6 +209,16 @@ func (api *API) ListPlaylists(c *gin.Context) { c.JSON(http.StatusOK, PlaylistsListing{playlists}) } +// ReadPlaylist retrieves a playlist. +// @Summary Retrieve playlist +// @Description Retrieves a playlist. +// @Produce json +// @Param id path int true "ID of the playlist" +// @Success 200 {object} store.Playlist +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /api/v1/playlists/{id} [get] func (api *API) ReadPlaylist(c *gin.Context) { if authorized, _ := authorizeRequestAllShows(c); !authorized { return -- GitLab From fdc817423f2b8799c7b5e50005fc05fbedbbb057 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 14 Mar 2022 13:29:45 +0100 Subject: [PATCH 06/11] feat: Add API doc for auth and other endpoints --- Makefile | 2 +- api/docs/docs.go | 331 +++++++++++++++++++++++++++++++++++++++++++ auth/auth.go | 32 +++++ auth/backend_oidc.go | 22 +++ cmd/tank/web.go | 7 + 5 files changed, 393 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7405651..6cf0b86 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ SWAG := swag ifdef GOPATH SWAG = $(GOPATH)/bin/swag endif -SWAG_ARGS := -d api/v1/ -g api.go +SWAG_ARGS := -d api/v1/,cmd/tank/ -g api.go EXECUTEABLE := tank diff --git a/api/docs/docs.go b/api/docs/docs.go index 5ef5f88..7b61366 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -1172,9 +1172,323 @@ const docTemplate = `{ } } } + }, + "/auth/backends": { + "get": { + "description": "Lists authentication backends.", + "produces": [ + "application/json" + ], + "summary": "List authentication backends", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/auth.AuthBackendInfo" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + } + } + } + }, + "/auth/oidc/callback": { + "get": { + "description": "Completes OIDC login.", + "produces": [ + "application/json" + ], + "summary": "Complete OIDC login", + "parameters": [ + { + "type": "string", + "description": "OIDC state", + "name": "state", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "OIDC code", + "name": "code", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "410": { + "description": "Gone", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + } + } + } + }, + "/auth/oidc/login": { + "get": { + "description": "Creates a session via OIDC. Redirects to identity provider.", + "produces": [ + "application/json" + ], + "summary": "Create OIDC session", + "parameters": [ + { + "type": "string", + "description": "OIDC session ID", + "name": "session-id", + "in": "query", + "required": true + } + ], + "responses": { + "302": { + "description": "" + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + } + } + } + }, + "/auth/session": { + "get": { + "description": "Retrieves session info.", + "produces": [ + "application/json" + ], + "summary": "Retrieve session info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/auth.Session" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "408": { + "description": "Request Timeout", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + } + } + }, + "post": { + "description": "Creates a new session.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Create session", + "parameters": [ + { + "description": "Request data according to backend", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.NewSessionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/auth.NewSessionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + } + } + }, + "delete": { + "description": "Deletes the session.", + "produces": [ + "application/json" + ], + "summary": "Delete session", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + } + } + } + }, + "/healthz": { + "get": { + "description": "Checks daemon health.", + "produces": [ + "application/json" + ], + "summary": "Check health", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.Health" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/main.Health" + } + } + } + } } }, "definitions": { + "auth.AuthBackendInfo": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "state": { + "type": "integer" + } + } + }, + "auth.HTTPErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + }, + "auth.NewSessionRequest": { + "type": "object", + "properties": { + "arguments": { + "type": "array", + "items": { + "type": "integer" + } + }, + "backend": { + "type": "string" + } + } + }, + "auth.NewSessionResponse": { + "type": "object", + "properties": { + "session": { + "$ref": "#/definitions/auth.Session" + }, + "token": { + "type": "string" + } + } + }, + "auth.Session": { + "type": "object", + "properties": { + "all-shows": { + "type": "boolean" + }, + "privileged": { + "type": "boolean" + }, + "public-shows": { + "type": "array", + "items": { + "type": "string" + } + }, + "readonly": { + "type": "boolean" + }, + "shows": { + "type": "array", + "items": { + "type": "string" + } + }, + "username": { + "type": "string" + } + } + }, "importer.Job": { "type": "object", "properties": { @@ -1251,6 +1565,23 @@ const docTemplate = `{ } } }, + "main.Health": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/main.HealthStatus" + }, + "importer": { + "$ref": "#/definitions/main.HealthStatus" + }, + "store": { + "$ref": "#/definitions/main.HealthStatus" + } + } + }, + "main.HealthStatus": { + "type": "object" + }, "store.File": { "type": "object", "properties": { diff --git a/auth/auth.go b/auth/auth.go index b6efcce..321684b 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -87,6 +87,16 @@ func Init(c *Config, infoLog, errLog, dbgLog *log.Logger) (err error) { return } +// newSession creates a new session. +// @Summary Create session +// @Description Creates a new session. +// @Accept json +// @Produce json +// @Param request body NewSessionRequest true "Request data according to backend" +// @Success 200 {object} NewSessionResponse +// @Failure 400 {object} HTTPErrorResponse +// @Failure 409 {object} HTTPErrorResponse +// @Router /auth/session [post] func newSession(c *gin.Context) { request := &NewSessionRequest{} err := json.NewDecoder(c.Request.Body).Decode(request) @@ -128,6 +138,14 @@ func newSession(c *gin.Context) { c.JSON(http.StatusOK, response) } +// getSession retrieves session info. +// @Summary Retrieve session info +// @Description Retrieves session info. +// @Produce json +// @Success 200 {object} Session +// @Failure 404 {object} HTTPErrorResponse +// @Failure 408 {object} HTTPErrorResponse +// @Router /auth/session [get] func getSession(c *gin.Context) { s := getSessionFromBearerToken(c.Request) if s == nil { @@ -161,6 +179,13 @@ func getSession(c *gin.Context) { } } +// deleteSession deletes the session. +// @Summary Delete session +// @Description Deletes the session. +// @Produce json +// @Success 200 {object} string +// @Failure 401 {object} HTTPErrorResponse +// @Router /auth/session [delete] func deleteSession(c *gin.Context) { s := getSessionFromBearerToken(c.Request) if s == nil || s.State() > SessionStateLoggedIn { @@ -175,6 +200,13 @@ func deleteSession(c *gin.Context) { c.JSON(http.StatusOK, "you are now logged out") } +// listBackends lists authentication backends. +// @Summary List authentication backends +// @Description Lists authentication backends. +// @Produce json +// @Success 200 {object} []AuthBackendInfo +// @Failure 404 {object} HTTPErrorResponse +// @Router /auth/backends [get] func listBackends(c *gin.Context) { backends := []AuthBackendInfo{} if auth.backends.oidc != nil { diff --git a/auth/backend_oidc.go b/auth/backend_oidc.go index b99b757..5fc21e1 100644 --- a/auth/backend_oidc.go +++ b/auth/backend_oidc.go @@ -225,6 +225,15 @@ func (b *OIDCBackend) NewSession(ctx context.Context, arguments json.RawMessage) return } +// Login creates a session via OIDC. +// @Summary Create OIDC session +// @Description Creates a session via OIDC. Redirects to identity provider. +// @Produce json +// @Param session-id query string true "OIDC session ID" +// @Success 302 +// @Failure 401 {object} HTTPErrorResponse +// @Failure 409 {object} HTTPErrorResponse +// @Router /auth/oidc/login [get] func (b *OIDCBackend) Login(c *gin.Context) { s := auth.sessions.get(c.Query("session-id")) if s == nil { @@ -244,6 +253,19 @@ func (b *OIDCBackend) Login(c *gin.Context) { c.Redirect(http.StatusFound, b.oauth2Config.AuthCodeURL(s.ID(), oidc.Nonce(s.oidc.nonce))) } +// Callback completes OIDC login. +// @Summary Complete OIDC login +// @Description Completes OIDC login. +// @Produce json +// @Param state query string true "OIDC state" +// @Param code query string true "OIDC code" +// @Success 200 {object} string +// @Failure 400 {object} HTTPErrorResponse +// @Failure 401 {object} HTTPErrorResponse +// @Failure 409 {object} HTTPErrorResponse +// @Failure 410 {object} HTTPErrorResponse +// @Failure 500 {object} HTTPErrorResponse +// @Router /auth/oidc/callback [get] func (b *OIDCBackend) Callback(c *gin.Context) { s := auth.sessions.get(c.Query("state")) if s == nil { diff --git a/cmd/tank/web.go b/cmd/tank/web.go index 512b6cd..e5e91e2 100644 --- a/cmd/tank/web.go +++ b/cmd/tank/web.go @@ -114,6 +114,13 @@ type Health struct { Importer HealthStatus `json:"importer"` } +// healthzHandler checks daemon health. +// @Summary Check health +// @Description Checks daemon health. +// @Produce json +// @Success 200 {object} Health +// @Failure 503 {object} Health +// @Router /healthz [get] func healthzHandler(c *gin.Context, st *store.Store, im *importer.Importer) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // TODO: hardcoded value defer cancel() -- GitLab From 0cb79447da9225f8f5171d0d6a2be76a21dc531e Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 17 Mar 2022 10:56:21 +0100 Subject: [PATCH 07/11] fix: Added missing error codes according to schemathesis --- api/docs/docs.go | 130 ++++++++++++++++++++++++++++++++++++++++- api/v1/files.go | 7 +++ api/v1/files_import.go | 1 + api/v1/files_upload.go | 1 + api/v1/playlists.go | 6 ++ api/v1/shows.go | 5 +- auth/auth.go | 2 + auth/backend_oidc.go | 1 + 8 files changed, 150 insertions(+), 3 deletions(-) diff --git a/api/docs/docs.go b/api/docs/docs.go index 7b61366..007f7f8 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -51,6 +51,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.PlaylistsListing" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "403": { "description": "Forbidden", "schema": { @@ -101,6 +107,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -138,6 +150,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ShowsListing" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -170,8 +188,8 @@ const docTemplate = `{ } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { "$ref": "#/definitions/store.Show" } @@ -182,6 +200,18 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -259,6 +289,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.FilesListing" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "403": { "description": "Forbidden", "schema": { @@ -376,6 +412,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -419,6 +461,18 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -479,6 +533,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -642,6 +702,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -752,6 +818,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -855,6 +927,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -899,6 +977,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.JobsListing" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "403": { "description": "Forbidden", "schema": { @@ -949,6 +1033,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.PlaylistsListing" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "403": { "description": "Forbidden", "schema": { @@ -1060,6 +1150,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -1121,6 +1217,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -1164,6 +1266,12 @@ const docTemplate = `{ "$ref": "#/definitions/v1.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -1282,6 +1390,12 @@ const docTemplate = `{ "302": { "description": "" }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, "401": { "description": "Unauthorized", "schema": { @@ -1311,6 +1425,12 @@ const docTemplate = `{ "$ref": "#/definitions/auth.Session" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, "404": { "description": "Not Found", "schema": { @@ -1379,6 +1499,12 @@ const docTemplate = `{ "type": "string" } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/auth.HTTPErrorResponse" + } + }, "401": { "description": "Unauthorized", "schema": { diff --git a/api/v1/files.go b/api/v1/files.go index c257032..116a994 100644 --- a/api/v1/files.go +++ b/api/v1/files.go @@ -35,6 +35,7 @@ import ( // @Param limit query int false "Limit number of results" // @Param offset query int false "Start listing from offset" // @Success 200 {object} FilesListing +// @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files [get] @@ -164,6 +165,7 @@ create_file_response: // @Success 200 {object} store.File // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files/{id} [get] func (api *API) ReadFileOfShow(c *gin.Context) { @@ -196,6 +198,7 @@ func (api *API) ReadFileOfShow(c *gin.Context) { // @Success 200 {object} store.File // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files/{id} [patch] func (api *API) PatchFileOfShow(c *gin.Context) { @@ -230,6 +233,8 @@ func (api *API) PatchFileOfShow(c *gin.Context) { // @Success 204 {object} nil // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse +// @Failure 409 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files/{id} [delete] func (api *API) DeleteFileOfShow(c *gin.Context) { @@ -262,6 +267,7 @@ func (api *API) DeleteFileOfShow(c *gin.Context) { // @Success 200 {object} FileUsageListing // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files/{id}/usage [get] func (api *API) ReadUsageOfFile(c *gin.Context) { @@ -292,6 +298,7 @@ func (api *API) ReadUsageOfFile(c *gin.Context) { // @Success 200 {object} FileImportLogs // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files/{id}/logs [get] func (api *API) ReadLogsOfFile(c *gin.Context) { diff --git a/api/v1/files_import.go b/api/v1/files_import.go index 63e4bc2..9443639 100644 --- a/api/v1/files_import.go +++ b/api/v1/files_import.go @@ -32,6 +32,7 @@ import ( // @Param limit query int false "Limit number of results" // @Param offset query int false "Start listing from offset" // @Success 200 {object} JobsListing +// @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/imports [get] diff --git a/api/v1/files_upload.go b/api/v1/files_upload.go index 832d035..ca66c06 100644 --- a/api/v1/files_upload.go +++ b/api/v1/files_upload.go @@ -47,6 +47,7 @@ import ( // @Success 200 // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/files/{id}/upload [put] func (api *API) UploadFileSimple(c *gin.Context) { diff --git a/api/v1/playlists.go b/api/v1/playlists.go index 738960a..31577cb 100644 --- a/api/v1/playlists.go +++ b/api/v1/playlists.go @@ -34,6 +34,7 @@ import ( // @Param limit query int false "Limit number of results" // @Param offset query int false "Start listing from offset" // @Success 200 {object} PlaylistsListing +// @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/playlists [get] @@ -95,6 +96,7 @@ func (api *API) CreatePlaylistForShow(c *gin.Context) { // @Success 200 {object} store.Playlist // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/playlists/{id} [get] func (api *API) ReadPlaylistOfShow(c *gin.Context) { @@ -127,6 +129,7 @@ func (api *API) ReadPlaylistOfShow(c *gin.Context) { // @Success 200 {object} store.Playlist // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/playlists/{id} [put] func (api *API) UpdatePlaylistOfShow(c *gin.Context) { @@ -160,6 +163,7 @@ func (api *API) UpdatePlaylistOfShow(c *gin.Context) { // @Success 204 {object} nil // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name}/playlists/{id} [delete] func (api *API) DeletePlaylistOfShow(c *gin.Context) { @@ -189,6 +193,7 @@ func (api *API) DeletePlaylistOfShow(c *gin.Context) { // @Param limit query int false "Limit number of results" // @Param offset query int false "Start listing from offset" // @Success 200 {object} PlaylistsListing +// @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/playlists [get] @@ -217,6 +222,7 @@ func (api *API) ListPlaylists(c *gin.Context) { // @Success 200 {object} store.Playlist // @Failure 400 {object} ErrorResponse // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/playlists/{id} [get] func (api *API) ReadPlaylist(c *gin.Context) { diff --git a/api/v1/shows.go b/api/v1/shows.go index 1fb97d1..def4d6d 100644 --- a/api/v1/shows.go +++ b/api/v1/shows.go @@ -34,6 +34,7 @@ import ( // @Param limit query int false "Limit number of results" // @Param offset query int false "Start listing from offset" // @Success 200 {object} ShowsListing +// @Failure 400 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows [get] func (api *API) ListShows(c *gin.Context) { @@ -88,8 +89,10 @@ func (api *API) ListShows(c *gin.Context) { // @Produce json // @Param name path string true "Name of the show to be created" // @Param clone-from query string false "If given, all files and playlists will be copied from the show" -// @Success 200 {object} store.Show +// @Success 201 {object} store.Show // @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse +// @Failure 409 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /api/v1/shows/{name} [post] func (api *API) CreateShow(c *gin.Context) { diff --git a/auth/auth.go b/auth/auth.go index 321684b..7c1fc14 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -143,6 +143,7 @@ func newSession(c *gin.Context) { // @Description Retrieves session info. // @Produce json // @Success 200 {object} Session +// @Failure 400 {object} HTTPErrorResponse // @Failure 404 {object} HTTPErrorResponse // @Failure 408 {object} HTTPErrorResponse // @Router /auth/session [get] @@ -184,6 +185,7 @@ func getSession(c *gin.Context) { // @Description Deletes the session. // @Produce json // @Success 200 {object} string +// @Failure 400 {object} HTTPErrorResponse // @Failure 401 {object} HTTPErrorResponse // @Router /auth/session [delete] func deleteSession(c *gin.Context) { diff --git a/auth/backend_oidc.go b/auth/backend_oidc.go index 5fc21e1..ec8cc2a 100644 --- a/auth/backend_oidc.go +++ b/auth/backend_oidc.go @@ -231,6 +231,7 @@ func (b *OIDCBackend) NewSession(ctx context.Context, arguments json.RawMessage) // @Produce json // @Param session-id query string true "OIDC session ID" // @Success 302 +// @Failure 400 {object} HTTPErrorResponse // @Failure 401 {object} HTTPErrorResponse // @Failure 409 {object} HTTPErrorResponse // @Router /auth/oidc/login [get] -- GitLab From 93ab314afd099f6685278da8b82d7b71d6da8b5f Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 21 Mar 2022 14:39:31 +0100 Subject: [PATCH 08/11] fix: Fix return type doc for health endpoint --- api/docs/docs.go | 15 ++++++--------- cmd/tank/web.go | 11 +++++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/api/docs/docs.go b/api/docs/docs.go index 007f7f8..eb11aaa 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -1525,13 +1525,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/main.Health" + "$ref": "#/definitions/main.Health2" } }, "503": { "description": "Service Unavailable", "schema": { - "$ref": "#/definitions/main.Health" + "$ref": "#/definitions/main.Health2" } } } @@ -1691,23 +1691,20 @@ const docTemplate = `{ } } }, - "main.Health": { + "main.Health2": { "type": "object", "properties": { "auth": { - "$ref": "#/definitions/main.HealthStatus" + "type": "string" }, "importer": { - "$ref": "#/definitions/main.HealthStatus" + "type": "string" }, "store": { - "$ref": "#/definitions/main.HealthStatus" + "type": "string" } } }, - "main.HealthStatus": { - "type": "object" - }, "store.File": { "type": "object", "properties": { diff --git a/cmd/tank/web.go b/cmd/tank/web.go index e5e91e2..d989e2d 100644 --- a/cmd/tank/web.go +++ b/cmd/tank/web.go @@ -114,12 +114,19 @@ type Health struct { Importer HealthStatus `json:"importer"` } +// HealthDoc is used in doc and prevents errors when checking response schema conformance with schemathesis +type HealthDoc struct { + Auth string `json:"auth"` + Store string `json:"store"` + Importer string `json:"importer"` +} + // healthzHandler checks daemon health. // @Summary Check health // @Description Checks daemon health. // @Produce json -// @Success 200 {object} Health -// @Failure 503 {object} Health +// @Success 200 {object} HealthDoc +// @Failure 503 {object} HealthDoc // @Router /healthz [get] func healthzHandler(c *gin.Context, st *store.Store, im *importer.Importer) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // TODO: hardcoded value -- GitLab From 92ac5b8a42f47e3b6a87c85ba06823c06c616b62 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 22 Mar 2022 15:37:43 +0100 Subject: [PATCH 09/11] feat(gitlab-ci): Add api doc build jobs --- .gitlab-ci.yml | 23 +++++++++++++++++++++++ Makefile | 3 +++ go.mod | 6 ++++++ go.sum | 24 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e66fbb..f6b84c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -59,6 +59,7 @@ test-store-postgres: build: stage: build script: + # we should actually use `make build` here, see #30 - go build -ldflags "-extldflags '-static'" -tags netgo -o $CI_PROJECT_DIR/tank ./cmd/tank ## sqlite needs cgo... :( ##- go build -o $CI_PROJECT_DIR/tank ./cmd/tank @@ -68,6 +69,28 @@ build: paths: - tank +build-openapi-2-scheme: + stage: build + script: + - go get -u github.com/swaggo/swag/cmd/swag + - make api-docs + artifacts: + paths: + - api/docs/swagger.yaml + +build-openapi-3-scheme: + stage: build + image: + name: openapitools/openapi-generator-cli:latest-release + needs: + - job: build-openapi-2-scheme + artifacts: true + script: + - /usr/local/bin/docker-entrypoint.sh generate -i api/docs/swagger.yaml -o api/docs -g openapi-yaml --minimal-update + artifacts: + paths: + - api/docs/openapi/openapi.yaml + docker: stage: build image: diff --git a/Makefile b/Makefile index 6cf0b86..935c74f 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,9 @@ fmt-api-docs: api-docs: $(SWAG) init $(SWAG_ARGS) --pd -o api/docs +# build target actually depends on api-docs +# to allow building binary without generating api docs first, we put api/docs/docs.go under version control +# see #30 build: ui $(GOCMD) build -o $(EXECUTEABLE) ./cmd/tank diff --git a/go.mod b/go.mod index c50f10c..f9c7dd1 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,9 @@ require ( github.com/coreos/go-oidc v2.0.0+incompatible github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/gin-gonic/gin v1.7.7 + github.com/go-openapi/swag v0.21.1 // indirect github.com/jinzhu/gorm v1.9.8 + github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect @@ -16,7 +18,11 @@ require ( github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect github.com/swaggo/gin-swagger v1.4.1 github.com/swaggo/swag v1.8.0 + github.com/urfave/cli/v2 v2.4.0 // indirect + golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 + golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect + golang.org/x/tools v0.1.10 // indirect gopkg.in/gormigrate.v1 v1.5.0 gopkg.in/square/go-jose.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 diff --git a/go.sum b/go.sum index 2360d8b..3a74fdb 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5t github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -62,6 +64,8 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -136,6 +140,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= @@ -180,6 +186,8 @@ github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= @@ -210,7 +218,10 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I= +github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -222,12 +233,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -239,11 +253,15 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 h1:jIOcLT9BZzyJ9ce+IwwZ+aF9yeCqzrR+NrD68a/SHKw= @@ -268,13 +286,17 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -288,6 +310,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -- GitLab From fed90a8c9a5dd235068b1cb414c0bbdb3c55d060 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 23 Mar 2022 12:14:23 +0100 Subject: [PATCH 10/11] fix: Use swaggertype for custom schema types --- api/docs/docs.go | 6 +++--- cmd/tank/web.go | 17 +++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/api/docs/docs.go b/api/docs/docs.go index eb11aaa..c3dd4fc 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -1525,13 +1525,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/main.Health2" + "$ref": "#/definitions/main.Health" } }, "503": { "description": "Service Unavailable", "schema": { - "$ref": "#/definitions/main.Health2" + "$ref": "#/definitions/main.Health" } } } @@ -1691,7 +1691,7 @@ const docTemplate = `{ } } }, - "main.Health2": { + "main.Health": { "type": "object", "properties": { "auth": { diff --git a/cmd/tank/web.go b/cmd/tank/web.go index d989e2d..fa6a7ad 100644 --- a/cmd/tank/web.go +++ b/cmd/tank/web.go @@ -109,24 +109,17 @@ func (s HealthStatus) MarshalText() (data []byte, err error) { } type Health struct { - Auth HealthStatus `json:"auth"` - Store HealthStatus `json:"store"` - Importer HealthStatus `json:"importer"` -} - -// HealthDoc is used in doc and prevents errors when checking response schema conformance with schemathesis -type HealthDoc struct { - Auth string `json:"auth"` - Store string `json:"store"` - Importer string `json:"importer"` + Auth HealthStatus `json:"auth" swaggertype:"string"` + Store HealthStatus `json:"store" swaggertype:"string"` + Importer HealthStatus `json:"importer" swaggertype:"string"` } // healthzHandler checks daemon health. // @Summary Check health // @Description Checks daemon health. // @Produce json -// @Success 200 {object} HealthDoc -// @Failure 503 {object} HealthDoc +// @Success 200 {object} Health +// @Failure 503 {object} Health // @Router /healthz [get] func healthzHandler(c *gin.Context, st *store.Store, im *importer.Importer) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // TODO: hardcoded value -- GitLab From 752135a805b14d7d20ab0fdae6e9c86ee046eb1e Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 24 Mar 2022 14:30:14 +0100 Subject: [PATCH 11/11] fix: Specify enum value for API scheme --- api/docs/docs.go | 10 +++++++++- auth/utils.go | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/api/docs/docs.go b/api/docs/docs.go index c3dd4fc..b774597 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -1549,7 +1549,15 @@ const docTemplate = `{ "type": "string" }, "state": { - "type": "integer" + "type": "string", + "enum": [ + "new", + "initializing", + "ready", + "failed", + "destroyed", + "unknown" + ] } } }, diff --git a/auth/utils.go b/auth/utils.go index 36f4d28..5319426 100644 --- a/auth/utils.go +++ b/auth/utils.go @@ -84,7 +84,7 @@ func (s *BackendState) MarshalText() (data []byte, err error) { type AuthBackendInfo struct { Name string Description string - State *BackendState + State *BackendState `swaggertype:"string" enums:"new,initializing,ready,failed,destroyed,unknown"` } type NewSessionRequest struct { -- GitLab