From 04b28b53957e4f25f53201c6e4504d1903ee6227 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Wed, 12 Feb 2025 16:31:31 +0100 Subject: [PATCH 01/13] Add Swagger Config Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 2 +- workspaces/backend/docs/docs.go | 110 +++++++++++++++++++++++++++ workspaces/backend/docs/swagger.json | 86 +++++++++++++++++++++ workspaces/backend/docs/swagger.yaml | 59 ++++++++++++++ workspaces/backend/go.mod | 2 + workspaces/backend/go.sum | 4 + 6 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 workspaces/backend/docs/docs.go create mode 100644 workspaces/backend/docs/swagger.json create mode 100644 workspaces/backend/docs/swagger.yaml diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index ef3fe7db..9af8abd7 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -111,4 +111,4 @@ func (a *App) Routes() http.Handler { router.GET(SwaggerPath, a.GetSwaggerHandler) return a.recoverPanic(a.enableCORS(router)) -} +} \ No newline at end of file diff --git a/workspaces/backend/docs/docs.go b/workspaces/backend/docs/docs.go new file mode 100644 index 00000000..097d063a --- /dev/null +++ b/workspaces/backend/docs/docs.go @@ -0,0 +1,110 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "license": { + "name": "License: Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/healthcheck": { + "get": { + "description": "Provides a healthcheck response indicating the status of key services.", + "produces": [ + "application/json" + ], + "tags": [ + "healthcheck" + ], + "summary": "Returns the health status of the application", + "responses": { + "200": { + "description": "Successful healthcheck response", + "schema": { + "$ref": "#/definitions/health_check.HealthCheck" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "api.ErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "health_check.HealthCheck": { + "type": "object", + "properties": { + "status": { + "$ref": "#/definitions/health_check.ServiceStatus" + }, + "system_info": { + "$ref": "#/definitions/health_check.SystemInfo" + } + } + }, + "health_check.ServiceStatus": { + "type": "string", + "enum": [ + "Healthy", + "Unhealthy" + ], + "x-enum-varnames": [ + "ServiceStatusHealthy", + "ServiceStatusUnhealthy" + ] + }, + "health_check.SystemInfo": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0.0", + Host: "localhost:4000", + BasePath: "/api/v1", + Schemes: []string{}, + Title: "Kubeflow Notebooks API", + Description: "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/workspaces/backend/docs/swagger.json b/workspaces/backend/docs/swagger.json new file mode 100644 index 00000000..cd0be795 --- /dev/null +++ b/workspaces/backend/docs/swagger.json @@ -0,0 +1,86 @@ +{ + "swagger": "2.0", + "info": { + "description": "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", + "title": "Kubeflow Notebooks API", + "contact": {}, + "license": { + "name": "License: Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.0" + }, + "host": "localhost:4000", + "basePath": "/api/v1", + "paths": { + "/healthcheck": { + "get": { + "description": "Provides a healthcheck response indicating the status of key services.", + "produces": [ + "application/json" + ], + "tags": [ + "healthcheck" + ], + "summary": "Returns the health status of the application", + "responses": { + "200": { + "description": "Successful healthcheck response", + "schema": { + "$ref": "#/definitions/health_check.HealthCheck" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "api.ErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "health_check.HealthCheck": { + "type": "object", + "properties": { + "status": { + "$ref": "#/definitions/health_check.ServiceStatus" + }, + "system_info": { + "$ref": "#/definitions/health_check.SystemInfo" + } + } + }, + "health_check.ServiceStatus": { + "type": "string", + "enum": [ + "Healthy", + "Unhealthy" + ], + "x-enum-varnames": [ + "ServiceStatusHealthy", + "ServiceStatusUnhealthy" + ] + }, + "health_check.SystemInfo": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/workspaces/backend/docs/swagger.yaml b/workspaces/backend/docs/swagger.yaml new file mode 100644 index 00000000..5496c533 --- /dev/null +++ b/workspaces/backend/docs/swagger.yaml @@ -0,0 +1,59 @@ +basePath: /api/v1 +definitions: + api.ErrorResponse: + properties: + code: + type: string + message: + type: string + type: object + health_check.HealthCheck: + properties: + status: + $ref: '#/definitions/health_check.ServiceStatus' + system_info: + $ref: '#/definitions/health_check.SystemInfo' + type: object + health_check.ServiceStatus: + enum: + - Healthy + - Unhealthy + type: string + x-enum-varnames: + - ServiceStatusHealthy + - ServiceStatusUnhealthy + health_check.SystemInfo: + properties: + version: + type: string + type: object +host: localhost:4000 +info: + contact: {} + description: |- + This API provides endpoints to manage notebooks in a Kubernetes cluster. + For more information, visit https://www.kubeflow.org/docs/components/notebooks/ + license: + name: 'License: Apache 2.0' + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: Kubeflow Notebooks API + version: 1.0.0 +paths: + /healthcheck: + get: + description: Provides a healthcheck response indicating the status of key services. + produces: + - application/json + responses: + "200": + description: Successful healthcheck response + schema: + $ref: '#/definitions/health_check.HealthCheck' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api.ErrorResponse' + summary: Returns the health status of the application + tags: + - healthcheck +swagger: "2.0" diff --git a/workspaces/backend/go.mod b/workspaces/backend/go.mod index ab546a80..0b1154c6 100644 --- a/workspaces/backend/go.mod +++ b/workspaces/backend/go.mod @@ -23,6 +23,7 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect + github.com/KyleBanks/depth v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -66,6 +67,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/swaggo/files/v2 v2.0.2 // indirect github.com/x448/float16 v0.8.4 // indirect diff --git a/workspaces/backend/go.sum b/workspaces/backend/go.sum index 4e67123c..c50a9a44 100644 --- a/workspaces/backend/go.sum +++ b/workspaces/backend/go.sum @@ -93,6 +93,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= @@ -121,6 +122,7 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -212,6 +214,8 @@ google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjr google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= From 2933087a3ddc106b1f367364998ad025f95344e9 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Wed, 12 Feb 2025 18:57:41 +0100 Subject: [PATCH 02/13] add swag command Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 2 ++ workspaces/backend/go.mod | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index 9af8abd7..357fb098 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -26,6 +26,8 @@ import ( "k8s.io/apiserver/pkg/authorization/authorizer" "sigs.k8s.io/controller-runtime/pkg/client" + _ "github.com/kubeflow/notebooks/workspaces/backend/docs" + "github.com/kubeflow/notebooks/workspaces/backend/internal/config" "github.com/kubeflow/notebooks/workspaces/backend/internal/repositories" _ "github.com/kubeflow/notebooks/workspaces/backend/openapi" diff --git a/workspaces/backend/go.mod b/workspaces/backend/go.mod index 0b1154c6..1417d1b2 100644 --- a/workspaces/backend/go.mod +++ b/workspaces/backend/go.mod @@ -23,7 +23,6 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect - github.com/KyleBanks/depth v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -67,6 +66,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/swaggo/files/v2 v2.0.2 // indirect From 852d1ea1393f889c6e78a2900d8ea524609ac394 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Thu, 13 Feb 2025 10:43:50 +0100 Subject: [PATCH 03/13] Updated swagger output Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 2 +- workspaces/backend/{docs => api/v2/swagger}/docs.go | 4 ++-- workspaces/backend/{docs => api/v2/swagger}/swagger.json | 0 workspaces/backend/{docs => api/v2/swagger}/swagger.yaml | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename workspaces/backend/{docs => api/v2/swagger}/docs.go (97%) rename workspaces/backend/{docs => api/v2/swagger}/swagger.json (100%) rename workspaces/backend/{docs => api/v2/swagger}/swagger.yaml (100%) diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index 357fb098..ab3cf589 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -26,7 +26,7 @@ import ( "k8s.io/apiserver/pkg/authorization/authorizer" "sigs.k8s.io/controller-runtime/pkg/client" - _ "github.com/kubeflow/notebooks/workspaces/backend/docs" + _ "github.com/kubeflow/notebooks/workspaces/backend/api/v2/swagger" "github.com/kubeflow/notebooks/workspaces/backend/internal/config" "github.com/kubeflow/notebooks/workspaces/backend/internal/repositories" diff --git a/workspaces/backend/docs/docs.go b/workspaces/backend/api/v2/swagger/docs.go similarity index 97% rename from workspaces/backend/docs/docs.go rename to workspaces/backend/api/v2/swagger/docs.go index 097d063a..56871316 100644 --- a/workspaces/backend/docs/docs.go +++ b/workspaces/backend/api/v2/swagger/docs.go @@ -1,5 +1,5 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT -package docs +// Package swagger Code generated by swaggo/swag. DO NOT EDIT +package swagger import "github.com/swaggo/swag" diff --git a/workspaces/backend/docs/swagger.json b/workspaces/backend/api/v2/swagger/swagger.json similarity index 100% rename from workspaces/backend/docs/swagger.json rename to workspaces/backend/api/v2/swagger/swagger.json diff --git a/workspaces/backend/docs/swagger.yaml b/workspaces/backend/api/v2/swagger/swagger.yaml similarity index 100% rename from workspaces/backend/docs/swagger.yaml rename to workspaces/backend/api/v2/swagger/swagger.yaml From 1a27205bfd20963d4d722b4c7bfb701717b14824 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Thu, 13 Feb 2025 11:02:48 +0100 Subject: [PATCH 04/13] Updated general annotations Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/v2/swagger/docs.go | 4 ++-- workspaces/backend/api/v2/swagger/swagger.json | 6 +++++- workspaces/backend/api/v2/swagger/swagger.yaml | 5 ++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/workspaces/backend/api/v2/swagger/docs.go b/workspaces/backend/api/v2/swagger/docs.go index 56871316..b54e5e0c 100644 --- a/workspaces/backend/api/v2/swagger/docs.go +++ b/workspaces/backend/api/v2/swagger/docs.go @@ -96,9 +96,9 @@ var SwaggerInfo = &swag.Spec{ Version: "1.0.0", Host: "localhost:4000", BasePath: "/api/v1", - Schemes: []string{}, + Schemes: []string{"http", "https"}, Title: "Kubeflow Notebooks API", - Description: "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", + Description: "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/workspaces/backend/api/v2/swagger/swagger.json b/workspaces/backend/api/v2/swagger/swagger.json index cd0be795..7c11e0f9 100644 --- a/workspaces/backend/api/v2/swagger/swagger.json +++ b/workspaces/backend/api/v2/swagger/swagger.json @@ -1,7 +1,11 @@ { + "schemes": [ + "http", + "https" + ], "swagger": "2.0", "info": { - "description": "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", + "description": "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", "title": "Kubeflow Notebooks API", "contact": {}, "license": { diff --git a/workspaces/backend/api/v2/swagger/swagger.yaml b/workspaces/backend/api/v2/swagger/swagger.yaml index 5496c533..c97093f1 100644 --- a/workspaces/backend/api/v2/swagger/swagger.yaml +++ b/workspaces/backend/api/v2/swagger/swagger.yaml @@ -31,7 +31,7 @@ host: localhost:4000 info: contact: {} description: |- - This API provides endpoints to manage notebooks in a Kubernetes cluster. + This API provides endpoints to manage notebooks in a Kubernetes cluster. For more information, visit https://www.kubeflow.org/docs/components/notebooks/ license: name: 'License: Apache 2.0' @@ -56,4 +56,7 @@ paths: summary: Returns the health status of the application tags: - healthcheck +schemes: +- http +- https swagger: "2.0" From 31e1ff635f18447daf3ed898f0babb748a165e89 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Thu, 13 Feb 2025 11:57:30 +0100 Subject: [PATCH 05/13] Updated swagger docs version Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 5 +++-- workspaces/backend/api/{v2 => v1}/swagger/docs.go | 0 workspaces/backend/api/{v2 => v1}/swagger/swagger.json | 0 workspaces/backend/api/{v2 => v1}/swagger/swagger.yaml | 0 4 files changed, 3 insertions(+), 2 deletions(-) rename workspaces/backend/api/{v2 => v1}/swagger/docs.go (100%) rename workspaces/backend/api/{v2 => v1}/swagger/swagger.json (100%) rename workspaces/backend/api/{v2 => v1}/swagger/swagger.yaml (100%) diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index ab3cf589..94ddb627 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -17,8 +17,10 @@ limitations under the License. package api import ( + "fmt" "log/slog" "net/http" + "strings" "github.com/julienschmidt/httprouter" "k8s.io/apimachinery/pkg/runtime" @@ -26,8 +28,7 @@ import ( "k8s.io/apiserver/pkg/authorization/authorizer" "sigs.k8s.io/controller-runtime/pkg/client" - _ "github.com/kubeflow/notebooks/workspaces/backend/api/v2/swagger" - + _ "github.com/kubeflow/notebooks/workspaces/backend/api/v1/swagger" "github.com/kubeflow/notebooks/workspaces/backend/internal/config" "github.com/kubeflow/notebooks/workspaces/backend/internal/repositories" _ "github.com/kubeflow/notebooks/workspaces/backend/openapi" diff --git a/workspaces/backend/api/v2/swagger/docs.go b/workspaces/backend/api/v1/swagger/docs.go similarity index 100% rename from workspaces/backend/api/v2/swagger/docs.go rename to workspaces/backend/api/v1/swagger/docs.go diff --git a/workspaces/backend/api/v2/swagger/swagger.json b/workspaces/backend/api/v1/swagger/swagger.json similarity index 100% rename from workspaces/backend/api/v2/swagger/swagger.json rename to workspaces/backend/api/v1/swagger/swagger.json diff --git a/workspaces/backend/api/v2/swagger/swagger.yaml b/workspaces/backend/api/v1/swagger/swagger.yaml similarity index 100% rename from workspaces/backend/api/v2/swagger/swagger.yaml rename to workspaces/backend/api/v1/swagger/swagger.yaml From aedf8022dcdc27b6e55c563fca9269b246bd09d3 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Wed, 19 Feb 2025 15:06:05 +0100 Subject: [PATCH 06/13] updated swagger config Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 1 - workspaces/backend/api/v1/swagger/docs.go | 110 ------------------ .../backend/api/v1/swagger/swagger.json | 90 -------------- .../backend/api/v1/swagger/swagger.yaml | 62 ---------- 4 files changed, 263 deletions(-) delete mode 100644 workspaces/backend/api/v1/swagger/docs.go delete mode 100644 workspaces/backend/api/v1/swagger/swagger.json delete mode 100644 workspaces/backend/api/v1/swagger/swagger.yaml diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index 94ddb627..d5998232 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -28,7 +28,6 @@ import ( "k8s.io/apiserver/pkg/authorization/authorizer" "sigs.k8s.io/controller-runtime/pkg/client" - _ "github.com/kubeflow/notebooks/workspaces/backend/api/v1/swagger" "github.com/kubeflow/notebooks/workspaces/backend/internal/config" "github.com/kubeflow/notebooks/workspaces/backend/internal/repositories" _ "github.com/kubeflow/notebooks/workspaces/backend/openapi" diff --git a/workspaces/backend/api/v1/swagger/docs.go b/workspaces/backend/api/v1/swagger/docs.go deleted file mode 100644 index b54e5e0c..00000000 --- a/workspaces/backend/api/v1/swagger/docs.go +++ /dev/null @@ -1,110 +0,0 @@ -// Package swagger Code generated by swaggo/swag. DO NOT EDIT -package swagger - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "contact": {}, - "license": { - "name": "License: Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - }, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": { - "/healthcheck": { - "get": { - "description": "Provides a healthcheck response indicating the status of key services.", - "produces": [ - "application/json" - ], - "tags": [ - "healthcheck" - ], - "summary": "Returns the health status of the application", - "responses": { - "200": { - "description": "Successful healthcheck response", - "schema": { - "$ref": "#/definitions/health_check.HealthCheck" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api.ErrorResponse" - } - } - } - } - } - }, - "definitions": { - "api.ErrorResponse": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, - "health_check.HealthCheck": { - "type": "object", - "properties": { - "status": { - "$ref": "#/definitions/health_check.ServiceStatus" - }, - "system_info": { - "$ref": "#/definitions/health_check.SystemInfo" - } - } - }, - "health_check.ServiceStatus": { - "type": "string", - "enum": [ - "Healthy", - "Unhealthy" - ], - "x-enum-varnames": [ - "ServiceStatusHealthy", - "ServiceStatusUnhealthy" - ] - }, - "health_check.SystemInfo": { - "type": "object", - "properties": { - "version": { - "type": "string" - } - } - } - } -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "1.0.0", - Host: "localhost:4000", - BasePath: "/api/v1", - Schemes: []string{"http", "https"}, - Title: "Kubeflow Notebooks API", - Description: "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/workspaces/backend/api/v1/swagger/swagger.json b/workspaces/backend/api/v1/swagger/swagger.json deleted file mode 100644 index 7c11e0f9..00000000 --- a/workspaces/backend/api/v1/swagger/swagger.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "schemes": [ - "http", - "https" - ], - "swagger": "2.0", - "info": { - "description": "This API provides endpoints to manage notebooks in a Kubernetes cluster.\nFor more information, visit https://www.kubeflow.org/docs/components/notebooks/", - "title": "Kubeflow Notebooks API", - "contact": {}, - "license": { - "name": "License: Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - }, - "version": "1.0.0" - }, - "host": "localhost:4000", - "basePath": "/api/v1", - "paths": { - "/healthcheck": { - "get": { - "description": "Provides a healthcheck response indicating the status of key services.", - "produces": [ - "application/json" - ], - "tags": [ - "healthcheck" - ], - "summary": "Returns the health status of the application", - "responses": { - "200": { - "description": "Successful healthcheck response", - "schema": { - "$ref": "#/definitions/health_check.HealthCheck" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api.ErrorResponse" - } - } - } - } - } - }, - "definitions": { - "api.ErrorResponse": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, - "health_check.HealthCheck": { - "type": "object", - "properties": { - "status": { - "$ref": "#/definitions/health_check.ServiceStatus" - }, - "system_info": { - "$ref": "#/definitions/health_check.SystemInfo" - } - } - }, - "health_check.ServiceStatus": { - "type": "string", - "enum": [ - "Healthy", - "Unhealthy" - ], - "x-enum-varnames": [ - "ServiceStatusHealthy", - "ServiceStatusUnhealthy" - ] - }, - "health_check.SystemInfo": { - "type": "object", - "properties": { - "version": { - "type": "string" - } - } - } - } -} \ No newline at end of file diff --git a/workspaces/backend/api/v1/swagger/swagger.yaml b/workspaces/backend/api/v1/swagger/swagger.yaml deleted file mode 100644 index c97093f1..00000000 --- a/workspaces/backend/api/v1/swagger/swagger.yaml +++ /dev/null @@ -1,62 +0,0 @@ -basePath: /api/v1 -definitions: - api.ErrorResponse: - properties: - code: - type: string - message: - type: string - type: object - health_check.HealthCheck: - properties: - status: - $ref: '#/definitions/health_check.ServiceStatus' - system_info: - $ref: '#/definitions/health_check.SystemInfo' - type: object - health_check.ServiceStatus: - enum: - - Healthy - - Unhealthy - type: string - x-enum-varnames: - - ServiceStatusHealthy - - ServiceStatusUnhealthy - health_check.SystemInfo: - properties: - version: - type: string - type: object -host: localhost:4000 -info: - contact: {} - description: |- - This API provides endpoints to manage notebooks in a Kubernetes cluster. - For more information, visit https://www.kubeflow.org/docs/components/notebooks/ - license: - name: 'License: Apache 2.0' - url: http://www.apache.org/licenses/LICENSE-2.0.html - title: Kubeflow Notebooks API - version: 1.0.0 -paths: - /healthcheck: - get: - description: Provides a healthcheck response indicating the status of key services. - produces: - - application/json - responses: - "200": - description: Successful healthcheck response - schema: - $ref: '#/definitions/health_check.HealthCheck' - "500": - description: Internal server error - schema: - $ref: '#/definitions/api.ErrorResponse' - summary: Returns the health status of the application - tags: - - healthcheck -schemes: -- http -- https -swagger: "2.0" From 06ee6ea104c067bfa81c9ae9d876fa64d8ce0b69 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 17 Mar 2025 14:30:52 +0100 Subject: [PATCH 07/13] fix confilicts Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 2 -- workspaces/backend/go.mod | 2 -- workspaces/backend/go.sum | 4 ---- 3 files changed, 8 deletions(-) diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index d5998232..9af8abd7 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -17,10 +17,8 @@ limitations under the License. package api import ( - "fmt" "log/slog" "net/http" - "strings" "github.com/julienschmidt/httprouter" "k8s.io/apimachinery/pkg/runtime" diff --git a/workspaces/backend/go.mod b/workspaces/backend/go.mod index 1417d1b2..ab546a80 100644 --- a/workspaces/backend/go.mod +++ b/workspaces/backend/go.mod @@ -67,8 +67,6 @@ require ( github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect - github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect - github.com/stoewer/go-strcase v1.2.0 // indirect github.com/swaggo/files/v2 v2.0.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect diff --git a/workspaces/backend/go.sum b/workspaces/backend/go.sum index c50a9a44..4e67123c 100644 --- a/workspaces/backend/go.sum +++ b/workspaces/backend/go.sum @@ -93,7 +93,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= @@ -122,7 +121,6 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -214,8 +212,6 @@ google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjr google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= From 285183502e18f0ee9b51eabe1e809018a2396f10 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 17 Mar 2025 14:44:28 +0100 Subject: [PATCH 08/13] Add GetNamespaces Swagger Docs Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/app.go | 2 +- workspaces/backend/api/namespaces_handler.go | 11 ++++ workspaces/backend/openapi/docs.go | 58 ++++++++++++++++++++ workspaces/backend/openapi/swagger.json | 58 ++++++++++++++++++++ workspaces/backend/openapi/swagger.yaml | 38 +++++++++++++ 5 files changed, 166 insertions(+), 1 deletion(-) diff --git a/workspaces/backend/api/app.go b/workspaces/backend/api/app.go index 9af8abd7..ef3fe7db 100644 --- a/workspaces/backend/api/app.go +++ b/workspaces/backend/api/app.go @@ -111,4 +111,4 @@ func (a *App) Routes() http.Handler { router.GET(SwaggerPath, a.GetSwaggerHandler) return a.recoverPanic(a.enableCORS(router)) -} \ No newline at end of file +} diff --git a/workspaces/backend/api/namespaces_handler.go b/workspaces/backend/api/namespaces_handler.go index fb62d7d7..9405c2e0 100644 --- a/workspaces/backend/api/namespaces_handler.go +++ b/workspaces/backend/api/namespaces_handler.go @@ -28,6 +28,17 @@ import ( type NamespaceListEnvelope Envelope[[]models.Namespace] +// GetNamespacesHandler returns a list of all namespaces. +// +// @Summary Returns a list of all namespaces +// @Description Provides a list of all namespaces that the user has access to +// @Tags namespaces +// @Produce application/json +// @Success 200 {object} NamespaceListEnvelope "Successful namespaces response" +// @Failure 401 {object} ErrorEnvelope "Unauthorized" +// @Failure 403 {object} ErrorEnvelope "Forbidden" +// @Failure 500 {object} ErrorEnvelope "Internal server error" +// @Router /namespaces [get] func (a *App) GetNamespacesHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { // =========================== AUTH =========================== diff --git a/workspaces/backend/openapi/docs.go b/workspaces/backend/openapi/docs.go index c16957b2..3df863d0 100644 --- a/workspaces/backend/openapi/docs.go +++ b/workspaces/backend/openapi/docs.go @@ -44,6 +44,44 @@ const docTemplate = `{ } } } + }, + "/namespaces": { + "get": { + "description": "Provides a list of all namespaces that the user has access to", + "produces": [ + "application/json" + ], + "tags": [ + "namespaces" + ], + "summary": "Returns a list of all namespaces", + "responses": { + "200": { + "description": "Successful namespaces response", + "schema": { + "$ref": "#/definitions/api.NamespaceListEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } } }, "definitions": { @@ -80,6 +118,18 @@ const docTemplate = `{ } } }, + "api.NamespaceListEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "type": "array", + "items": { + "$ref": "#/definitions/namespaces.Namespace" + } + } + } + }, "api.ValidationError": { "type": "object", "properties": { @@ -150,6 +200,14 @@ const docTemplate = `{ "type": "string" } } + }, + "namespaces.Namespace": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } } } }` diff --git a/workspaces/backend/openapi/swagger.json b/workspaces/backend/openapi/swagger.json index 50644d14..4f1dd3cf 100644 --- a/workspaces/backend/openapi/swagger.json +++ b/workspaces/backend/openapi/swagger.json @@ -42,6 +42,44 @@ } } } + }, + "/namespaces": { + "get": { + "description": "Provides a list of all namespaces that the user has access to", + "produces": [ + "application/json" + ], + "tags": [ + "namespaces" + ], + "summary": "Returns a list of all namespaces", + "responses": { + "200": { + "description": "Successful namespaces response", + "schema": { + "$ref": "#/definitions/api.NamespaceListEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } } }, "definitions": { @@ -78,6 +116,18 @@ } } }, + "api.NamespaceListEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "type": "array", + "items": { + "$ref": "#/definitions/namespaces.Namespace" + } + } + } + }, "api.ValidationError": { "type": "object", "properties": { @@ -148,6 +198,14 @@ "type": "string" } } + }, + "namespaces.Namespace": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } } } } \ No newline at end of file diff --git a/workspaces/backend/openapi/swagger.yaml b/workspaces/backend/openapi/swagger.yaml index 89027d92..f41362e1 100644 --- a/workspaces/backend/openapi/swagger.yaml +++ b/workspaces/backend/openapi/swagger.yaml @@ -21,6 +21,14 @@ definitions: message: type: string type: object + api.NamespaceListEnvelope: + properties: + data: + description: 'TODO: make all declarations of Envelope use pointers for D' + items: + $ref: '#/definitions/namespaces.Namespace' + type: array + type: object api.ValidationError: properties: field: @@ -74,6 +82,11 @@ definitions: version: type: string type: object + namespaces.Namespace: + properties: + name: + type: string + type: object host: localhost:4000 info: contact: {} @@ -103,6 +116,31 @@ paths: summary: Returns the health status of the application tags: - healthcheck + /namespaces: + get: + description: Provides a list of all namespaces that the user has access to + produces: + - application/json + responses: + "200": + description: Successful namespaces response + schema: + $ref: '#/definitions/api.NamespaceListEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api.ErrorEnvelope' + summary: Returns a list of all namespaces + tags: + - namespaces schemes: - http - https From c687937dc7bb7f31e6a62787e79b166e45ebbd59 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 17 Mar 2025 14:45:51 +0100 Subject: [PATCH 09/13] Add Workspaces Swagger Docs Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/workspaces_handler.go | 71 +++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/workspaces/backend/api/workspaces_handler.go b/workspaces/backend/api/workspaces_handler.go index 889ba009..5e8bfacc 100644 --- a/workspaces/backend/api/workspaces_handler.go +++ b/workspaces/backend/api/workspaces_handler.go @@ -39,6 +39,23 @@ type WorkspaceListEnvelope Envelope[[]models.Workspace] type WorkspaceEnvelope Envelope[models.Workspace] +// GetWorkspaceHandler retrieves a specific workspace by namespace and name. +// +// @Summary Get workspace +// @Description Returns details of a specific workspace identified by namespace and workspace name. +// @Tags workspaces +// @Accept json +// @Produce json +// @Param namespace path string true "Namespace of the workspace" example(kubeflow-user-example-com) +// @Param workspace_name path string true "Name of the workspace" example(my-workspace) +// @Success 200 {object} WorkspaceEnvelope "Successful operation. Returns the requested workspace details." +// @Failure 400 {object} ErrorEnvelope "Bad Request. Invalid namespace or workspace name format." +// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." +// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to access the workspace." +// @Failure 404 {object} ErrorEnvelope "Not Found. Workspace does not exist." +// @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." +// @Router /api/v1/workspaces/{namespace}/{workspace_name} [get] +// @Security ApiKeyAuth func (a *App) GetWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) workspaceName := ps.ByName(ResourceNamePathParam) @@ -83,6 +100,24 @@ func (a *App) GetWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps htt a.dataResponse(w, r, responseEnvelope) } +// GetWorkspacesHandler returns a list of workspaces. +// +// @Summary List workspaces +// @Description Returns a list of workspaces. The endpoint supports two modes: +// @Description 1. List all workspaces across all namespaces (when no namespace is provided) +// @Description 2. List workspaces in a specific namespace (when namespace is provided) +// @Tags workspaces +// @Accept json +// @Produce json +// @Param namespace path string false "Namespace to filter workspaces. If not provided, returns all workspaces across all namespaces." example(kubeflow-user-example-com) +// @Success 200 {object} WorkspaceListEnvelope "Successful operation. Returns a list of workspaces." +// @Failure 400 {object} ErrorEnvelope "Bad Request. Invalid namespace format." +// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." +// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to list workspaces." +// @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." +// @Router /api/v1/workspaces [get] +// @Router /api/v1/workspaces/{namespace} [get] +// @Security ApiKeyAuth func (a *App) GetWorkspacesHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) @@ -129,6 +164,23 @@ func (a *App) GetWorkspacesHandler(w http.ResponseWriter, r *http.Request, ps ht a.dataResponse(w, r, responseEnvelope) } +// CreateWorkspaceHandler creates a new workspace in the specified namespace. +// +// @Summary Create workspace +// @Description Creates a new workspace in the specified namespace. +// @Tags workspaces +// @Accept json +// @Produce json +// @Param namespace path string true "Namespace for the workspace" example(kubeflow-user-example-com) +// @Param body body WorkspaceCreateEnvelope true "Workspace creation configuration" +// @Success 201 {object} WorkspaceEnvelope "Workspace created successfully" +// @Failure 400 {object} ErrorEnvelope "Bad Request. Invalid request body or namespace format." +// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." +// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to create workspace." +// @Failure 409 {object} ErrorEnvelope "Conflict. Workspace with the same name already exists." +// @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." +// @Router /api/v1/workspaces/{namespace} [post] +// @Security ApiKeyAuth func (a *App) CreateWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) @@ -207,6 +259,23 @@ func (a *App) CreateWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps a.createdResponse(w, r, responseEnvelope, location) } +// DeleteWorkspaceHandler deletes a specific workspace by namespace and name. +// +// @Summary Delete workspace +// @Description Deletes a specific workspace identified by namespace and workspace name. +// @Tags workspaces +// @Accept json +// @Produce json +// @Param namespace path string true "Namespace of the workspace" example(kubeflow-user-example-com) +// @Param workspace_name path string true "Name of the workspace" example(my-workspace) +// @Success 204 {object} nil "Workspace deleted successfully" +// @Failure 400 {object} ErrorEnvelope "Bad Request. Invalid namespace or workspace name format." +// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." +// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to delete the workspace." +// @Failure 404 {object} ErrorEnvelope "Not Found. Workspace does not exist." +// @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." +// @Router /api/v1/workspaces/{namespace}/{workspace_name} [delete] +// @Security ApiKeyAuth func (a *App) DeleteWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) workspaceName := ps.ByName(ResourceNamePathParam) @@ -248,4 +317,4 @@ func (a *App) DeleteWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps } a.deletedResponse(w, r) -} +} \ No newline at end of file From 7ac38a12a0defb219b3f66769516d956529e8976 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 17 Mar 2025 14:47:07 +0100 Subject: [PATCH 10/13] Add Workspaces Swagger Docs Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/workspaces_handler.go | 2 +- workspaces/backend/openapi/docs.go | 791 +++++++++++++++++++ workspaces/backend/openapi/swagger.json | 791 +++++++++++++++++++ workspaces/backend/openapi/swagger.yaml | 532 +++++++++++++ 4 files changed, 2115 insertions(+), 1 deletion(-) diff --git a/workspaces/backend/api/workspaces_handler.go b/workspaces/backend/api/workspaces_handler.go index 5e8bfacc..16578c87 100644 --- a/workspaces/backend/api/workspaces_handler.go +++ b/workspaces/backend/api/workspaces_handler.go @@ -317,4 +317,4 @@ func (a *App) DeleteWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps } a.deletedResponse(w, r) -} \ No newline at end of file +} diff --git a/workspaces/backend/openapi/docs.go b/workspaces/backend/openapi/docs.go index 3df863d0..1cd07490 100644 --- a/workspaces/backend/openapi/docs.go +++ b/workspaces/backend/openapi/docs.go @@ -19,6 +19,341 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/v1/workspaces": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of workspaces. The endpoint supports two modes:\n1. List all workspaces across all namespaces (when no namespace is provided)\n2. List workspaces in a specific namespace (when namespace is provided)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspaces", + "responses": { + "200": { + "description": "Successful operation. Returns a list of workspaces.", + "schema": { + "$ref": "#/definitions/api.WorkspaceListEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid namespace format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to list workspaces.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/api/v1/workspaces/{namespace}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of workspaces. The endpoint supports two modes:\n1. List all workspaces across all namespaces (when no namespace is provided)\n2. List workspaces in a specific namespace (when namespace is provided)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspaces", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace to filter workspaces. If not provided, returns all workspaces across all namespaces.", + "name": "namespace", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful operation. Returns a list of workspaces.", + "schema": { + "$ref": "#/definitions/api.WorkspaceListEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid namespace format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to list workspaces.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Creates a new workspace in the specified namespace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Create workspace", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace for the workspace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "description": "Workspace creation configuration", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.WorkspaceCreateEnvelope" + } + } + ], + "responses": { + "201": { + "description": "Workspace created successfully", + "schema": { + "$ref": "#/definitions/api.WorkspaceEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid request body or namespace format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to create workspace.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "409": { + "description": "Conflict. Workspace with the same name already exists.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/api/v1/workspaces/{namespace}/{workspace_name}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns details of a specific workspace identified by namespace and workspace name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Get workspace", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace of the workspace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "example": "my-workspace", + "description": "Name of the workspace", + "name": "workspace_name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Successful operation. Returns the requested workspace details.", + "schema": { + "$ref": "#/definitions/api.WorkspaceEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid namespace or workspace name format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to access the workspace.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found. Workspace does not exist.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Deletes a specific workspace identified by namespace and workspace name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Delete workspace", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace of the workspace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "example": "my-workspace", + "description": "Name of the workspace", + "name": "workspace_name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "Workspace deleted successfully" + }, + "400": { + "description": "Bad Request. Invalid namespace or workspace name format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to delete the workspace.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found. Workspace does not exist.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, "/healthcheck": { "get": { "description": "Provides a healthcheck response indicating the status of key services.", @@ -144,6 +479,44 @@ const docTemplate = `{ } } }, + "api.WorkspaceCreateEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "allOf": [ + { + "$ref": "#/definitions/workspaces.WorkspaceCreate" + } + ] + } + } + }, + "api.WorkspaceEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "allOf": [ + { + "$ref": "#/definitions/workspaces.Workspace" + } + ] + } + } + }, + "api.WorkspaceListEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.Workspace" + } + } + } + }, "field.ErrorType": { "type": "string", "enum": [ @@ -208,6 +581,424 @@ const docTemplate = `{ "type": "string" } } + }, + "workspaces.Activity": { + "type": "object", + "properties": { + "lastActivity": { + "description": "Unix Epoch time", + "type": "integer" + }, + "lastProbe": { + "$ref": "#/definitions/workspaces.LastProbeInfo" + }, + "lastUpdate": { + "description": "Unix Epoch time", + "type": "integer" + } + } + }, + "workspaces.HttpService": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "httpPath": { + "type": "string" + } + } + }, + "workspaces.ImageConfig": { + "type": "object", + "properties": { + "current": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "desired": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "redirectChain": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.RedirectStep" + } + } + } + }, + "workspaces.ImageRef": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "workspaces.LastProbeInfo": { + "type": "object", + "properties": { + "endTimeMs": { + "description": "Unix Epoch time in milliseconds", + "type": "integer" + }, + "message": { + "type": "string" + }, + "result": { + "$ref": "#/definitions/workspaces.ProbeResult" + }, + "startTimeMs": { + "description": "Unix Epoch time in milliseconds", + "type": "integer" + } + } + }, + "workspaces.OptionInfo": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.OptionLabel" + } + } + } + }, + "workspaces.OptionLabel": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "workspaces.PodConfig": { + "type": "object", + "properties": { + "current": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "desired": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "redirectChain": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.RedirectStep" + } + } + } + }, + "workspaces.PodMetadata": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "workspaces.PodMetadataMutate": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "workspaces.PodTemplate": { + "type": "object", + "properties": { + "options": { + "$ref": "#/definitions/workspaces.PodTemplateOptions" + }, + "podMetadata": { + "$ref": "#/definitions/workspaces.PodMetadata" + }, + "volumes": { + "$ref": "#/definitions/workspaces.PodVolumes" + } + } + }, + "workspaces.PodTemplateMutate": { + "type": "object", + "properties": { + "options": { + "$ref": "#/definitions/workspaces.PodTemplateOptionsMutate" + }, + "podMetadata": { + "$ref": "#/definitions/workspaces.PodMetadataMutate" + }, + "volumes": { + "$ref": "#/definitions/workspaces.PodVolumesMutate" + } + } + }, + "workspaces.PodTemplateOptions": { + "type": "object", + "properties": { + "imageConfig": { + "$ref": "#/definitions/workspaces.ImageConfig" + }, + "podConfig": { + "$ref": "#/definitions/workspaces.PodConfig" + } + } + }, + "workspaces.PodTemplateOptionsMutate": { + "type": "object", + "properties": { + "imageConfig": { + "type": "string" + }, + "podConfig": { + "type": "string" + } + } + }, + "workspaces.PodVolumeInfo": { + "type": "object", + "properties": { + "mountPath": { + "type": "string" + }, + "pvcName": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "workspaces.PodVolumeMount": { + "type": "object", + "properties": { + "mountPath": { + "type": "string" + }, + "pvcName": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "workspaces.PodVolumes": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.PodVolumeInfo" + } + }, + "home": { + "$ref": "#/definitions/workspaces.PodVolumeInfo" + } + } + }, + "workspaces.PodVolumesMutate": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.PodVolumeMount" + } + }, + "home": { + "type": "string" + } + } + }, + "workspaces.ProbeResult": { + "type": "string", + "enum": [ + "Success", + "Failure", + "Timeout" + ], + "x-enum-varnames": [ + "ProbeResultSuccess", + "ProbeResultFailure", + "ProbeResultTimeout" + ] + }, + "workspaces.RedirectMessage": { + "type": "object", + "properties": { + "level": { + "$ref": "#/definitions/workspaces.RedirectMessageLevel" + }, + "text": { + "type": "string" + } + } + }, + "workspaces.RedirectMessageLevel": { + "type": "string", + "enum": [ + "Info", + "Warning", + "Danger" + ], + "x-enum-varnames": [ + "RedirectMessageLevelInfo", + "RedirectMessageLevelWarning", + "RedirectMessageLevelDanger" + ] + }, + "workspaces.RedirectStep": { + "type": "object", + "properties": { + "message": { + "$ref": "#/definitions/workspaces.RedirectMessage" + }, + "sourceId": { + "type": "string" + }, + "targetId": { + "type": "string" + } + } + }, + "workspaces.Service": { + "type": "object", + "properties": { + "httpService": { + "$ref": "#/definitions/workspaces.HttpService" + } + } + }, + "workspaces.Workspace": { + "type": "object", + "properties": { + "activity": { + "$ref": "#/definitions/workspaces.Activity" + }, + "deferUpdates": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "pausedTime": { + "type": "integer" + }, + "pendingRestart": { + "type": "boolean" + }, + "podTemplate": { + "$ref": "#/definitions/workspaces.PodTemplate" + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.Service" + } + }, + "state": { + "$ref": "#/definitions/workspaces.WorkspaceState" + }, + "stateMessage": { + "type": "string" + }, + "workspaceKind": { + "$ref": "#/definitions/workspaces.WorkspaceKindInfo" + } + } + }, + "workspaces.WorkspaceCreate": { + "type": "object", + "properties": { + "deferUpdates": { + "type": "boolean" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "podTemplate": { + "$ref": "#/definitions/workspaces.PodTemplateMutate" + } + } + }, + "workspaces.WorkspaceKindInfo": { + "type": "object", + "properties": { + "icon": { + "$ref": "#/definitions/workspaces.ImageRef" + }, + "logo": { + "$ref": "#/definitions/workspaces.ImageRef" + }, + "missing": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "workspaces.WorkspaceState": { + "type": "string", + "enum": [ + "Running", + "Terminating", + "Paused", + "Pending", + "Error", + "Unknown" + ], + "x-enum-varnames": [ + "WorkspaceStateRunning", + "WorkspaceStateTerminating", + "WorkspaceStatePaused", + "WorkspaceStatePending", + "WorkspaceStateError", + "WorkspaceStateUnknown" + ] } } }` diff --git a/workspaces/backend/openapi/swagger.json b/workspaces/backend/openapi/swagger.json index 4f1dd3cf..612a5549 100644 --- a/workspaces/backend/openapi/swagger.json +++ b/workspaces/backend/openapi/swagger.json @@ -17,6 +17,341 @@ "host": "localhost:4000", "basePath": "/api/v1", "paths": { + "/api/v1/workspaces": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of workspaces. The endpoint supports two modes:\n1. List all workspaces across all namespaces (when no namespace is provided)\n2. List workspaces in a specific namespace (when namespace is provided)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspaces", + "responses": { + "200": { + "description": "Successful operation. Returns a list of workspaces.", + "schema": { + "$ref": "#/definitions/api.WorkspaceListEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid namespace format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to list workspaces.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/api/v1/workspaces/{namespace}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of workspaces. The endpoint supports two modes:\n1. List all workspaces across all namespaces (when no namespace is provided)\n2. List workspaces in a specific namespace (when namespace is provided)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "List workspaces", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace to filter workspaces. If not provided, returns all workspaces across all namespaces.", + "name": "namespace", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful operation. Returns a list of workspaces.", + "schema": { + "$ref": "#/definitions/api.WorkspaceListEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid namespace format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to list workspaces.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Creates a new workspace in the specified namespace.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Create workspace", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace for the workspace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "description": "Workspace creation configuration", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.WorkspaceCreateEnvelope" + } + } + ], + "responses": { + "201": { + "description": "Workspace created successfully", + "schema": { + "$ref": "#/definitions/api.WorkspaceEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid request body or namespace format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to create workspace.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "409": { + "description": "Conflict. Workspace with the same name already exists.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/api/v1/workspaces/{namespace}/{workspace_name}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns details of a specific workspace identified by namespace and workspace name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Get workspace", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace of the workspace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "example": "my-workspace", + "description": "Name of the workspace", + "name": "workspace_name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Successful operation. Returns the requested workspace details.", + "schema": { + "$ref": "#/definitions/api.WorkspaceEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid namespace or workspace name format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to access the workspace.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found. Workspace does not exist.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Deletes a specific workspace identified by namespace and workspace name.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspaces" + ], + "summary": "Delete workspace", + "parameters": [ + { + "type": "string", + "example": "kubeflow-user-example-com", + "description": "Namespace of the workspace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "example": "my-workspace", + "description": "Name of the workspace", + "name": "workspace_name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "Workspace deleted successfully" + }, + "400": { + "description": "Bad Request. Invalid namespace or workspace name format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to delete the workspace.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found. Workspace does not exist.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, "/healthcheck": { "get": { "description": "Provides a healthcheck response indicating the status of key services.", @@ -142,6 +477,44 @@ } } }, + "api.WorkspaceCreateEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "allOf": [ + { + "$ref": "#/definitions/workspaces.WorkspaceCreate" + } + ] + } + } + }, + "api.WorkspaceEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "allOf": [ + { + "$ref": "#/definitions/workspaces.Workspace" + } + ] + } + } + }, + "api.WorkspaceListEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.Workspace" + } + } + } + }, "field.ErrorType": { "type": "string", "enum": [ @@ -206,6 +579,424 @@ "type": "string" } } + }, + "workspaces.Activity": { + "type": "object", + "properties": { + "lastActivity": { + "description": "Unix Epoch time", + "type": "integer" + }, + "lastProbe": { + "$ref": "#/definitions/workspaces.LastProbeInfo" + }, + "lastUpdate": { + "description": "Unix Epoch time", + "type": "integer" + } + } + }, + "workspaces.HttpService": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "httpPath": { + "type": "string" + } + } + }, + "workspaces.ImageConfig": { + "type": "object", + "properties": { + "current": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "desired": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "redirectChain": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.RedirectStep" + } + } + } + }, + "workspaces.ImageRef": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "workspaces.LastProbeInfo": { + "type": "object", + "properties": { + "endTimeMs": { + "description": "Unix Epoch time in milliseconds", + "type": "integer" + }, + "message": { + "type": "string" + }, + "result": { + "$ref": "#/definitions/workspaces.ProbeResult" + }, + "startTimeMs": { + "description": "Unix Epoch time in milliseconds", + "type": "integer" + } + } + }, + "workspaces.OptionInfo": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.OptionLabel" + } + } + } + }, + "workspaces.OptionLabel": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "workspaces.PodConfig": { + "type": "object", + "properties": { + "current": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "desired": { + "$ref": "#/definitions/workspaces.OptionInfo" + }, + "redirectChain": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.RedirectStep" + } + } + } + }, + "workspaces.PodMetadata": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "workspaces.PodMetadataMutate": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "workspaces.PodTemplate": { + "type": "object", + "properties": { + "options": { + "$ref": "#/definitions/workspaces.PodTemplateOptions" + }, + "podMetadata": { + "$ref": "#/definitions/workspaces.PodMetadata" + }, + "volumes": { + "$ref": "#/definitions/workspaces.PodVolumes" + } + } + }, + "workspaces.PodTemplateMutate": { + "type": "object", + "properties": { + "options": { + "$ref": "#/definitions/workspaces.PodTemplateOptionsMutate" + }, + "podMetadata": { + "$ref": "#/definitions/workspaces.PodMetadataMutate" + }, + "volumes": { + "$ref": "#/definitions/workspaces.PodVolumesMutate" + } + } + }, + "workspaces.PodTemplateOptions": { + "type": "object", + "properties": { + "imageConfig": { + "$ref": "#/definitions/workspaces.ImageConfig" + }, + "podConfig": { + "$ref": "#/definitions/workspaces.PodConfig" + } + } + }, + "workspaces.PodTemplateOptionsMutate": { + "type": "object", + "properties": { + "imageConfig": { + "type": "string" + }, + "podConfig": { + "type": "string" + } + } + }, + "workspaces.PodVolumeInfo": { + "type": "object", + "properties": { + "mountPath": { + "type": "string" + }, + "pvcName": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "workspaces.PodVolumeMount": { + "type": "object", + "properties": { + "mountPath": { + "type": "string" + }, + "pvcName": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "workspaces.PodVolumes": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.PodVolumeInfo" + } + }, + "home": { + "$ref": "#/definitions/workspaces.PodVolumeInfo" + } + } + }, + "workspaces.PodVolumesMutate": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.PodVolumeMount" + } + }, + "home": { + "type": "string" + } + } + }, + "workspaces.ProbeResult": { + "type": "string", + "enum": [ + "Success", + "Failure", + "Timeout" + ], + "x-enum-varnames": [ + "ProbeResultSuccess", + "ProbeResultFailure", + "ProbeResultTimeout" + ] + }, + "workspaces.RedirectMessage": { + "type": "object", + "properties": { + "level": { + "$ref": "#/definitions/workspaces.RedirectMessageLevel" + }, + "text": { + "type": "string" + } + } + }, + "workspaces.RedirectMessageLevel": { + "type": "string", + "enum": [ + "Info", + "Warning", + "Danger" + ], + "x-enum-varnames": [ + "RedirectMessageLevelInfo", + "RedirectMessageLevelWarning", + "RedirectMessageLevelDanger" + ] + }, + "workspaces.RedirectStep": { + "type": "object", + "properties": { + "message": { + "$ref": "#/definitions/workspaces.RedirectMessage" + }, + "sourceId": { + "type": "string" + }, + "targetId": { + "type": "string" + } + } + }, + "workspaces.Service": { + "type": "object", + "properties": { + "httpService": { + "$ref": "#/definitions/workspaces.HttpService" + } + } + }, + "workspaces.Workspace": { + "type": "object", + "properties": { + "activity": { + "$ref": "#/definitions/workspaces.Activity" + }, + "deferUpdates": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "pausedTime": { + "type": "integer" + }, + "pendingRestart": { + "type": "boolean" + }, + "podTemplate": { + "$ref": "#/definitions/workspaces.PodTemplate" + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/workspaces.Service" + } + }, + "state": { + "$ref": "#/definitions/workspaces.WorkspaceState" + }, + "stateMessage": { + "type": "string" + }, + "workspaceKind": { + "$ref": "#/definitions/workspaces.WorkspaceKindInfo" + } + } + }, + "workspaces.WorkspaceCreate": { + "type": "object", + "properties": { + "deferUpdates": { + "type": "boolean" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "podTemplate": { + "$ref": "#/definitions/workspaces.PodTemplateMutate" + } + } + }, + "workspaces.WorkspaceKindInfo": { + "type": "object", + "properties": { + "icon": { + "$ref": "#/definitions/workspaces.ImageRef" + }, + "logo": { + "$ref": "#/definitions/workspaces.ImageRef" + }, + "missing": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "workspaces.WorkspaceState": { + "type": "string", + "enum": [ + "Running", + "Terminating", + "Paused", + "Pending", + "Error", + "Unknown" + ], + "x-enum-varnames": [ + "WorkspaceStateRunning", + "WorkspaceStateTerminating", + "WorkspaceStatePaused", + "WorkspaceStatePending", + "WorkspaceStateError", + "WorkspaceStateUnknown" + ] } } } \ No newline at end of file diff --git a/workspaces/backend/openapi/swagger.yaml b/workspaces/backend/openapi/swagger.yaml index f41362e1..84b4475b 100644 --- a/workspaces/backend/openapi/swagger.yaml +++ b/workspaces/backend/openapi/swagger.yaml @@ -38,6 +38,28 @@ definitions: type: $ref: '#/definitions/field.ErrorType' type: object + api.WorkspaceCreateEnvelope: + properties: + data: + allOf: + - $ref: '#/definitions/workspaces.WorkspaceCreate' + description: 'TODO: make all declarations of Envelope use pointers for D' + type: object + api.WorkspaceEnvelope: + properties: + data: + allOf: + - $ref: '#/definitions/workspaces.Workspace' + description: 'TODO: make all declarations of Envelope use pointers for D' + type: object + api.WorkspaceListEnvelope: + properties: + data: + description: 'TODO: make all declarations of Envelope use pointers for D' + items: + $ref: '#/definitions/workspaces.Workspace' + type: array + type: object field.ErrorType: enum: - FieldValueNotFound @@ -87,6 +109,284 @@ definitions: name: type: string type: object + workspaces.Activity: + properties: + lastActivity: + description: Unix Epoch time + type: integer + lastProbe: + $ref: '#/definitions/workspaces.LastProbeInfo' + lastUpdate: + description: Unix Epoch time + type: integer + type: object + workspaces.HttpService: + properties: + displayName: + type: string + httpPath: + type: string + type: object + workspaces.ImageConfig: + properties: + current: + $ref: '#/definitions/workspaces.OptionInfo' + desired: + $ref: '#/definitions/workspaces.OptionInfo' + redirectChain: + items: + $ref: '#/definitions/workspaces.RedirectStep' + type: array + type: object + workspaces.ImageRef: + properties: + url: + type: string + type: object + workspaces.LastProbeInfo: + properties: + endTimeMs: + description: Unix Epoch time in milliseconds + type: integer + message: + type: string + result: + $ref: '#/definitions/workspaces.ProbeResult' + startTimeMs: + description: Unix Epoch time in milliseconds + type: integer + type: object + workspaces.OptionInfo: + properties: + description: + type: string + displayName: + type: string + id: + type: string + labels: + items: + $ref: '#/definitions/workspaces.OptionLabel' + type: array + type: object + workspaces.OptionLabel: + properties: + key: + type: string + value: + type: string + type: object + workspaces.PodConfig: + properties: + current: + $ref: '#/definitions/workspaces.OptionInfo' + desired: + $ref: '#/definitions/workspaces.OptionInfo' + redirectChain: + items: + $ref: '#/definitions/workspaces.RedirectStep' + type: array + type: object + workspaces.PodMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + workspaces.PodMetadataMutate: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + workspaces.PodTemplate: + properties: + options: + $ref: '#/definitions/workspaces.PodTemplateOptions' + podMetadata: + $ref: '#/definitions/workspaces.PodMetadata' + volumes: + $ref: '#/definitions/workspaces.PodVolumes' + type: object + workspaces.PodTemplateMutate: + properties: + options: + $ref: '#/definitions/workspaces.PodTemplateOptionsMutate' + podMetadata: + $ref: '#/definitions/workspaces.PodMetadataMutate' + volumes: + $ref: '#/definitions/workspaces.PodVolumesMutate' + type: object + workspaces.PodTemplateOptions: + properties: + imageConfig: + $ref: '#/definitions/workspaces.ImageConfig' + podConfig: + $ref: '#/definitions/workspaces.PodConfig' + type: object + workspaces.PodTemplateOptionsMutate: + properties: + imageConfig: + type: string + podConfig: + type: string + type: object + workspaces.PodVolumeInfo: + properties: + mountPath: + type: string + pvcName: + type: string + readOnly: + type: boolean + type: object + workspaces.PodVolumeMount: + properties: + mountPath: + type: string + pvcName: + type: string + readOnly: + type: boolean + type: object + workspaces.PodVolumes: + properties: + data: + items: + $ref: '#/definitions/workspaces.PodVolumeInfo' + type: array + home: + $ref: '#/definitions/workspaces.PodVolumeInfo' + type: object + workspaces.PodVolumesMutate: + properties: + data: + items: + $ref: '#/definitions/workspaces.PodVolumeMount' + type: array + home: + type: string + type: object + workspaces.ProbeResult: + enum: + - Success + - Failure + - Timeout + type: string + x-enum-varnames: + - ProbeResultSuccess + - ProbeResultFailure + - ProbeResultTimeout + workspaces.RedirectMessage: + properties: + level: + $ref: '#/definitions/workspaces.RedirectMessageLevel' + text: + type: string + type: object + workspaces.RedirectMessageLevel: + enum: + - Info + - Warning + - Danger + type: string + x-enum-varnames: + - RedirectMessageLevelInfo + - RedirectMessageLevelWarning + - RedirectMessageLevelDanger + workspaces.RedirectStep: + properties: + message: + $ref: '#/definitions/workspaces.RedirectMessage' + sourceId: + type: string + targetId: + type: string + type: object + workspaces.Service: + properties: + httpService: + $ref: '#/definitions/workspaces.HttpService' + type: object + workspaces.Workspace: + properties: + activity: + $ref: '#/definitions/workspaces.Activity' + deferUpdates: + type: boolean + name: + type: string + namespace: + type: string + paused: + type: boolean + pausedTime: + type: integer + pendingRestart: + type: boolean + podTemplate: + $ref: '#/definitions/workspaces.PodTemplate' + services: + items: + $ref: '#/definitions/workspaces.Service' + type: array + state: + $ref: '#/definitions/workspaces.WorkspaceState' + stateMessage: + type: string + workspaceKind: + $ref: '#/definitions/workspaces.WorkspaceKindInfo' + type: object + workspaces.WorkspaceCreate: + properties: + deferUpdates: + type: boolean + kind: + type: string + name: + type: string + paused: + type: boolean + podTemplate: + $ref: '#/definitions/workspaces.PodTemplateMutate' + type: object + workspaces.WorkspaceKindInfo: + properties: + icon: + $ref: '#/definitions/workspaces.ImageRef' + logo: + $ref: '#/definitions/workspaces.ImageRef' + missing: + type: boolean + name: + type: string + type: object + workspaces.WorkspaceState: + enum: + - Running + - Terminating + - Paused + - Pending + - Error + - Unknown + type: string + x-enum-varnames: + - WorkspaceStateRunning + - WorkspaceStateTerminating + - WorkspaceStatePaused + - WorkspaceStatePending + - WorkspaceStateError + - WorkspaceStateUnknown host: localhost:4000 info: contact: {} @@ -99,6 +399,238 @@ info: title: Kubeflow Notebooks API version: 1.0.0 paths: + /api/v1/workspaces: + get: + consumes: + - application/json + description: |- + Returns a list of workspaces. The endpoint supports two modes: + 1. List all workspaces across all namespaces (when no namespace is provided) + 2. List workspaces in a specific namespace (when namespace is provided) + produces: + - application/json + responses: + "200": + description: Successful operation. Returns a list of workspaces. + schema: + $ref: '#/definitions/api.WorkspaceListEnvelope' + "400": + description: Bad Request. Invalid namespace format. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to list workspaces. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: List workspaces + tags: + - workspaces + /api/v1/workspaces/{namespace}: + get: + consumes: + - application/json + description: |- + Returns a list of workspaces. The endpoint supports two modes: + 1. List all workspaces across all namespaces (when no namespace is provided) + 2. List workspaces in a specific namespace (when namespace is provided) + parameters: + - description: Namespace to filter workspaces. If not provided, returns all + workspaces across all namespaces. + example: kubeflow-user-example-com + in: path + name: namespace + type: string + produces: + - application/json + responses: + "200": + description: Successful operation. Returns a list of workspaces. + schema: + $ref: '#/definitions/api.WorkspaceListEnvelope' + "400": + description: Bad Request. Invalid namespace format. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to list workspaces. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: List workspaces + tags: + - workspaces + post: + consumes: + - application/json + description: Creates a new workspace in the specified namespace. + parameters: + - description: Namespace for the workspace + example: kubeflow-user-example-com + in: path + name: namespace + required: true + type: string + - description: Workspace creation configuration + in: body + name: body + required: true + schema: + $ref: '#/definitions/api.WorkspaceCreateEnvelope' + produces: + - application/json + responses: + "201": + description: Workspace created successfully + schema: + $ref: '#/definitions/api.WorkspaceEnvelope' + "400": + description: Bad Request. Invalid request body or namespace format. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to create workspace. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "409": + description: Conflict. Workspace with the same name already exists. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: Create workspace + tags: + - workspaces + /api/v1/workspaces/{namespace}/{workspace_name}: + delete: + consumes: + - application/json + description: Deletes a specific workspace identified by namespace and workspace + name. + parameters: + - description: Namespace of the workspace + example: kubeflow-user-example-com + in: path + name: namespace + required: true + type: string + - description: Name of the workspace + example: my-workspace + in: path + name: workspace_name + required: true + type: string + produces: + - application/json + responses: + "204": + description: Workspace deleted successfully + "400": + description: Bad Request. Invalid namespace or workspace name format. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to delete the workspace. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "404": + description: Not Found. Workspace does not exist. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: Delete workspace + tags: + - workspaces + get: + consumes: + - application/json + description: Returns details of a specific workspace identified by namespace + and workspace name. + parameters: + - description: Namespace of the workspace + example: kubeflow-user-example-com + in: path + name: namespace + required: true + type: string + - description: Name of the workspace + example: my-workspace + in: path + name: workspace_name + required: true + type: string + produces: + - application/json + responses: + "200": + description: Successful operation. Returns the requested workspace details. + schema: + $ref: '#/definitions/api.WorkspaceEnvelope' + "400": + description: Bad Request. Invalid namespace or workspace name format. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to access the workspace. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "404": + description: Not Found. Workspace does not exist. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: Get workspace + tags: + - workspaces /healthcheck: get: description: Provides a healthcheck response indicating the status of key services. From dc5fc4755af976effd1dbc8a6f007d8a4ae3cf5a Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 17 Mar 2025 14:49:18 +0100 Subject: [PATCH 11/13] Add Workspacekinds Swagger Docs Signed-off-by: mohamed-ben-khemis --- .../backend/api/workspacekinds_handler.go | 29 ++ workspaces/backend/openapi/docs.go | 355 ++++++++++++++++++ workspaces/backend/openapi/swagger.json | 355 ++++++++++++++++++ workspaces/backend/openapi/swagger.yaml | 238 ++++++++++++ 4 files changed, 977 insertions(+) diff --git a/workspaces/backend/api/workspacekinds_handler.go b/workspaces/backend/api/workspacekinds_handler.go index 14b97995..ebc39e5a 100644 --- a/workspaces/backend/api/workspacekinds_handler.go +++ b/workspaces/backend/api/workspacekinds_handler.go @@ -35,6 +35,22 @@ type WorkspaceKindListEnvelope Envelope[[]models.WorkspaceKind] type WorkspaceKindEnvelope Envelope[models.WorkspaceKind] +// GetWorkspaceKindHandler retrieves a specific workspace kind by name. +// +// @Summary Get workspace kind +// @Description Returns details of a specific workspace kind identified by its name. Workspace kinds define the available types of workspaces that can be created. +// @Tags workspace-kinds +// @Accept json +// @Produce json +// @Param name path string true "Name of the workspace kind" example(jupyterlab) +// @Success 200 {object} WorkspaceKindEnvelope "Successful operation. Returns the requested workspace kind details." +// @Failure 400 {object} ErrorEnvelope "Bad Request. Invalid workspace kind name format." +// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." +// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to access the workspace kind." +// @Failure 404 {object} ErrorEnvelope "Not Found. Workspace kind does not exist." +// @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." +// @Router /api/v1/workspace-kinds/{name} [get] +// @Security ApiKeyAuth func (a *App) GetWorkspaceKindHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { name := ps.ByName(ResourceNamePathParam) @@ -74,6 +90,19 @@ func (a *App) GetWorkspaceKindHandler(w http.ResponseWriter, r *http.Request, ps a.dataResponse(w, r, responseEnvelope) } +// GetWorkspaceKindsHandler returns a list of all available workspace kinds. +// +// @Summary List workspace kinds +// @Description Returns a list of all available workspace kinds. Workspace kinds define the different types of workspaces that can be created in the system. +// @Tags workspace-kinds +// @Accept json +// @Produce json +// @Success 200 {object} WorkspaceKindListEnvelope "Successful operation. Returns a list of all available workspace kinds." +// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." +// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to list workspace kinds." +// @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." +// @Router /api/v1/workspace-kinds [get] +// @Security ApiKeyAuth func (a *App) GetWorkspaceKindsHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { // =========================== AUTH =========================== authPolicies := []*auth.ResourcePolicy{ diff --git a/workspaces/backend/openapi/docs.go b/workspaces/backend/openapi/docs.go index 1cd07490..2df155bc 100644 --- a/workspaces/backend/openapi/docs.go +++ b/workspaces/backend/openapi/docs.go @@ -19,6 +19,120 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/v1/workspace-kinds": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all available workspace kinds. Workspace kinds define the different types of workspaces that can be created in the system.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspace-kinds" + ], + "summary": "List workspace kinds", + "responses": { + "200": { + "description": "Successful operation. Returns a list of all available workspace kinds.", + "schema": { + "$ref": "#/definitions/api.WorkspaceKindListEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to list workspace kinds.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/api/v1/workspace-kinds/{name}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns details of a specific workspace kind identified by its name. Workspace kinds define the available types of workspaces that can be created.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspace-kinds" + ], + "summary": "Get workspace kind", + "parameters": [ + { + "type": "string", + "example": "jupyterlab", + "description": "Name of the workspace kind", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Successful operation. Returns the requested workspace kind details.", + "schema": { + "$ref": "#/definitions/api.WorkspaceKindEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid workspace kind name format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to access the workspace kind.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found. Workspace kind does not exist.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, "/api/v1/workspaces": { "get": { "security": [ @@ -505,6 +619,31 @@ const docTemplate = `{ } } }, + "api.WorkspaceKindEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "allOf": [ + { + "$ref": "#/definitions/workspacekinds.WorkspaceKind" + } + ] + } + } + }, + "api.WorkspaceKindListEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.WorkspaceKind" + } + } + } + }, "api.WorkspaceListEnvelope": { "type": "object", "properties": { @@ -582,6 +721,222 @@ const docTemplate = `{ } } }, + "workspacekinds.ImageConfig": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.ImageConfigValue" + } + } + } + }, + "workspacekinds.ImageConfigValue": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.OptionLabel" + } + }, + "redirect": { + "$ref": "#/definitions/workspacekinds.OptionRedirect" + } + } + }, + "workspacekinds.ImageRef": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "workspacekinds.OptionLabel": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "workspacekinds.OptionRedirect": { + "type": "object", + "properties": { + "message": { + "$ref": "#/definitions/workspacekinds.RedirectMessage" + }, + "to": { + "type": "string" + } + } + }, + "workspacekinds.PodConfig": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.PodConfigValue" + } + } + } + }, + "workspacekinds.PodConfigValue": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.OptionLabel" + } + }, + "redirect": { + "$ref": "#/definitions/workspacekinds.OptionRedirect" + } + } + }, + "workspacekinds.PodMetadata": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "workspacekinds.PodTemplate": { + "type": "object", + "properties": { + "options": { + "$ref": "#/definitions/workspacekinds.PodTemplateOptions" + }, + "podMetadata": { + "$ref": "#/definitions/workspacekinds.PodMetadata" + }, + "volumeMounts": { + "$ref": "#/definitions/workspacekinds.PodVolumeMounts" + } + } + }, + "workspacekinds.PodTemplateOptions": { + "type": "object", + "properties": { + "imageConfig": { + "$ref": "#/definitions/workspacekinds.ImageConfig" + }, + "podConfig": { + "$ref": "#/definitions/workspacekinds.PodConfig" + } + } + }, + "workspacekinds.PodVolumeMounts": { + "type": "object", + "properties": { + "home": { + "type": "string" + } + } + }, + "workspacekinds.RedirectMessage": { + "type": "object", + "properties": { + "level": { + "$ref": "#/definitions/workspacekinds.RedirectMessageLevel" + }, + "text": { + "type": "string" + } + } + }, + "workspacekinds.RedirectMessageLevel": { + "type": "string", + "enum": [ + "Info", + "Warning", + "Danger" + ], + "x-enum-varnames": [ + "RedirectMessageLevelInfo", + "RedirectMessageLevelWarning", + "RedirectMessageLevelDanger" + ] + }, + "workspacekinds.WorkspaceKind": { + "type": "object", + "properties": { + "deprecated": { + "type": "boolean" + }, + "deprecationMessage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "icon": { + "$ref": "#/definitions/workspacekinds.ImageRef" + }, + "logo": { + "$ref": "#/definitions/workspacekinds.ImageRef" + }, + "name": { + "type": "string" + }, + "podTemplate": { + "$ref": "#/definitions/workspacekinds.PodTemplate" + } + } + }, "workspaces.Activity": { "type": "object", "properties": { diff --git a/workspaces/backend/openapi/swagger.json b/workspaces/backend/openapi/swagger.json index 612a5549..50a2a5bd 100644 --- a/workspaces/backend/openapi/swagger.json +++ b/workspaces/backend/openapi/swagger.json @@ -17,6 +17,120 @@ "host": "localhost:4000", "basePath": "/api/v1", "paths": { + "/api/v1/workspace-kinds": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all available workspace kinds. Workspace kinds define the different types of workspaces that can be created in the system.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspace-kinds" + ], + "summary": "List workspace kinds", + "responses": { + "200": { + "description": "Successful operation. Returns a list of all available workspace kinds.", + "schema": { + "$ref": "#/definitions/api.WorkspaceKindListEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to list workspace kinds.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/api/v1/workspace-kinds/{name}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns details of a specific workspace kind identified by its name. Workspace kinds define the available types of workspaces that can be created.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "workspace-kinds" + ], + "summary": "Get workspace kind", + "parameters": [ + { + "type": "string", + "example": "jupyterlab", + "description": "Name of the workspace kind", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Successful operation. Returns the requested workspace kind details.", + "schema": { + "$ref": "#/definitions/api.WorkspaceKindEnvelope" + } + }, + "400": { + "description": "Bad Request. Invalid workspace kind name format.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized. Authentication is required.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden. User does not have permission to access the workspace kind.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found. Workspace kind does not exist.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error. An unexpected error occurred on the server.", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, "/api/v1/workspaces": { "get": { "security": [ @@ -503,6 +617,31 @@ } } }, + "api.WorkspaceKindEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "allOf": [ + { + "$ref": "#/definitions/workspacekinds.WorkspaceKind" + } + ] + } + } + }, + "api.WorkspaceKindListEnvelope": { + "type": "object", + "properties": { + "data": { + "description": "TODO: make all declarations of Envelope use pointers for D", + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.WorkspaceKind" + } + } + } + }, "api.WorkspaceListEnvelope": { "type": "object", "properties": { @@ -580,6 +719,222 @@ } } }, + "workspacekinds.ImageConfig": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.ImageConfigValue" + } + } + } + }, + "workspacekinds.ImageConfigValue": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.OptionLabel" + } + }, + "redirect": { + "$ref": "#/definitions/workspacekinds.OptionRedirect" + } + } + }, + "workspacekinds.ImageRef": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "workspacekinds.OptionLabel": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "workspacekinds.OptionRedirect": { + "type": "object", + "properties": { + "message": { + "$ref": "#/definitions/workspacekinds.RedirectMessage" + }, + "to": { + "type": "string" + } + } + }, + "workspacekinds.PodConfig": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.PodConfigValue" + } + } + } + }, + "workspacekinds.PodConfigValue": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/workspacekinds.OptionLabel" + } + }, + "redirect": { + "$ref": "#/definitions/workspacekinds.OptionRedirect" + } + } + }, + "workspacekinds.PodMetadata": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "workspacekinds.PodTemplate": { + "type": "object", + "properties": { + "options": { + "$ref": "#/definitions/workspacekinds.PodTemplateOptions" + }, + "podMetadata": { + "$ref": "#/definitions/workspacekinds.PodMetadata" + }, + "volumeMounts": { + "$ref": "#/definitions/workspacekinds.PodVolumeMounts" + } + } + }, + "workspacekinds.PodTemplateOptions": { + "type": "object", + "properties": { + "imageConfig": { + "$ref": "#/definitions/workspacekinds.ImageConfig" + }, + "podConfig": { + "$ref": "#/definitions/workspacekinds.PodConfig" + } + } + }, + "workspacekinds.PodVolumeMounts": { + "type": "object", + "properties": { + "home": { + "type": "string" + } + } + }, + "workspacekinds.RedirectMessage": { + "type": "object", + "properties": { + "level": { + "$ref": "#/definitions/workspacekinds.RedirectMessageLevel" + }, + "text": { + "type": "string" + } + } + }, + "workspacekinds.RedirectMessageLevel": { + "type": "string", + "enum": [ + "Info", + "Warning", + "Danger" + ], + "x-enum-varnames": [ + "RedirectMessageLevelInfo", + "RedirectMessageLevelWarning", + "RedirectMessageLevelDanger" + ] + }, + "workspacekinds.WorkspaceKind": { + "type": "object", + "properties": { + "deprecated": { + "type": "boolean" + }, + "deprecationMessage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "icon": { + "$ref": "#/definitions/workspacekinds.ImageRef" + }, + "logo": { + "$ref": "#/definitions/workspacekinds.ImageRef" + }, + "name": { + "type": "string" + }, + "podTemplate": { + "$ref": "#/definitions/workspacekinds.PodTemplate" + } + } + }, "workspaces.Activity": { "type": "object", "properties": { diff --git a/workspaces/backend/openapi/swagger.yaml b/workspaces/backend/openapi/swagger.yaml index 84b4475b..79cc0e73 100644 --- a/workspaces/backend/openapi/swagger.yaml +++ b/workspaces/backend/openapi/swagger.yaml @@ -52,6 +52,21 @@ definitions: - $ref: '#/definitions/workspaces.Workspace' description: 'TODO: make all declarations of Envelope use pointers for D' type: object + api.WorkspaceKindEnvelope: + properties: + data: + allOf: + - $ref: '#/definitions/workspacekinds.WorkspaceKind' + description: 'TODO: make all declarations of Envelope use pointers for D' + type: object + api.WorkspaceKindListEnvelope: + properties: + data: + description: 'TODO: make all declarations of Envelope use pointers for D' + items: + $ref: '#/definitions/workspacekinds.WorkspaceKind' + type: array + type: object api.WorkspaceListEnvelope: properties: data: @@ -109,6 +124,147 @@ definitions: name: type: string type: object + workspacekinds.ImageConfig: + properties: + default: + type: string + values: + items: + $ref: '#/definitions/workspacekinds.ImageConfigValue' + type: array + type: object + workspacekinds.ImageConfigValue: + properties: + description: + type: string + displayName: + type: string + hidden: + type: boolean + id: + type: string + labels: + items: + $ref: '#/definitions/workspacekinds.OptionLabel' + type: array + redirect: + $ref: '#/definitions/workspacekinds.OptionRedirect' + type: object + workspacekinds.ImageRef: + properties: + url: + type: string + type: object + workspacekinds.OptionLabel: + properties: + key: + type: string + value: + type: string + type: object + workspacekinds.OptionRedirect: + properties: + message: + $ref: '#/definitions/workspacekinds.RedirectMessage' + to: + type: string + type: object + workspacekinds.PodConfig: + properties: + default: + type: string + values: + items: + $ref: '#/definitions/workspacekinds.PodConfigValue' + type: array + type: object + workspacekinds.PodConfigValue: + properties: + description: + type: string + displayName: + type: string + hidden: + type: boolean + id: + type: string + labels: + items: + $ref: '#/definitions/workspacekinds.OptionLabel' + type: array + redirect: + $ref: '#/definitions/workspacekinds.OptionRedirect' + type: object + workspacekinds.PodMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + workspacekinds.PodTemplate: + properties: + options: + $ref: '#/definitions/workspacekinds.PodTemplateOptions' + podMetadata: + $ref: '#/definitions/workspacekinds.PodMetadata' + volumeMounts: + $ref: '#/definitions/workspacekinds.PodVolumeMounts' + type: object + workspacekinds.PodTemplateOptions: + properties: + imageConfig: + $ref: '#/definitions/workspacekinds.ImageConfig' + podConfig: + $ref: '#/definitions/workspacekinds.PodConfig' + type: object + workspacekinds.PodVolumeMounts: + properties: + home: + type: string + type: object + workspacekinds.RedirectMessage: + properties: + level: + $ref: '#/definitions/workspacekinds.RedirectMessageLevel' + text: + type: string + type: object + workspacekinds.RedirectMessageLevel: + enum: + - Info + - Warning + - Danger + type: string + x-enum-varnames: + - RedirectMessageLevelInfo + - RedirectMessageLevelWarning + - RedirectMessageLevelDanger + workspacekinds.WorkspaceKind: + properties: + deprecated: + type: boolean + deprecationMessage: + type: string + description: + type: string + displayName: + type: string + hidden: + type: boolean + icon: + $ref: '#/definitions/workspacekinds.ImageRef' + logo: + $ref: '#/definitions/workspacekinds.ImageRef' + name: + type: string + podTemplate: + $ref: '#/definitions/workspacekinds.PodTemplate' + type: object workspaces.Activity: properties: lastActivity: @@ -399,6 +555,88 @@ info: title: Kubeflow Notebooks API version: 1.0.0 paths: + /api/v1/workspace-kinds: + get: + consumes: + - application/json + description: Returns a list of all available workspace kinds. Workspace kinds + define the different types of workspaces that can be created in the system. + produces: + - application/json + responses: + "200": + description: Successful operation. Returns a list of all available workspace + kinds. + schema: + $ref: '#/definitions/api.WorkspaceKindListEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to list workspace + kinds. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: List workspace kinds + tags: + - workspace-kinds + /api/v1/workspace-kinds/{name}: + get: + consumes: + - application/json + description: Returns details of a specific workspace kind identified by its + name. Workspace kinds define the available types of workspaces that can be + created. + parameters: + - description: Name of the workspace kind + example: jupyterlab + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: Successful operation. Returns the requested workspace kind + details. + schema: + $ref: '#/definitions/api.WorkspaceKindEnvelope' + "400": + description: Bad Request. Invalid workspace kind name format. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "401": + description: Unauthorized. Authentication is required. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden. User does not have permission to access the workspace + kind. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "404": + description: Not Found. Workspace kind does not exist. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error. An unexpected error occurred on the + server. + schema: + $ref: '#/definitions/api.ErrorEnvelope' + security: + - ApiKeyAuth: [] + summary: Get workspace kind + tags: + - workspace-kinds /api/v1/workspaces: get: consumes: From 684bc95bfb1775e4b9766626da6ca0508dc66759 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 17 Mar 2025 14:59:02 +0100 Subject: [PATCH 12/13] fix: prevent Swagger from reading TODO comment by adding a tab Signed-off-by: mohamed-ben-khemis --- workspaces/backend/api/helpers.go | 1 + workspaces/backend/openapi/docs.go | 24 +++--------------------- workspaces/backend/openapi/swagger.json | 24 +++--------------------- workspaces/backend/openapi/swagger.yaml | 15 +++------------ 4 files changed, 10 insertions(+), 54 deletions(-) diff --git a/workspaces/backend/api/helpers.go b/workspaces/backend/api/helpers.go index ed14d3f8..0076a060 100644 --- a/workspaces/backend/api/helpers.go +++ b/workspaces/backend/api/helpers.go @@ -28,6 +28,7 @@ import ( // NOTE: error responses use the ErrorEnvelope type type Envelope[D any] struct { // TODO: make all declarations of Envelope use pointers for D + Data D `json:"data"` } diff --git a/workspaces/backend/openapi/docs.go b/workspaces/backend/openapi/docs.go index 2df155bc..9ace8688 100644 --- a/workspaces/backend/openapi/docs.go +++ b/workspaces/backend/openapi/docs.go @@ -571,7 +571,6 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", "type": "array", "items": { "$ref": "#/definitions/namespaces.Namespace" @@ -597,12 +596,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", - "allOf": [ - { - "$ref": "#/definitions/workspaces.WorkspaceCreate" - } - ] + "$ref": "#/definitions/workspaces.WorkspaceCreate" } } }, @@ -610,12 +604,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", - "allOf": [ - { - "$ref": "#/definitions/workspaces.Workspace" - } - ] + "$ref": "#/definitions/workspaces.Workspace" } } }, @@ -623,12 +612,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", - "allOf": [ - { - "$ref": "#/definitions/workspacekinds.WorkspaceKind" - } - ] + "$ref": "#/definitions/workspacekinds.WorkspaceKind" } } }, @@ -636,7 +620,6 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", "type": "array", "items": { "$ref": "#/definitions/workspacekinds.WorkspaceKind" @@ -648,7 +631,6 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", "type": "array", "items": { "$ref": "#/definitions/workspaces.Workspace" diff --git a/workspaces/backend/openapi/swagger.json b/workspaces/backend/openapi/swagger.json index 50a2a5bd..06721c49 100644 --- a/workspaces/backend/openapi/swagger.json +++ b/workspaces/backend/openapi/swagger.json @@ -569,7 +569,6 @@ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", "type": "array", "items": { "$ref": "#/definitions/namespaces.Namespace" @@ -595,12 +594,7 @@ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", - "allOf": [ - { - "$ref": "#/definitions/workspaces.WorkspaceCreate" - } - ] + "$ref": "#/definitions/workspaces.WorkspaceCreate" } } }, @@ -608,12 +602,7 @@ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", - "allOf": [ - { - "$ref": "#/definitions/workspaces.Workspace" - } - ] + "$ref": "#/definitions/workspaces.Workspace" } } }, @@ -621,12 +610,7 @@ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", - "allOf": [ - { - "$ref": "#/definitions/workspacekinds.WorkspaceKind" - } - ] + "$ref": "#/definitions/workspacekinds.WorkspaceKind" } } }, @@ -634,7 +618,6 @@ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", "type": "array", "items": { "$ref": "#/definitions/workspacekinds.WorkspaceKind" @@ -646,7 +629,6 @@ "type": "object", "properties": { "data": { - "description": "TODO: make all declarations of Envelope use pointers for D", "type": "array", "items": { "$ref": "#/definitions/workspaces.Workspace" diff --git a/workspaces/backend/openapi/swagger.yaml b/workspaces/backend/openapi/swagger.yaml index 79cc0e73..10879a1d 100644 --- a/workspaces/backend/openapi/swagger.yaml +++ b/workspaces/backend/openapi/swagger.yaml @@ -24,7 +24,6 @@ definitions: api.NamespaceListEnvelope: properties: data: - description: 'TODO: make all declarations of Envelope use pointers for D' items: $ref: '#/definitions/namespaces.Namespace' type: array @@ -41,28 +40,21 @@ definitions: api.WorkspaceCreateEnvelope: properties: data: - allOf: - - $ref: '#/definitions/workspaces.WorkspaceCreate' - description: 'TODO: make all declarations of Envelope use pointers for D' + $ref: '#/definitions/workspaces.WorkspaceCreate' type: object api.WorkspaceEnvelope: properties: data: - allOf: - - $ref: '#/definitions/workspaces.Workspace' - description: 'TODO: make all declarations of Envelope use pointers for D' + $ref: '#/definitions/workspaces.Workspace' type: object api.WorkspaceKindEnvelope: properties: data: - allOf: - - $ref: '#/definitions/workspacekinds.WorkspaceKind' - description: 'TODO: make all declarations of Envelope use pointers for D' + $ref: '#/definitions/workspacekinds.WorkspaceKind' type: object api.WorkspaceKindListEnvelope: properties: data: - description: 'TODO: make all declarations of Envelope use pointers for D' items: $ref: '#/definitions/workspacekinds.WorkspaceKind' type: array @@ -70,7 +62,6 @@ definitions: api.WorkspaceListEnvelope: properties: data: - description: 'TODO: make all declarations of Envelope use pointers for D' items: $ref: '#/definitions/workspaces.Workspace' type: array From 8ad2d5553285fc11df5807ef91ce846a88b56d59 Mon Sep 17 00:00:00 2001 From: mohamed-ben-khemis Date: Mon, 5 May 2025 23:02:13 +0100 Subject: [PATCH 13/13] fix: resolve double api/v1 prefix in route paths Signed-off-by: mohamed-ben-khemis --- .../backend/api/workspacekinds_handler.go | 8 +- workspaces/backend/api/workspaces_handler.go | 10 +- workspaces/backend/openapi/docs.go | 142 +++++++++--------- workspaces/backend/openapi/swagger.json | 142 +++++++++--------- workspaces/backend/openapi/swagger.yaml | 98 ++++++------ 5 files changed, 200 insertions(+), 200 deletions(-) diff --git a/workspaces/backend/api/workspacekinds_handler.go b/workspaces/backend/api/workspacekinds_handler.go index ebc39e5a..b19fc327 100644 --- a/workspaces/backend/api/workspacekinds_handler.go +++ b/workspaces/backend/api/workspacekinds_handler.go @@ -39,7 +39,7 @@ type WorkspaceKindEnvelope Envelope[models.WorkspaceKind] // // @Summary Get workspace kind // @Description Returns details of a specific workspace kind identified by its name. Workspace kinds define the available types of workspaces that can be created. -// @Tags workspace-kinds +// @Tags workspacekinds // @Accept json // @Produce json // @Param name path string true "Name of the workspace kind" example(jupyterlab) @@ -49,7 +49,7 @@ type WorkspaceKindEnvelope Envelope[models.WorkspaceKind] // @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to access the workspace kind." // @Failure 404 {object} ErrorEnvelope "Not Found. Workspace kind does not exist." // @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." -// @Router /api/v1/workspace-kinds/{name} [get] +// @Router /workspacekinds/{name} [get] // @Security ApiKeyAuth func (a *App) GetWorkspaceKindHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { name := ps.ByName(ResourceNamePathParam) @@ -94,14 +94,14 @@ func (a *App) GetWorkspaceKindHandler(w http.ResponseWriter, r *http.Request, ps // // @Summary List workspace kinds // @Description Returns a list of all available workspace kinds. Workspace kinds define the different types of workspaces that can be created in the system. -// @Tags workspace-kinds +// @Tags workspacekinds // @Accept json // @Produce json // @Success 200 {object} WorkspaceKindListEnvelope "Successful operation. Returns a list of all available workspace kinds." // @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." // @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to list workspace kinds." // @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." -// @Router /api/v1/workspace-kinds [get] +// @Router /workspacekinds [get] // @Security ApiKeyAuth func (a *App) GetWorkspaceKindsHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { // =========================== AUTH =========================== diff --git a/workspaces/backend/api/workspaces_handler.go b/workspaces/backend/api/workspaces_handler.go index 16578c87..a64c04c7 100644 --- a/workspaces/backend/api/workspaces_handler.go +++ b/workspaces/backend/api/workspaces_handler.go @@ -54,7 +54,7 @@ type WorkspaceEnvelope Envelope[models.Workspace] // @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to access the workspace." // @Failure 404 {object} ErrorEnvelope "Not Found. Workspace does not exist." // @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." -// @Router /api/v1/workspaces/{namespace}/{workspace_name} [get] +// @Router /workspaces/{namespace}/{workspace_name} [get] // @Security ApiKeyAuth func (a *App) GetWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) @@ -115,8 +115,8 @@ func (a *App) GetWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps htt // @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required." // @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to list workspaces." // @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." -// @Router /api/v1/workspaces [get] -// @Router /api/v1/workspaces/{namespace} [get] +// @Router /workspaces [get] +// @Router /workspaces/{namespace} [get] // @Security ApiKeyAuth func (a *App) GetWorkspacesHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) @@ -179,7 +179,7 @@ func (a *App) GetWorkspacesHandler(w http.ResponseWriter, r *http.Request, ps ht // @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to create workspace." // @Failure 409 {object} ErrorEnvelope "Conflict. Workspace with the same name already exists." // @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." -// @Router /api/v1/workspaces/{namespace} [post] +// @Router /workspaces/{namespace} [post] // @Security ApiKeyAuth func (a *App) CreateWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) @@ -274,7 +274,7 @@ func (a *App) CreateWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps // @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to delete the workspace." // @Failure 404 {object} ErrorEnvelope "Not Found. Workspace does not exist." // @Failure 500 {object} ErrorEnvelope "Internal server error. An unexpected error occurred on the server." -// @Router /api/v1/workspaces/{namespace}/{workspace_name} [delete] +// @Router /workspaces/{namespace}/{workspace_name} [delete] // @Security ApiKeyAuth func (a *App) DeleteWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { namespace := ps.ByName(NamespacePathParam) diff --git a/workspaces/backend/openapi/docs.go b/workspaces/backend/openapi/docs.go index 9ace8688..245a64fb 100644 --- a/workspaces/backend/openapi/docs.go +++ b/workspaces/backend/openapi/docs.go @@ -19,7 +19,71 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/api/v1/workspace-kinds": { + "/healthcheck": { + "get": { + "description": "Provides a healthcheck response indicating the status of key services.", + "produces": [ + "application/json" + ], + "tags": [ + "healthcheck" + ], + "summary": "Returns the health status of the application", + "responses": { + "200": { + "description": "Successful healthcheck response", + "schema": { + "$ref": "#/definitions/health_check.HealthCheck" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/namespaces": { + "get": { + "description": "Provides a list of all namespaces that the user has access to", + "produces": [ + "application/json" + ], + "tags": [ + "namespaces" + ], + "summary": "Returns a list of all namespaces", + "responses": { + "200": { + "description": "Successful namespaces response", + "schema": { + "$ref": "#/definitions/api.NamespaceListEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/workspacekinds": { "get": { "security": [ { @@ -34,7 +98,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspace-kinds" + "workspacekinds" ], "summary": "List workspace kinds", "responses": { @@ -65,7 +129,7 @@ const docTemplate = `{ } } }, - "/api/v1/workspace-kinds/{name}": { + "/workspacekinds/{name}": { "get": { "security": [ { @@ -80,7 +144,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "workspace-kinds" + "workspacekinds" ], "summary": "Get workspace kind", "parameters": [ @@ -133,7 +197,7 @@ const docTemplate = `{ } } }, - "/api/v1/workspaces": { + "/workspaces": { "get": { "security": [ { @@ -185,7 +249,7 @@ const docTemplate = `{ } } }, - "/api/v1/workspaces/{namespace}": { + "/workspaces/{namespace}": { "get": { "security": [ { @@ -321,7 +385,7 @@ const docTemplate = `{ } } }, - "/api/v1/workspaces/{namespace}/{workspace_name}": { + "/workspaces/{namespace}/{workspace_name}": { "get": { "security": [ { @@ -467,70 +531,6 @@ const docTemplate = `{ } } } - }, - "/healthcheck": { - "get": { - "description": "Provides a healthcheck response indicating the status of key services.", - "produces": [ - "application/json" - ], - "tags": [ - "healthcheck" - ], - "summary": "Returns the health status of the application", - "responses": { - "200": { - "description": "Successful healthcheck response", - "schema": { - "$ref": "#/definitions/health_check.HealthCheck" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - } - } - } - }, - "/namespaces": { - "get": { - "description": "Provides a list of all namespaces that the user has access to", - "produces": [ - "application/json" - ], - "tags": [ - "namespaces" - ], - "summary": "Returns a list of all namespaces", - "responses": { - "200": { - "description": "Successful namespaces response", - "schema": { - "$ref": "#/definitions/api.NamespaceListEnvelope" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - } - } - } } }, "definitions": { diff --git a/workspaces/backend/openapi/swagger.json b/workspaces/backend/openapi/swagger.json index 06721c49..fc438928 100644 --- a/workspaces/backend/openapi/swagger.json +++ b/workspaces/backend/openapi/swagger.json @@ -17,7 +17,71 @@ "host": "localhost:4000", "basePath": "/api/v1", "paths": { - "/api/v1/workspace-kinds": { + "/healthcheck": { + "get": { + "description": "Provides a healthcheck response indicating the status of key services.", + "produces": [ + "application/json" + ], + "tags": [ + "healthcheck" + ], + "summary": "Returns the health status of the application", + "responses": { + "200": { + "description": "Successful healthcheck response", + "schema": { + "$ref": "#/definitions/health_check.HealthCheck" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/namespaces": { + "get": { + "description": "Provides a list of all namespaces that the user has access to", + "produces": [ + "application/json" + ], + "tags": [ + "namespaces" + ], + "summary": "Returns a list of all namespaces", + "responses": { + "200": { + "description": "Successful namespaces response", + "schema": { + "$ref": "#/definitions/api.NamespaceListEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api.ErrorEnvelope" + } + } + } + } + }, + "/workspacekinds": { "get": { "security": [ { @@ -32,7 +96,7 @@ "application/json" ], "tags": [ - "workspace-kinds" + "workspacekinds" ], "summary": "List workspace kinds", "responses": { @@ -63,7 +127,7 @@ } } }, - "/api/v1/workspace-kinds/{name}": { + "/workspacekinds/{name}": { "get": { "security": [ { @@ -78,7 +142,7 @@ "application/json" ], "tags": [ - "workspace-kinds" + "workspacekinds" ], "summary": "Get workspace kind", "parameters": [ @@ -131,7 +195,7 @@ } } }, - "/api/v1/workspaces": { + "/workspaces": { "get": { "security": [ { @@ -183,7 +247,7 @@ } } }, - "/api/v1/workspaces/{namespace}": { + "/workspaces/{namespace}": { "get": { "security": [ { @@ -319,7 +383,7 @@ } } }, - "/api/v1/workspaces/{namespace}/{workspace_name}": { + "/workspaces/{namespace}/{workspace_name}": { "get": { "security": [ { @@ -465,70 +529,6 @@ } } } - }, - "/healthcheck": { - "get": { - "description": "Provides a healthcheck response indicating the status of key services.", - "produces": [ - "application/json" - ], - "tags": [ - "healthcheck" - ], - "summary": "Returns the health status of the application", - "responses": { - "200": { - "description": "Successful healthcheck response", - "schema": { - "$ref": "#/definitions/health_check.HealthCheck" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - } - } - } - }, - "/namespaces": { - "get": { - "description": "Provides a list of all namespaces that the user has access to", - "produces": [ - "application/json" - ], - "tags": [ - "namespaces" - ], - "summary": "Returns a list of all namespaces", - "responses": { - "200": { - "description": "Successful namespaces response", - "schema": { - "$ref": "#/definitions/api.NamespaceListEnvelope" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api.ErrorEnvelope" - } - } - } - } } }, "definitions": { diff --git a/workspaces/backend/openapi/swagger.yaml b/workspaces/backend/openapi/swagger.yaml index 10879a1d..27cf9bcc 100644 --- a/workspaces/backend/openapi/swagger.yaml +++ b/workspaces/backend/openapi/swagger.yaml @@ -546,7 +546,49 @@ info: title: Kubeflow Notebooks API version: 1.0.0 paths: - /api/v1/workspace-kinds: + /healthcheck: + get: + description: Provides a healthcheck response indicating the status of key services. + produces: + - application/json + responses: + "200": + description: Successful healthcheck response + schema: + $ref: '#/definitions/health_check.HealthCheck' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api.ErrorEnvelope' + summary: Returns the health status of the application + tags: + - healthcheck + /namespaces: + get: + description: Provides a list of all namespaces that the user has access to + produces: + - application/json + responses: + "200": + description: Successful namespaces response + schema: + $ref: '#/definitions/api.NamespaceListEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.ErrorEnvelope' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api.ErrorEnvelope' + summary: Returns a list of all namespaces + tags: + - namespaces + /workspacekinds: get: consumes: - application/json @@ -578,8 +620,8 @@ paths: - ApiKeyAuth: [] summary: List workspace kinds tags: - - workspace-kinds - /api/v1/workspace-kinds/{name}: + - workspacekinds + /workspacekinds/{name}: get: consumes: - application/json @@ -627,8 +669,8 @@ paths: - ApiKeyAuth: [] summary: Get workspace kind tags: - - workspace-kinds - /api/v1/workspaces: + - workspacekinds + /workspaces: get: consumes: - application/json @@ -665,7 +707,7 @@ paths: summary: List workspaces tags: - workspaces - /api/v1/workspaces/{namespace}: + /workspaces/{namespace}: get: consumes: - application/json @@ -759,7 +801,7 @@ paths: summary: Create workspace tags: - workspaces - /api/v1/workspaces/{namespace}/{workspace_name}: + /workspaces/{namespace}/{workspace_name}: delete: consumes: - application/json @@ -860,48 +902,6 @@ paths: summary: Get workspace tags: - workspaces - /healthcheck: - get: - description: Provides a healthcheck response indicating the status of key services. - produces: - - application/json - responses: - "200": - description: Successful healthcheck response - schema: - $ref: '#/definitions/health_check.HealthCheck' - "500": - description: Internal server error - schema: - $ref: '#/definitions/api.ErrorEnvelope' - summary: Returns the health status of the application - tags: - - healthcheck - /namespaces: - get: - description: Provides a list of all namespaces that the user has access to - produces: - - application/json - responses: - "200": - description: Successful namespaces response - schema: - $ref: '#/definitions/api.NamespaceListEnvelope' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/api.ErrorEnvelope' - "403": - description: Forbidden - schema: - $ref: '#/definitions/api.ErrorEnvelope' - "500": - description: Internal server error - schema: - $ref: '#/definitions/api.ErrorEnvelope' - summary: Returns a list of all namespaces - tags: - - namespaces schemes: - http - https