mirror of
https://github.com/hanzo-ml/hub.git
synced 2026-07-27 07:04:53 +00:00
Auto generate OpenAPI spec (#2172)
* Generate openapi spec Signed-off-by: ppadti <ppadti@redhat.com> * Add swagger ui and fix format and reoragnize file Signed-off-by: ppadti <ppadti@redhat.com> * Fix ci failure Signed-off-by: ppadti <ppadti@redhat.com> * Fix validate openapi spec ci failure Signed-off-by: ppadti <ppadti@redhat.com> * Add a note in readme about two openapi spec Signed-off-by: ppadti <ppadti@redhat.com> * Fix swagger ui in dev mode and package naming Signed-off-by: ppadti <ppadti@redhat.com> --------- Signed-off-by: ppadti <ppadti@redhat.com>
This commit is contained in:
@@ -229,6 +229,7 @@ clients/python/src/mr_openapi/models/serving_environment_list.py linguist-genera
|
||||
clients/python/src/mr_openapi/models/serving_environment_update.py linguist-generated=true
|
||||
clients/python/src/mr_openapi/models/sort_order.py linguist-generated=true
|
||||
clients/python/src/mr_openapi/rest.py linguist-generated=true
|
||||
clients/ui/bff/openapi/docs.go linguist-generated=true
|
||||
internal/converter/generated/embedmd_openapi_converter.gen.go linguist-generated=true
|
||||
internal/converter/generated/openapi_converter.gen.go linguist-generated=true
|
||||
internal/converter/generated/openapi_embedmd_converter.gen.go linguist-generated=true
|
||||
|
||||
@@ -47,6 +47,10 @@ jobs:
|
||||
working-directory: clients/ui/bff
|
||||
run: make build
|
||||
|
||||
- name: Generate OpenAPI spec
|
||||
working-directory: clients/ui/bff
|
||||
run: make openapi
|
||||
|
||||
- name: Check if there are uncommitted file changes
|
||||
working-directory: clients/ui/bff
|
||||
run: |
|
||||
|
||||
@@ -45,6 +45,7 @@ RUN go mod download
|
||||
# Copy the go source files
|
||||
COPY ${BFF_SOURCE_CODE}/cmd/ cmd/
|
||||
COPY ${BFF_SOURCE_CODE}/internal/ internal/
|
||||
COPY ${BFF_SOURCE_CODE}/openapi/ openapi/
|
||||
|
||||
# Build the Go application
|
||||
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o /usr/src/app/bff ./cmd
|
||||
|
||||
@@ -45,6 +45,7 @@ RUN go mod download
|
||||
# Copy the go source files
|
||||
COPY ${BFF_SOURCE_CODE}/cmd/ cmd/
|
||||
COPY ${BFF_SOURCE_CODE}/internal/ internal/
|
||||
COPY ${BFF_SOURCE_CODE}/openapi/ openapi/
|
||||
|
||||
# Build the Go application
|
||||
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o /usr/src/app/bff ./cmd
|
||||
|
||||
+19
-2
@@ -52,7 +52,7 @@ test: fmt vet envtest ## Runs the full test suite.
|
||||
go test ./...
|
||||
|
||||
.PHONY: build
|
||||
build: fmt vet test ## Builds the project to produce a binary executable.
|
||||
build: fmt vet openapi test ## Builds the project to produce a binary executable. OpenAPI spec is generated before build.
|
||||
ifeq ($(DEBUG), true) ## If DEBUG is true, build with debugging symbols
|
||||
go build $(GCFLAGS_DEBUG) -o bin/bff ./cmd
|
||||
else
|
||||
@@ -60,7 +60,7 @@ else
|
||||
endif
|
||||
|
||||
.PHONY: run
|
||||
run: fmt vet envtest ## Runs the project.
|
||||
run: fmt vet openapi envtest ## Runs the project. OpenAPI spec is generated before run.
|
||||
trap 'exit 0' INT; \
|
||||
ENVTEST_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \
|
||||
go run ./cmd --port=$(PORT) --auth-method=${AUTH_METHOD} --auth-token-header=$(AUTH_TOKEN_HEADER) --auth-token-prefix="$(AUTH_TOKEN_PREFIX)" --static-assets-dir=$(STATIC_ASSETS_DIR) --mock-k8s-client=$(MOCK_K8S_CLIENT) --mock-mr-client=$(MOCK_MR_CLIENT) --mock-mr-catalog-client=$(MOCK_MR_CATALOG_CLIENT) --dev-mode=$(DEV_MODE) --dev-mode-model-registry-port=$(DEV_MODE_MODEL_REGISTRY_PORT) --dev-mode-catalog-port=$(DEV_MODE_CATALOG_PORT) --deployment-mode=$(DEPLOYMENT_MODE) --log-level=$(LOG_LEVEL) --allowed-origins=$(ALLOWED_ORIGINS) --insecure-skip-verify=$(INSECURE_SKIP_VERIFY)
|
||||
@@ -90,6 +90,23 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||
$(GOLANGCI_LINT): $(LOCALBIN)
|
||||
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,${GOLANGCI_LINT_VERSION})
|
||||
|
||||
SWAG_VERSION ?= v1.16.4
|
||||
SWAG ?= $(LOCALBIN)/swag-$(SWAG_VERSION)
|
||||
|
||||
.PHONY: swag
|
||||
swag: $(SWAG) ## Download swag locally if necessary.
|
||||
$(SWAG): $(LOCALBIN)
|
||||
$(call go-install-tool,$(SWAG),github.com/swaggo/swag/cmd/swag,$(SWAG_VERSION))
|
||||
|
||||
ALL_GO_DIRS := $(shell find . -type f -name '*.go' -exec dirname {} \; | sed 's|^\./||' | sort -u)
|
||||
ALL_GO_DIRS_NO_CMD := $(shell echo "$(ALL_GO_DIRS)" | tr ' ' '\n' | grep -v '^cmd$$' | paste -sd, -)
|
||||
SWAG_DIRS := cmd,$(ALL_GO_DIRS_NO_CMD)
|
||||
|
||||
.PHONY: openapi
|
||||
openapi: swag ## Generate OpenAPI spec from annotations
|
||||
$(SWAG) fmt -g cmd/main.go -d $(SWAG_DIRS)
|
||||
$(SWAG) init --parseDependency -q -g main.go -d $(SWAG_DIRS) -o openapi --outputTypes go,json,yaml --requiredByDefault
|
||||
|
||||
|
||||
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
|
||||
# $1 - target path with name of binary (ideally with version)
|
||||
|
||||
@@ -82,6 +82,8 @@ make docker-build
|
||||
|
||||
See the [OpenAPI specification](../api/openapi/mod-arch.yaml) for a complete list of endpoints.
|
||||
|
||||
> **Note:** We are in the process of moving from a manually maintained OpenAPI spec to a generated one. For a short period we have both: the hand-maintained [mod-arch.yaml](../api/openapi/mod-arch.yaml) and the generated [swagger.yaml](openapi/swagger.yaml) / [swagger.json](openapi/swagger.json) (produced from Go code via swaggo). The goal is to generate the OpenAPI spec from the codebase rather than managing it manually; once the transition is complete, the generated spec will be the single source of truth.
|
||||
|
||||
### Sample local calls
|
||||
|
||||
You will need to inject your requests with a `kubeflow-userid` header and namespace for authorization purposes.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// @title Model Registry BFF REST API
|
||||
// @version 1.0.0
|
||||
// @description REST API for Model Registry BFF
|
||||
// @license.name Apache 2.0
|
||||
// @license.url https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
// @host localhost:4000
|
||||
// @BasePath /api/v1
|
||||
// @schemes http https
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -15,6 +25,8 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "github.com/kubeflow/hub/ui/bff/openapi" // swagger docs for Swagger UI
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -11,6 +11,8 @@ require (
|
||||
github.com/onsi/gomega v1.42.1
|
||||
github.com/rs/cors v1.11.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2
|
||||
github.com/swaggo/swag v1.16.4
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.36.2
|
||||
k8s.io/apimachinery v0.36.2
|
||||
@@ -19,6 +21,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
@@ -30,6 +33,7 @@ require (
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.6 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
@@ -48,6 +52,7 @@ require (
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.1 // indirect
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -6,6 +8,7 @@ github.com/brianvoe/gofakeit/v7 v7.15.0 h1:kGLYAWN8tnmxq2PelKVK6zwpM7kMxdz9SGPH3
|
||||
github.com/brianvoe/gofakeit/v7 v7.15.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -28,10 +31,17 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
|
||||
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
@@ -55,10 +65,16 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
|
||||
@@ -98,8 +114,15 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
|
||||
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2 h1:FKCdLsl+sFCx60KFsyM0rDarwiUSZ8DqbfSyIKC9OBg=
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2/go.mod h1:r7/GBkAWIfK6E/OLnE8fXnviHiDeAHmgIyooa4xm3AQ=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@@ -143,12 +166,19 @@ gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuB
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
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.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY=
|
||||
|
||||
@@ -90,6 +90,10 @@ const (
|
||||
McpServerFilterOptionListPath = McpServerCatalogPathPrefix + "/mcp_servers_filter_options"
|
||||
McpServerPath = McpServerListPath + "/:" + McpServerId
|
||||
McpServersToolListPath = McpServerPath + "/tools"
|
||||
|
||||
// Swagger UI (interactive API docs)
|
||||
SwaggerPath = ApiPathPrefix + "/swagger"
|
||||
SwaggerDocPath = SwaggerPath + "/doc.json"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
@@ -279,6 +283,11 @@ func (app *App) Routes() http.Handler {
|
||||
apiRouter.POST(CheckNamespaceRegistryAccessPath, app.CheckNamespaceRegistryAccessHandler)
|
||||
apiRouter.GET(ModelRegistryListPath, app.AttachNamespace(app.RequireListServiceAccessInNamespace(app.GetAllModelRegistriesHandler)))
|
||||
|
||||
// Swagger UI (interactive API docs) — only in dev mode
|
||||
if app.config.DevMode {
|
||||
apiRouter.GET(SwaggerPath+"/*filepath", app.GetSwaggerHandler)
|
||||
}
|
||||
|
||||
// Enable these routes in all cases except Kubeflow integration mode
|
||||
// (Kubeflow integration mode is when DeploymentMode is kubeflow)
|
||||
isKubeflowIntegrationMode := app.config.DeploymentMode.IsKubeflowMode()
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/kubeflow/hub/ui/bff/internal/config"
|
||||
"github.com/kubeflow/hub/ui/bff/internal/mocks"
|
||||
"github.com/kubeflow/hub/ui/bff/internal/models"
|
||||
"github.com/kubeflow/hub/ui/bff/internal/models/healthcheck"
|
||||
"github.com/kubeflow/hub/ui/bff/internal/repositories"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -35,15 +35,15 @@ func TestHealthCheckHandler(t *testing.T) {
|
||||
body, err := io.ReadAll(rs.Body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var healthCheckRes models.HealthCheckModel
|
||||
var healthCheckRes healthcheck.HealthCheckModel
|
||||
err = json.Unmarshal(body, &healthCheckRes)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
expected := models.HealthCheckModel{
|
||||
expected := healthcheck.HealthCheckModel{
|
||||
Status: "available",
|
||||
SystemInfo: models.SystemInfo{
|
||||
SystemInfo: healthcheck.SystemInfo{
|
||||
Version: Version,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,9 +4,26 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
// imported for swag documentation
|
||||
_ "github.com/kubeflow/hub/ui/bff/internal/models/healthcheck"
|
||||
)
|
||||
|
||||
func (app *App) HealthcheckHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// HealthcheckHandler returns the health status of the application.
|
||||
//
|
||||
// @Summary Returns the health status of the application
|
||||
// @Description Provides a healthcheck response indicating the status of key services.
|
||||
// @Tags healthcheck
|
||||
// @ID getHealthcheck
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} healthcheck.HealthCheckModel "Successful healthcheck response"
|
||||
// @Failure 401 {object} ErrorEnvelope "Unauthorized. Authentication is required."
|
||||
// @Failure 403 {object} ErrorEnvelope "Forbidden. User does not have permission to access the resource."
|
||||
// @Failure 404 {object} ErrorEnvelope "Not Found. Resource does not exist."
|
||||
// @Failure 422 {object} ErrorEnvelope "Unprocessable Entity. Validation error."
|
||||
// @Failure 500 {object} ErrorEnvelope "Internal server error"
|
||||
// @Router /healthcheck [get]
|
||||
func (app *App) HealthcheckHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
healthCheck, err := app.repositories.HealthCheck.HealthCheck(Version)
|
||||
if err != nil {
|
||||
app.serverErrorResponse(w, r, err)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2026.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
httpSwagger "github.com/swaggo/http-swagger/v2"
|
||||
)
|
||||
|
||||
// GetSwaggerHandler serves the Swagger UI for interactive API documentation.
|
||||
func (app *App) GetSwaggerHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpSwagger.Handler(
|
||||
httpSwagger.URL(SwaggerDocPath),
|
||||
httpSwagger.DeepLinking(true),
|
||||
httpSwagger.DocExpansion("list"),
|
||||
httpSwagger.DomID("swagger-ui"),
|
||||
).ServeHTTP(w, r)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package healthcheck
|
||||
|
||||
type SystemInfo struct {
|
||||
Version string `json:"version"`
|
||||
@@ -1,6 +1,6 @@
|
||||
package repositories
|
||||
|
||||
import "github.com/kubeflow/hub/ui/bff/internal/models"
|
||||
import "github.com/kubeflow/hub/ui/bff/internal/models/healthcheck"
|
||||
|
||||
type HealthCheckRepository struct{}
|
||||
|
||||
@@ -8,13 +8,11 @@ func NewHealthCheckRepository() *HealthCheckRepository {
|
||||
return &HealthCheckRepository{}
|
||||
}
|
||||
|
||||
func (r *HealthCheckRepository) HealthCheck(version string) (models.HealthCheckModel, error) {
|
||||
var res = models.HealthCheckModel{
|
||||
func (r *HealthCheckRepository) HealthCheck(version string) (healthcheck.HealthCheckModel, error) {
|
||||
return healthcheck.HealthCheckModel{
|
||||
Status: "available",
|
||||
SystemInfo: models.SystemInfo{
|
||||
SystemInfo: healthcheck.SystemInfo{
|
||||
Version: version,
|
||||
},
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
Generated
+146
@@ -0,0 +1,146 @@
|
||||
// Package openapi Code generated by swaggo/swag. DO NOT EDIT
|
||||
package openapi
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
"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",
|
||||
"operationId": "getHealthcheck",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful healthcheck response",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/healthcheck.HealthCheckModel"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized. Authentication is required.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden. User does not have permission to access the resource.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found. Resource does not exist.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Unprocessable Entity. Validation error.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrorEnvelope": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"$ref": "#/definitions/httpclient.HTTPError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthcheck.HealthCheckModel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"system_info"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"system_info": {
|
||||
"$ref": "#/definitions/healthcheck.SystemInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthcheck.SystemInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"httpclient.HTTPError": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"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: "Model Registry BFF REST API",
|
||||
Description: "REST API for Model Registry BFF",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "REST API for Model Registry BFF",
|
||||
"title": "Model Registry BFF REST API",
|
||||
"contact": {},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
"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",
|
||||
"operationId": "getHealthcheck",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful healthcheck response",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/healthcheck.HealthCheckModel"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized. Authentication is required.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden. User does not have permission to access the resource.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found. Resource does not exist.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Unprocessable Entity. Validation error.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrorEnvelope": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"$ref": "#/definitions/httpclient.HTTPError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthcheck.HealthCheckModel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"system_info"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"system_info": {
|
||||
"$ref": "#/definitions/healthcheck.SystemInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthcheck.SystemInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"httpclient.HTTPError": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
basePath: /api/v1
|
||||
definitions:
|
||||
api.ErrorEnvelope:
|
||||
properties:
|
||||
error:
|
||||
$ref: '#/definitions/httpclient.HTTPError'
|
||||
required:
|
||||
- error
|
||||
type: object
|
||||
healthcheck.HealthCheckModel:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
system_info:
|
||||
$ref: '#/definitions/healthcheck.SystemInfo'
|
||||
required:
|
||||
- status
|
||||
- system_info
|
||||
type: object
|
||||
healthcheck.SystemInfo:
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
required:
|
||||
- version
|
||||
type: object
|
||||
httpclient.HTTPError:
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
type: object
|
||||
host: localhost:4000
|
||||
info:
|
||||
contact: {}
|
||||
description: REST API for Model Registry BFF
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: https://www.apache.org/licenses/LICENSE-2.0
|
||||
title: Model Registry BFF REST API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/healthcheck:
|
||||
get:
|
||||
description: Provides a healthcheck response indicating the status of key services.
|
||||
operationId: getHealthcheck
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful healthcheck response
|
||||
schema:
|
||||
$ref: '#/definitions/healthcheck.HealthCheckModel'
|
||||
"401":
|
||||
description: Unauthorized. Authentication is required.
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorEnvelope'
|
||||
"403":
|
||||
description: Forbidden. User does not have permission to access the resource.
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorEnvelope'
|
||||
"404":
|
||||
description: Not Found. Resource does not exist.
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorEnvelope'
|
||||
"422":
|
||||
description: Unprocessable Entity. Validation error.
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorEnvelope'
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorEnvelope'
|
||||
summary: Returns the health status of the application
|
||||
tags:
|
||||
- healthcheck
|
||||
schemes:
|
||||
- http
|
||||
- https
|
||||
swagger: "2.0"
|
||||
Reference in New Issue
Block a user