From 634b58e6857f825f520659a143c98045fa5cebf6 Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Tue, 23 May 2023 15:19:13 +0530 Subject: [PATCH 1/7] Implement HTTP Server based on OpenAPI spec Signed-off-by: Parthvi Vala Co-authored-by: Armel Soro Co-authored-by: Philippe Martin --- Makefile | 11 +- ododevapispec.yaml | 211 ++++++++++++++++ pkg/apiserver-gen/.openapi-generator-ignore | 23 ++ pkg/apiserver-gen/.openapi-generator/FILES | 15 ++ pkg/apiserver-gen/.openapi-generator/VERSION | 1 + pkg/apiserver-gen/README.md | 34 +++ pkg/apiserver-gen/api/openapi.yaml | 184 ++++++++++++++ pkg/apiserver-gen/go/api.go | 39 +++ pkg/apiserver-gen/go/api_default.go | 141 +++++++++++ pkg/apiserver-gen/go/error.go | 62 +++++ pkg/apiserver-gen/go/helpers.go | 54 +++++ pkg/apiserver-gen/go/impl.go | 16 ++ pkg/apiserver-gen/go/logger.go | 32 +++ .../model__component_command_post_request.go | 33 +++ .../go/model__component_get_200_response.go | 33 +++ .../go/model__instance_get_200_response.go | 36 +++ pkg/apiserver-gen/go/model_general_error.go | 32 +++ pkg/apiserver-gen/go/model_general_success.go | 32 +++ pkg/apiserver-gen/go/routers.go | 229 ++++++++++++++++++ pkg/apiserver-impl/api_default_service.go | 72 ++++++ 20 files changed, 1286 insertions(+), 4 deletions(-) create mode 100644 ododevapispec.yaml create mode 100644 pkg/apiserver-gen/.openapi-generator-ignore create mode 100644 pkg/apiserver-gen/.openapi-generator/FILES create mode 100644 pkg/apiserver-gen/.openapi-generator/VERSION create mode 100644 pkg/apiserver-gen/README.md create mode 100644 pkg/apiserver-gen/api/openapi.yaml create mode 100644 pkg/apiserver-gen/go/api.go create mode 100644 pkg/apiserver-gen/go/api_default.go create mode 100644 pkg/apiserver-gen/go/error.go create mode 100644 pkg/apiserver-gen/go/helpers.go create mode 100644 pkg/apiserver-gen/go/impl.go create mode 100644 pkg/apiserver-gen/go/logger.go create mode 100644 pkg/apiserver-gen/go/model__component_command_post_request.go create mode 100644 pkg/apiserver-gen/go/model__component_get_200_response.go create mode 100644 pkg/apiserver-gen/go/model__instance_get_200_response.go create mode 100644 pkg/apiserver-gen/go/model_general_error.go create mode 100644 pkg/apiserver-gen/go/model_general_success.go create mode 100644 pkg/apiserver-gen/go/routers.go create mode 100644 pkg/apiserver-impl/api_default_service.go diff --git a/Makefile b/Makefile index 51a49129103..1abae585fca 100644 --- a/Makefile +++ b/Makefile @@ -151,10 +151,6 @@ cross: ## compile for multiple platforms generate-cli-structure: go run cmd/cli-doc/cli-doc.go structure -.PHONY: generate-cli-reference -generate-cli-reference: - go run cmd/cli-doc/cli-doc.go reference > docs/cli-reference.adoc - # run make cross before this! .PHONY: prepare-release prepare-release: cross ## create gzipped binaries in ./dist/release/ for uploading to GitHub release page @@ -232,3 +228,10 @@ test-e2e: .PHONY: test-doc-automation test-doc-automation: $(RUN_GINKGO) $(GINKGO_FLAGS_ONE) --junit-report="test-doc-automation.xml" tests/documentation/... + + +# Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen; this will only generate interfaces +# Actual implementation must be done inside pkg/apiserver-impl +.PHONY: generate-apiserver +generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen + openapi-generator generate -i ododevapispec.yaml -g go-server -o pkg/apiserver-gen --additional-properties=outputAsLibrary=true,onlyInterfaces=true diff --git a/ododevapispec.yaml b/ododevapispec.yaml new file mode 100644 index 00000000000..4f2257ceffe --- /dev/null +++ b/ododevapispec.yaml @@ -0,0 +1,211 @@ +openapi: '3.0.2' +info: + title: odo dev + version: '0.1' + description: API interface for 'odo dev' +paths: + /instance: + get: + description: Get information about the this 'odo dev' instance. + responses: + '200': + description: Information about the this 'odo dev' instance. + content: + application/json: + schema: + type: object + properties: + componentDirectory: + type: string + description: Directory on which this 'odo dev' instance is running + pid: + type: integer + description: PID of the this 'odo dev' instance. + example: + componentDirectory: "/Users/user/Documents/myproject" + pid: 42 + + delete: + description: "Stop this 'odo dev' instance" + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/GeneralSuccess' + example: + message: "'odo dev' instance with pid: 42 is shuting down." + description: "'odo dev' instance will shutdown." + + /component: + get: + description: Get the Information about the component controlled by this 'odo dev' instance. + responses: + '200': + description: Information about the component. + content: + application/json: + schema: + type: object + properties: + component: + type: object + description: Description of the component. This is the same as output of 'odo describe component -o json' + example: + { + "devfilePath": "/home/tomas/Code/odo-examples/java-maven/devfile.yaml", + "devfileData": { + "devfile": { + "schemaVersion": "2.1.0", + "metadata": { + "name": "demo", + "version": "1.1.1", + "displayName": "Maven Java", + "description": "Upstream Maven and OpenJDK 11", + "tags": [ + "Java", + "Maven" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg", + "projectType": "Maven", + "language": "Java" + }, + "components": [ + { + "name": "tools", + "container": { + "image": "quay.io/eclipse/che-java11-maven:next", + "env": [ + { + "name": "DEBUG_PORT", + "value": "5858" + } + ], + "volumeMounts": [ + { + "name": "m2", + "path": "/home/user/.m2" + } + ], + "memoryLimit": "512Mi", + "mountSources": true, + "dedicatedPod": false, + "endpoints": [ + { + "name": "http-maven", + "targetPort": 8080, + "secure": false + } + ] + } + }, + { + "name": "m2", + "volume": { + "ephemeral": false + } + } + ], + "starterProjects": [ + { + "name": "springbootproject", + "git": { + "remotes": { + "origin": "https://github.com/odo-devfiles/springboot-ex.git" + } + } + } + ], + "commands": [ + { + "id": "mvn-package", + "exec": { + "group": { + "kind": "build", + "isDefault": true + }, + "commandLine": "mvn -Dmaven.repo.local=/home/user/.m2/repository package", + "component": "tools", + "workingDir": "${PROJECT_SOURCE}", + "hotReloadCapable": false + } + }, + { + "id": "run", + "exec": { + "group": { + "kind": "run", + "isDefault": true + }, + "commandLine": "java -jar target/*.jar", + "component": "tools", + "workingDir": "${PROJECT_SOURCE}", + "hotReloadCapable": false + } + }, + { + "id": "debug", + "exec": { + "group": { + "kind": "debug", + "isDefault": true + }, + "commandLine": "java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar", + "component": "tools", + "workingDir": "${PROJECT_SOURCE}", + "hotReloadCapable": false + } + } + ] + }, + "supportedOdoFeatures": { + "dev": true, + "deploy": false, + "debug": true + } + }, + "runningIn": { + "deploy": false, + "dev": true + }, + "managedBy": "odo" + } + + /component/command: + post: + description: Instruct 'odo dev' to perform given command on the component + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + description: Name of the command that should be executed + type: string + enum: + - "push" + example: + action: push + responses: + '200': + description: command was successfully executed + content: + application/json: + schema: + $ref: '#/components/schemas/GeneralSuccess' + example: + message: "push was successfully executed" + +components: + schemas: + GeneralSuccess: + type: object + properties: + message: + type: string + GeneralError: + type: object + properties: + message: + type: string \ No newline at end of file diff --git a/pkg/apiserver-gen/.openapi-generator-ignore b/pkg/apiserver-gen/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/pkg/apiserver-gen/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/pkg/apiserver-gen/.openapi-generator/FILES b/pkg/apiserver-gen/.openapi-generator/FILES new file mode 100644 index 00000000000..8291ee9ec8f --- /dev/null +++ b/pkg/apiserver-gen/.openapi-generator/FILES @@ -0,0 +1,15 @@ +README.md +api/openapi.yaml +go/api.go +go/api_default.go +go/api_default_service.go +go/error.go +go/helpers.go +go/impl.go +go/logger.go +go/model__component_command_post_request.go +go/model__component_get_200_response.go +go/model__instance_get_200_response.go +go/model_general_error.go +go/model_general_success.go +go/routers.go diff --git a/pkg/apiserver-gen/.openapi-generator/VERSION b/pkg/apiserver-gen/.openapi-generator/VERSION new file mode 100644 index 00000000000..4be2c727ad9 --- /dev/null +++ b/pkg/apiserver-gen/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.5.0 \ No newline at end of file diff --git a/pkg/apiserver-gen/README.md b/pkg/apiserver-gen/README.md new file mode 100644 index 00000000000..cc82bc1e8b3 --- /dev/null +++ b/pkg/apiserver-gen/README.md @@ -0,0 +1,34 @@ +# Go API Server for openapi + +API interface for 'odo dev' + +## Overview +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. +By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. +- + +To see how to make this your own, look here: + +[README](https://openapi-generator.tech) + +- API version: 0.1 +- Build date: 2023-05-23T15:06:41.821542+05:30[Asia/Kolkata] + + +### Running the server +To run the server, follow these simple steps: + +``` +go run main.go +``` + +To run the server in a docker container +``` +docker build --network=host -t openapi . +``` + +Once image is built use +``` +docker run --rm -it openapi +``` diff --git a/pkg/apiserver-gen/api/openapi.yaml b/pkg/apiserver-gen/api/openapi.yaml new file mode 100644 index 00000000000..f4cd8140bc5 --- /dev/null +++ b/pkg/apiserver-gen/api/openapi.yaml @@ -0,0 +1,184 @@ +openapi: 3.0.2 +info: + description: API interface for 'odo dev' + title: odo dev + version: "0.1" +servers: +- url: / +paths: + /instance: + delete: + description: Stop this 'odo dev' instance + responses: + "200": + content: + application/json: + example: + message: "'odo dev' instance with pid: 42 is shuting down." + schema: + $ref: '#/components/schemas/GeneralSuccess' + description: '''odo dev'' instance will shutdown.' + get: + description: Get information about the this 'odo dev' instance. + responses: + "200": + content: + application/json: + example: + componentDirectory: /Users/user/Documents/myproject + pid: 42 + schema: + $ref: '#/components/schemas/_instance_get_200_response' + description: Information about the this 'odo dev' instance. + /component: + get: + description: Get the Information about the component controlled by this 'odo + dev' instance. + responses: + "200": + content: + application/json: + example: + devfilePath: /home/tomas/Code/odo-examples/java-maven/devfile.yaml + devfileData: + devfile: + schemaVersion: 2.1.0 + metadata: + name: demo + version: 1.1.1 + displayName: Maven Java + description: Upstream Maven and OpenJDK 11 + tags: + - Java + - Maven + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg + projectType: Maven + language: Java + components: + - name: tools + container: + image: quay.io/eclipse/che-java11-maven:next + env: + - name: DEBUG_PORT + value: "5858" + volumeMounts: + - name: m2 + path: /home/user/.m2 + memoryLimit: 512Mi + mountSources: true + dedicatedPod: false + endpoints: + - name: http-maven + targetPort: 8080 + secure: false + - name: m2 + volume: + ephemeral: false + starterProjects: + - name: springbootproject + git: + remotes: + origin: https://github.com/odo-devfiles/springboot-ex.git + commands: + - id: mvn-package + exec: + group: + kind: build + isDefault: true + commandLine: mvn -Dmaven.repo.local=/home/user/.m2/repository + package + component: tools + workingDir: "${PROJECT_SOURCE}" + hotReloadCapable: false + - id: run + exec: + group: + kind: run + isDefault: true + commandLine: java -jar target/*.jar + component: tools + workingDir: "${PROJECT_SOURCE}" + hotReloadCapable: false + - id: debug + exec: + group: + kind: debug + isDefault: true + commandLine: "java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n\ + \ -jar target/*.jar" + component: tools + workingDir: "${PROJECT_SOURCE}" + hotReloadCapable: false + supportedOdoFeatures: + dev: true + deploy: false + debug: true + runningIn: + deploy: false + dev: true + managedBy: odo + schema: + $ref: '#/components/schemas/_component_get_200_response' + description: Information about the component. + /component/command: + post: + description: Instruct 'odo dev' to perform given command on the component + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/_component_command_post_request' + responses: + "200": + content: + application/json: + example: + message: push was successfully executed + schema: + $ref: '#/components/schemas/GeneralSuccess' + description: command was successfully executed +components: + schemas: + GeneralSuccess: + example: + message: message + properties: + message: + type: string + type: object + GeneralError: + properties: + message: + type: string + type: object + _instance_get_200_response: + example: + componentDirectory: componentDirectory + pid: 0 + properties: + componentDirectory: + description: Directory on which this 'odo dev' instance is running + type: string + pid: + description: PID of the this 'odo dev' instance. + type: integer + type: object + _component_get_200_response: + example: + component: "{}" + properties: + component: + description: Description of the component. This is the same as output of + 'odo describe component -o json' + type: object + type: object + _component_command_post_request: + example: + action: push + properties: + name: + description: Name of the command that should be executed + enum: + - push + type: string + type: object diff --git a/pkg/apiserver-gen/go/api.go b/pkg/apiserver-gen/go/api.go new file mode 100644 index 00000000000..400cd16e71b --- /dev/null +++ b/pkg/apiserver-gen/go/api.go @@ -0,0 +1,39 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "context" + "net/http" +) + + + +// DefaultApiRouter defines the required methods for binding the api requests to a responses for the DefaultApi +// The DefaultApiRouter implementation should parse necessary information from the http request, +// pass the data to a DefaultApiServicer to perform the required actions, then write the service results to the http response. +type DefaultApiRouter interface { + ComponentCommandPost(http.ResponseWriter, *http.Request) + ComponentGet(http.ResponseWriter, *http.Request) + InstanceDelete(http.ResponseWriter, *http.Request) + InstanceGet(http.ResponseWriter, *http.Request) +} + + +// DefaultApiServicer defines the api actions for the DefaultApi service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can be ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type DefaultApiServicer interface { + ComponentCommandPost(context.Context, ComponentCommandPostRequest) (ImplResponse, error) + ComponentGet(context.Context) (ImplResponse, error) + InstanceDelete(context.Context) (ImplResponse, error) + InstanceGet(context.Context) (ImplResponse, error) +} diff --git a/pkg/apiserver-gen/go/api_default.go b/pkg/apiserver-gen/go/api_default.go new file mode 100644 index 00000000000..e48e1bbaa89 --- /dev/null +++ b/pkg/apiserver-gen/go/api_default.go @@ -0,0 +1,141 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/gorilla/mux" +) + +// DefaultApiController binds http requests to an api service and writes the service results to the http response +type DefaultApiController struct { + service DefaultApiServicer + errorHandler ErrorHandler +} + +// DefaultApiOption for how the controller is set up. +type DefaultApiOption func(*DefaultApiController) + +// WithDefaultApiErrorHandler inject ErrorHandler into controller +func WithDefaultApiErrorHandler(h ErrorHandler) DefaultApiOption { + return func(c *DefaultApiController) { + c.errorHandler = h + } +} + +// NewDefaultApiController creates a default api controller +func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Router { + controller := &DefaultApiController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the DefaultApiController +func (c *DefaultApiController) Routes() Routes { + return Routes{ + { + "ComponentCommandPost", + strings.ToUpper("Post"), + "/component/command", + c.ComponentCommandPost, + }, + { + "ComponentGet", + strings.ToUpper("Get"), + "/component", + c.ComponentGet, + }, + { + "InstanceDelete", + strings.ToUpper("Delete"), + "/instance", + c.InstanceDelete, + }, + { + "InstanceGet", + strings.ToUpper("Get"), + "/instance", + c.InstanceGet, + }, + } +} + +// ComponentCommandPost - +func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *http.Request) { + componentCommandPostRequestParam := ComponentCommandPostRequest{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&componentCommandPostRequestParam); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertComponentCommandPostRequestRequired(componentCommandPostRequestParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.ComponentCommandPost(r.Context(), componentCommandPostRequestParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// ComponentGet - +func (c *DefaultApiController) ComponentGet(w http.ResponseWriter, r *http.Request) { + result, err := c.service.ComponentGet(r.Context()) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// InstanceDelete - +func (c *DefaultApiController) InstanceDelete(w http.ResponseWriter, r *http.Request) { + result, err := c.service.InstanceDelete(r.Context()) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// InstanceGet - +func (c *DefaultApiController) InstanceGet(w http.ResponseWriter, r *http.Request) { + result, err := c.service.InstanceGet(r.Context()) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} diff --git a/pkg/apiserver-gen/go/error.go b/pkg/apiserver-gen/go/error.go new file mode 100644 index 00000000000..7561a1df2fd --- /dev/null +++ b/pkg/apiserver-gen/go/error.go @@ -0,0 +1,62 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "errors" + "fmt" + "net/http" +) + +var ( + // ErrTypeAssertionError is thrown when type an interface does not match the asserted type + ErrTypeAssertionError = errors.New("unable to assert type") +) + +// ParsingError indicates that an error has occurred when parsing request parameters +type ParsingError struct { + Err error +} + +func (e *ParsingError) Unwrap() error { + return e.Err +} + +func (e *ParsingError) Error() string { + return e.Err.Error() +} + +// RequiredError indicates that an error has occurred when parsing request parameters +type RequiredError struct { + Field string +} + +func (e *RequiredError) Error() string { + return fmt.Sprintf("required field '%s' is zero value.", e.Field) +} + +// ErrorHandler defines the required method for handling error. You may implement it and inject this into a controller if +// you would like errors to be handled differently from the DefaultErrorHandler +type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) + +// DefaultErrorHandler defines the default logic on how to handle errors from the controller. Any errors from parsing +// request params will return a StatusBadRequest. Otherwise, the error code originating from the servicer will be used. +func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) { + if _, ok := err.(*ParsingError); ok { + // Handle parsing errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest), w) + } else if _, ok := err.(*RequiredError); ok { + // Handle missing required errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity), w) + } else { + // Handle all other errors + EncodeJSONResponse(err.Error(), &result.Code, w) + } +} diff --git a/pkg/apiserver-gen/go/helpers.go b/pkg/apiserver-gen/go/helpers.go new file mode 100644 index 00000000000..ea45946a8f8 --- /dev/null +++ b/pkg/apiserver-gen/go/helpers.go @@ -0,0 +1,54 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "reflect" +) + +// Response return a ImplResponse struct filled +func Response(code int, body interface{}) ImplResponse { + return ImplResponse { + Code: code, + Body: body, + } +} + +// IsZeroValue checks if the val is the zero-ed value. +func IsZeroValue(val interface{}) bool { + return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) +} + +// AssertRecurseInterfaceRequired recursively checks each struct in a slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { + return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) +} + +// AssertRecurseValueRequired checks each struct in the nested slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { + switch value.Kind() { + // If it is a struct we check using callback + case reflect.Struct: + if err := callback(value.Interface()); err != nil { + return err + } + + // If it is a slice we continue recursion + case reflect.Slice: + for i := 0; i < value.Len(); i += 1 { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/apiserver-gen/go/impl.go b/pkg/apiserver-gen/go/impl.go new file mode 100644 index 00000000000..9ca7add4e99 --- /dev/null +++ b/pkg/apiserver-gen/go/impl.go @@ -0,0 +1,16 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +// ImplResponse response defines an error code with the associated body +type ImplResponse struct { + Code int + Body interface{} +} diff --git a/pkg/apiserver-gen/go/logger.go b/pkg/apiserver-gen/go/logger.go new file mode 100644 index 00000000000..5f54be8c3d7 --- /dev/null +++ b/pkg/apiserver-gen/go/logger.go @@ -0,0 +1,32 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "log" + "net/http" + "time" +) + +func Logger(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + inner.ServeHTTP(w, r) + + log.Printf( + "%s %s %s %s", + r.Method, + r.RequestURI, + name, + time.Since(start), + ) + }) +} diff --git a/pkg/apiserver-gen/go/model__component_command_post_request.go b/pkg/apiserver-gen/go/model__component_command_post_request.go new file mode 100644 index 00000000000..26e4fc63be3 --- /dev/null +++ b/pkg/apiserver-gen/go/model__component_command_post_request.go @@ -0,0 +1,33 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +type ComponentCommandPostRequest struct { + + // Name of the command that should be executed + Name string `json:"name,omitempty"` +} + +// AssertComponentCommandPostRequestRequired checks if the required fields are not zero-ed +func AssertComponentCommandPostRequestRequired(obj ComponentCommandPostRequest) error { + return nil +} + +// AssertRecurseComponentCommandPostRequestRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ComponentCommandPostRequest (e.g. [][]ComponentCommandPostRequest), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseComponentCommandPostRequestRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aComponentCommandPostRequest, ok := obj.(ComponentCommandPostRequest) + if !ok { + return ErrTypeAssertionError + } + return AssertComponentCommandPostRequestRequired(aComponentCommandPostRequest) + }) +} diff --git a/pkg/apiserver-gen/go/model__component_get_200_response.go b/pkg/apiserver-gen/go/model__component_get_200_response.go new file mode 100644 index 00000000000..fc416bd3848 --- /dev/null +++ b/pkg/apiserver-gen/go/model__component_get_200_response.go @@ -0,0 +1,33 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +type ComponentGet200Response struct { + + // Description of the component. This is the same as output of 'odo describe component -o json' + Component map[string]interface{} `json:"component,omitempty"` +} + +// AssertComponentGet200ResponseRequired checks if the required fields are not zero-ed +func AssertComponentGet200ResponseRequired(obj ComponentGet200Response) error { + return nil +} + +// AssertRecurseComponentGet200ResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ComponentGet200Response (e.g. [][]ComponentGet200Response), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseComponentGet200ResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aComponentGet200Response, ok := obj.(ComponentGet200Response) + if !ok { + return ErrTypeAssertionError + } + return AssertComponentGet200ResponseRequired(aComponentGet200Response) + }) +} diff --git a/pkg/apiserver-gen/go/model__instance_get_200_response.go b/pkg/apiserver-gen/go/model__instance_get_200_response.go new file mode 100644 index 00000000000..1485bd19b4e --- /dev/null +++ b/pkg/apiserver-gen/go/model__instance_get_200_response.go @@ -0,0 +1,36 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +type InstanceGet200Response struct { + + // Directory on which this 'odo dev' instance is running + ComponentDirectory string `json:"componentDirectory,omitempty"` + + // PID of the this 'odo dev' instance. + Pid int32 `json:"pid,omitempty"` +} + +// AssertInstanceGet200ResponseRequired checks if the required fields are not zero-ed +func AssertInstanceGet200ResponseRequired(obj InstanceGet200Response) error { + return nil +} + +// AssertRecurseInstanceGet200ResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of InstanceGet200Response (e.g. [][]InstanceGet200Response), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseInstanceGet200ResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aInstanceGet200Response, ok := obj.(InstanceGet200Response) + if !ok { + return ErrTypeAssertionError + } + return AssertInstanceGet200ResponseRequired(aInstanceGet200Response) + }) +} diff --git a/pkg/apiserver-gen/go/model_general_error.go b/pkg/apiserver-gen/go/model_general_error.go new file mode 100644 index 00000000000..146860b1f20 --- /dev/null +++ b/pkg/apiserver-gen/go/model_general_error.go @@ -0,0 +1,32 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +type GeneralError struct { + + Message string `json:"message,omitempty"` +} + +// AssertGeneralErrorRequired checks if the required fields are not zero-ed +func AssertGeneralErrorRequired(obj GeneralError) error { + return nil +} + +// AssertRecurseGeneralErrorRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of GeneralError (e.g. [][]GeneralError), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseGeneralErrorRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aGeneralError, ok := obj.(GeneralError) + if !ok { + return ErrTypeAssertionError + } + return AssertGeneralErrorRequired(aGeneralError) + }) +} diff --git a/pkg/apiserver-gen/go/model_general_success.go b/pkg/apiserver-gen/go/model_general_success.go new file mode 100644 index 00000000000..82baf61ca74 --- /dev/null +++ b/pkg/apiserver-gen/go/model_general_success.go @@ -0,0 +1,32 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +type GeneralSuccess struct { + + Message string `json:"message,omitempty"` +} + +// AssertGeneralSuccessRequired checks if the required fields are not zero-ed +func AssertGeneralSuccessRequired(obj GeneralSuccess) error { + return nil +} + +// AssertRecurseGeneralSuccessRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of GeneralSuccess (e.g. [][]GeneralSuccess), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseGeneralSuccessRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aGeneralSuccess, ok := obj.(GeneralSuccess) + if !ok { + return ErrTypeAssertionError + } + return AssertGeneralSuccessRequired(aGeneralSuccess) + }) +} diff --git a/pkg/apiserver-gen/go/routers.go b/pkg/apiserver-gen/go/routers.go new file mode 100644 index 00000000000..9f33fab4241 --- /dev/null +++ b/pkg/apiserver-gen/go/routers.go @@ -0,0 +1,229 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" + "errors" + "github.com/gorilla/mux" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "strconv" + "strings" +) + +// A Route defines the parameters for an api endpoint +type Route struct { + Name string + Method string + Pattern string + HandlerFunc http.HandlerFunc +} + +// Routes are a collection of defined api endpoints +type Routes []Route + +// Router defines the required methods for retrieving api routes +type Router interface { + Routes() Routes +} + +const errMsgRequiredMissing = "required parameter is missing" + +// NewRouter creates a new router for any number of api routers +func NewRouter(routers ...Router) *mux.Router { + router := mux.NewRouter().StrictSlash(true) + for _, api := range routers { + for _, route := range api.Routes() { + var handler http.Handler + handler = route.HandlerFunc + handler = Logger(handler, route.Name) + + router. + Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(handler) + } + } + + return router +} + +// EncodeJSONResponse uses the json encoder to write an interface to the http response with an optional status code +func EncodeJSONResponse(i interface{}, status *int, w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + if status != nil { + w.WriteHeader(*status) + } else { + w.WriteHeader(http.StatusOK) + } + + if i != nil { + return json.NewEncoder(w).Encode(i) + } + + return nil +} + +// ReadFormFileToTempFile reads file data from a request form and writes it to a temporary file +func ReadFormFileToTempFile(r *http.Request, key string) (*os.File, error) { + _, fileHeader, err := r.FormFile(key) + if err != nil { + return nil, err + } + + return readFileHeaderToTempFile(fileHeader) +} + +// ReadFormFilesToTempFiles reads files array data from a request form and writes it to a temporary files +func ReadFormFilesToTempFiles(r *http.Request, key string) ([]*os.File, error) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + + files := make([]*os.File, 0, len(r.MultipartForm.File[key])) + + for _, fileHeader := range r.MultipartForm.File[key] { + file, err := readFileHeaderToTempFile(fileHeader) + if err != nil { + return nil, err + } + + files = append(files, file) + } + + return files, nil +} + +// readFileHeaderToTempFile reads multipart.FileHeader and writes it to a temporary file +func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error) { + formFile, err := fileHeader.Open() + if err != nil { + return nil, err + } + + defer formFile.Close() + + fileBytes, err := ioutil.ReadAll(formFile) + if err != nil { + return nil, err + } + + file, err := ioutil.TempFile("", fileHeader.Filename) + if err != nil { + return nil, err + } + + defer file.Close() + + file.Write(fileBytes) + + return file, nil +} + +// parseInt64Parameter parses a string parameter to an int64. +func parseInt64Parameter(param string, required bool) (int64, error) { + if param == "" { + if required { + return 0, errors.New(errMsgRequiredMissing) + } + + return 0, nil + } + + return strconv.ParseInt(param, 10, 64) +} + +// parseInt32Parameter parses a string parameter to an int32. +func parseInt32Parameter(param string, required bool) (int32, error) { + if param == "" { + if required { + return 0, errors.New(errMsgRequiredMissing) + } + + return 0, nil + } + + val, err := strconv.ParseInt(param, 10, 32) + if err != nil { + return -1, err + } + + return int32(val), nil +} + +// parseBoolParameter parses a string parameter to a bool +func parseBoolParameter(param string, required bool) (bool, error) { + if param == "" { + if required { + return false, errors.New(errMsgRequiredMissing) + } + + return false, nil + } + + val, err := strconv.ParseBool(param) + if err != nil { + return false, err + } + + return bool(val), nil +} + +// parseInt64ArrayParameter parses a string parameter containing array of values to []int64. +func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) { + if param == "" { + if required { + return nil, errors.New(errMsgRequiredMissing) + } + + return nil, nil + } + + str := strings.Split(param, delim) + ints := make([]int64, len(str)) + + for i, s := range str { + if v, err := strconv.ParseInt(s, 10, 64); err != nil { + return nil, err + } else { + ints[i] = v + } + } + + return ints, nil +} + +// parseInt32ArrayParameter parses a string parameter containing array of values to []int32. +func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) { + if param == "" { + if required { + return nil, errors.New(errMsgRequiredMissing) + } + + return nil, nil + } + + str := strings.Split(param, delim) + ints := make([]int32, len(str)) + + for i, s := range str { + if v, err := strconv.ParseInt(s, 10, 32); err != nil { + return nil, err + } else { + ints[i] = int32(v) + } + } + + return ints, nil +} diff --git a/pkg/apiserver-impl/api_default_service.go b/pkg/apiserver-impl/api_default_service.go new file mode 100644 index 00000000000..76b41c7937e --- /dev/null +++ b/pkg/apiserver-impl/api_default_service.go @@ -0,0 +1,72 @@ +/* + * odo dev + * + * API interface for 'odo dev' + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package apiserver_impl + +import ( + "context" + "errors" + openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go" + "net/http" +) + +// DefaultApiService is a service that implements the logic for the DefaultApiServicer +// This service should implement the business logic for every endpoint for the DefaultApi API. +// Include any external packages or services that will be required by this service. +type DefaultApiService struct { +} + +// NewDefaultApiService creates a default api service +func NewDefaultApiService() openapi.DefaultApiServicer { + return &DefaultApiService{} +} + +// ComponentCommandPost - +func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) { + // TODO - update ComponentCommandPost with the required logic for this service method. + // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ... + //return Response(200, GeneralSuccess{}), nil + + return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentCommandPost method not implemented") +} + +// ComponentGet - +func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) { + // TODO - update ComponentGet with the required logic for this service method. + // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, ComponentGet200Response{}) or use other options such as http.Ok ... + //return Response(200, ComponentGet200Response{}), nil + + return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentGet method not implemented") +} + +// InstanceDelete - +func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) { + // TODO - update InstanceDelete with the required logic for this service method. + // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ... + //return Response(200, GeneralSuccess{}), nil + + return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceDelete method not implemented") +} + +// InstanceGet - +func (s *DefaultApiService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) { + // TODO - update InstanceGet with the required logic for this service method. + // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, InstanceGet200Response{}) or use other options such as http.Ok ... + //return Response(200, InstanceGet200Response{}), nil + + return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceGet method not implemented") +} From 55b719e794689a01d3fea6cf968944ab310068e7 Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Fri, 26 May 2023 16:25:50 +0530 Subject: [PATCH 2/7] Starter server when odo dev starts Signed-off-by: Parthvi Vala --- go.mod | 2 +- pkg/apiserver-gen/.openapi-generator/FILES | 1 - pkg/apiserver-gen/README.md | 2 +- pkg/apiserver-gen/go/api_default.go | 2 +- pkg/apiserver-impl/api_default_service.go | 9 ------ pkg/apiserver-impl/starterserver.go | 37 ++++++++++++++++++++++ pkg/odo/cli/dev/dev.go | 3 ++ 7 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 pkg/apiserver-impl/starterserver.go diff --git a/go.mod b/go.mod index b53fc02e788..fa4a2c5f557 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/go-openapi/spec v0.20.8 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.9 + github.com/gorilla/mux v1.8.0 github.com/jedib0t/go-pretty/v6 v6.4.3 github.com/kubernetes-sigs/service-catalog v0.3.1 github.com/mattn/go-colorable v0.1.13 @@ -126,7 +127,6 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gookit/color v1.5.2 // indirect - github.com/gorilla/mux v1.8.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect diff --git a/pkg/apiserver-gen/.openapi-generator/FILES b/pkg/apiserver-gen/.openapi-generator/FILES index 8291ee9ec8f..dcaf4f3948c 100644 --- a/pkg/apiserver-gen/.openapi-generator/FILES +++ b/pkg/apiserver-gen/.openapi-generator/FILES @@ -2,7 +2,6 @@ README.md api/openapi.yaml go/api.go go/api_default.go -go/api_default_service.go go/error.go go/helpers.go go/impl.go diff --git a/pkg/apiserver-gen/README.md b/pkg/apiserver-gen/README.md index cc82bc1e8b3..fdf52e2ecd7 100644 --- a/pkg/apiserver-gen/README.md +++ b/pkg/apiserver-gen/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://openapi-generator.tech) - API version: 0.1 -- Build date: 2023-05-23T15:06:41.821542+05:30[Asia/Kolkata] +- Build date: 2023-05-26T15:33:42.589940+05:30[Asia/Kolkata] ### Running the server diff --git a/pkg/apiserver-gen/go/api_default.go b/pkg/apiserver-gen/go/api_default.go index e48e1bbaa89..b7a0fc508d4 100644 --- a/pkg/apiserver-gen/go/api_default.go +++ b/pkg/apiserver-gen/go/api_default.go @@ -14,7 +14,7 @@ import ( "net/http" "strings" - "github.com/gorilla/mux" + // "github.com/gorilla/mux" ) // DefaultApiController binds http requests to an api service and writes the service results to the http response diff --git a/pkg/apiserver-impl/api_default_service.go b/pkg/apiserver-impl/api_default_service.go index 76b41c7937e..40471fc35f2 100644 --- a/pkg/apiserver-impl/api_default_service.go +++ b/pkg/apiserver-impl/api_default_service.go @@ -1,12 +1,3 @@ -/* - * odo dev - * - * API interface for 'odo dev' - * - * API version: 0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - package apiserver_impl import ( diff --git a/pkg/apiserver-impl/starterserver.go b/pkg/apiserver-impl/starterserver.go new file mode 100644 index 00000000000..436eb49c5b2 --- /dev/null +++ b/pkg/apiserver-impl/starterserver.go @@ -0,0 +1,37 @@ +package apiserver_impl + +import ( + "context" + openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go" + "k8s.io/klog" + "log" + "net/http" +) + +func StartServer(ctx context.Context, cancelFunc context.CancelFunc) { + log.Printf("Server started") + + DefaultApiService := NewDefaultApiService() + DefaultApiController := openapi.NewDefaultApiController(DefaultApiService) + + router := openapi.NewRouter(DefaultApiController) + server := &http.Server{Addr: ":20000", Handler: router} + var errChan = make(chan error) + go func() { + err := server.ListenAndServe() + errChan <- err + }() + go func() { + select { + case <-ctx.Done(): + err := server.Shutdown(ctx) + klog.V(1).Infof("Shutting down the server: %v", ctx.Err()) + if err != nil { + klog.V(1).Infof("Error while shutting down the server: %v", err) + } + case err := <-errChan: + log.Printf("Stopping the server; encounter error: %v", err) + cancelFunc() + } + }() +} diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index c3aff5f9843..3224bf494f6 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + apiserver_impl "github.com/redhat-developer/odo/pkg/apiserver-impl" "io" "path/filepath" "regexp" @@ -207,6 +208,8 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { genericclioptions.WarnIfDefaultNamespace(odocontext.GetNamespace(ctx), o.clientset.KubernetesClient) } + // Start the server here; it will be shutdown when context is cancelled; or if the server encounters an error + apiserver_impl.StartServer(ctx, o.cancel) // check for .gitignore file and add odo-file-index.json to .gitignore. // In case the .gitignore was created by odo, it is purposely not reported as candidate for deletion (via a call to files.ReportLocalFileGeneratedByOdo) // because a .gitignore file is more likely to be modified by the user afterward (for another usage). From 791521bf3255fccb2d276fab5582fc82505858e3 Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Fri, 26 May 2023 23:15:51 +0530 Subject: [PATCH 3/7] Add --api-server and --api-server-port flags to start API Server; write the port to stat file; TODO: make this feature experimental Signed-off-by: Parthvi Vala Co-authored-by: Armel Soro Co-authored-by: Philippe Martin Signed-off-by: Parthvi Vala Make the flag experimental Signed-off-by: Parthvi Vala Make apiserver and apiserverport flag local Signed-off-by: Parthvi Vala --- .golangci.yaml | 2 + Makefile | 4 +- ododevapispec.yaml | 2 + pkg/apiserver-gen/README.md | 2 +- pkg/apiserver-gen/api/openapi.yaml | 2 +- pkg/apiserver-gen/go/api.go | 7 +--- pkg/apiserver-gen/go/api_default.go | 22 +++++----- pkg/apiserver-gen/go/helpers.go | 2 +- pkg/apiserver-gen/go/model_general_error.go | 1 - pkg/apiserver-gen/go/model_general_success.go | 1 - pkg/apiserver-gen/go/routers.go | 10 +++-- pkg/apiserver-impl/api_default_service.go | 8 ++-- pkg/apiserver-impl/starterserver.go | 40 ++++++++++++++----- pkg/odo/cli/cli.go | 2 +- pkg/odo/cli/dev/dev.go | 22 ++++++++-- pkg/odo/cli/feature/features.go | 4 ++ pkg/odo/cmdline/cmdline.go | 3 +- pkg/odo/cmdline/cobra.go | 2 +- pkg/state/interface.go | 3 ++ pkg/state/state.go | 11 +++++ pkg/state/types.go | 1 + 21 files changed, 103 insertions(+), 48 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index e5707f4394f..a4ad2d39068 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -17,6 +17,8 @@ run: # Allowed values: readonly|vendor|mod # By default, it isn't set. modules-download-mode: vendor + skip-dirs: + - pkg/apiserver-gen linters: # Note that some linters not listed below are enabled by default. diff --git a/Makefile b/Makefile index 1abae585fca..1d6fbf71936 100644 --- a/Makefile +++ b/Makefile @@ -233,5 +233,5 @@ test-doc-automation: # Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen; this will only generate interfaces # Actual implementation must be done inside pkg/apiserver-impl .PHONY: generate-apiserver -generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen - openapi-generator generate -i ododevapispec.yaml -g go-server -o pkg/apiserver-gen --additional-properties=outputAsLibrary=true,onlyInterfaces=true +generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen; also go fmt the files + openapi-generator generate -i ododevapispec.yaml -g go-server -o pkg/apiserver-gen --additional-properties=outputAsLibrary=true,onlyInterfaces=true && echo "Formatting generated files:" && go fmt ./pkg/apiserver-gen/... diff --git a/ododevapispec.yaml b/ododevapispec.yaml index 4f2257ceffe..e4bb37245ed 100644 --- a/ododevapispec.yaml +++ b/ododevapispec.yaml @@ -3,6 +3,8 @@ info: title: odo dev version: '0.1' description: API interface for 'odo dev' +servers: + - url: /api/v1 paths: /instance: get: diff --git a/pkg/apiserver-gen/README.md b/pkg/apiserver-gen/README.md index fdf52e2ecd7..1271fa3afea 100644 --- a/pkg/apiserver-gen/README.md +++ b/pkg/apiserver-gen/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://openapi-generator.tech) - API version: 0.1 -- Build date: 2023-05-26T15:33:42.589940+05:30[Asia/Kolkata] +- Build date: 2023-05-26T23:07:17.061089+05:30[Asia/Kolkata] ### Running the server diff --git a/pkg/apiserver-gen/api/openapi.yaml b/pkg/apiserver-gen/api/openapi.yaml index f4cd8140bc5..d6a392cc77f 100644 --- a/pkg/apiserver-gen/api/openapi.yaml +++ b/pkg/apiserver-gen/api/openapi.yaml @@ -4,7 +4,7 @@ info: title: odo dev version: "0.1" servers: -- url: / +- url: /api/v1 paths: /instance: delete: diff --git a/pkg/apiserver-gen/go/api.go b/pkg/apiserver-gen/go/api.go index 400cd16e71b..c8e13b62193 100644 --- a/pkg/apiserver-gen/go/api.go +++ b/pkg/apiserver-gen/go/api.go @@ -14,24 +14,21 @@ import ( "net/http" ) - - // DefaultApiRouter defines the required methods for binding the api requests to a responses for the DefaultApi // The DefaultApiRouter implementation should parse necessary information from the http request, // pass the data to a DefaultApiServicer to perform the required actions, then write the service results to the http response. -type DefaultApiRouter interface { +type DefaultApiRouter interface { ComponentCommandPost(http.ResponseWriter, *http.Request) ComponentGet(http.ResponseWriter, *http.Request) InstanceDelete(http.ResponseWriter, *http.Request) InstanceGet(http.ResponseWriter, *http.Request) } - // DefaultApiServicer defines the api actions for the DefaultApi service // This interface intended to stay up to date with the openapi yaml used to generate it, // while the service implementation can be ignored with the .openapi-generator-ignore file // and updated with the logic required for the API. -type DefaultApiServicer interface { +type DefaultApiServicer interface { ComponentCommandPost(context.Context, ComponentCommandPostRequest) (ImplResponse, error) ComponentGet(context.Context) (ImplResponse, error) InstanceDelete(context.Context) (ImplResponse, error) diff --git a/pkg/apiserver-gen/go/api_default.go b/pkg/apiserver-gen/go/api_default.go index b7a0fc508d4..3d31b4c7160 100644 --- a/pkg/apiserver-gen/go/api_default.go +++ b/pkg/apiserver-gen/go/api_default.go @@ -14,12 +14,12 @@ import ( "net/http" "strings" - // "github.com/gorilla/mux" + _ "github.com/gorilla/mux" ) // DefaultApiController binds http requests to an api service and writes the service results to the http response type DefaultApiController struct { - service DefaultApiServicer + service DefaultApiServicer errorHandler ErrorHandler } @@ -49,35 +49,35 @@ func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Rou // Routes returns all the api routes for the DefaultApiController func (c *DefaultApiController) Routes() Routes { - return Routes{ + return Routes{ { "ComponentCommandPost", strings.ToUpper("Post"), - "/component/command", + "/api/v1/component/command", c.ComponentCommandPost, }, { "ComponentGet", strings.ToUpper("Get"), - "/component", + "/api/v1/component", c.ComponentGet, }, { "InstanceDelete", strings.ToUpper("Delete"), - "/instance", + "/api/v1/instance", c.InstanceDelete, }, { "InstanceGet", strings.ToUpper("Get"), - "/instance", + "/api/v1/instance", c.InstanceGet, }, } } -// ComponentCommandPost - +// ComponentCommandPost - func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *http.Request) { componentCommandPostRequestParam := ComponentCommandPostRequest{} d := json.NewDecoder(r.Body) @@ -101,7 +101,7 @@ func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *ht } -// ComponentGet - +// ComponentGet - func (c *DefaultApiController) ComponentGet(w http.ResponseWriter, r *http.Request) { result, err := c.service.ComponentGet(r.Context()) // If an error occurred, encode the error with the status code @@ -114,7 +114,7 @@ func (c *DefaultApiController) ComponentGet(w http.ResponseWriter, r *http.Reque } -// InstanceDelete - +// InstanceDelete - func (c *DefaultApiController) InstanceDelete(w http.ResponseWriter, r *http.Request) { result, err := c.service.InstanceDelete(r.Context()) // If an error occurred, encode the error with the status code @@ -127,7 +127,7 @@ func (c *DefaultApiController) InstanceDelete(w http.ResponseWriter, r *http.Req } -// InstanceGet - +// InstanceGet - func (c *DefaultApiController) InstanceGet(w http.ResponseWriter, r *http.Request) { result, err := c.service.InstanceGet(r.Context()) // If an error occurred, encode the error with the status code diff --git a/pkg/apiserver-gen/go/helpers.go b/pkg/apiserver-gen/go/helpers.go index ea45946a8f8..48f03265ee0 100644 --- a/pkg/apiserver-gen/go/helpers.go +++ b/pkg/apiserver-gen/go/helpers.go @@ -15,7 +15,7 @@ import ( // Response return a ImplResponse struct filled func Response(code int, body interface{}) ImplResponse { - return ImplResponse { + return ImplResponse{ Code: code, Body: body, } diff --git a/pkg/apiserver-gen/go/model_general_error.go b/pkg/apiserver-gen/go/model_general_error.go index 146860b1f20..2d47ac83f40 100644 --- a/pkg/apiserver-gen/go/model_general_error.go +++ b/pkg/apiserver-gen/go/model_general_error.go @@ -10,7 +10,6 @@ package openapi type GeneralError struct { - Message string `json:"message,omitempty"` } diff --git a/pkg/apiserver-gen/go/model_general_success.go b/pkg/apiserver-gen/go/model_general_success.go index 82baf61ca74..3ef5848b2bb 100644 --- a/pkg/apiserver-gen/go/model_general_success.go +++ b/pkg/apiserver-gen/go/model_general_success.go @@ -10,7 +10,6 @@ package openapi type GeneralSuccess struct { - Message string `json:"message,omitempty"` } diff --git a/pkg/apiserver-gen/go/routers.go b/pkg/apiserver-gen/go/routers.go index 9f33fab4241..83917012741 100644 --- a/pkg/apiserver-gen/go/routers.go +++ b/pkg/apiserver-gen/go/routers.go @@ -23,9 +23,9 @@ import ( // A Route defines the parameters for an api endpoint type Route struct { - Name string - Method string - Pattern string + Name string + Method string + Pattern string HandlerFunc http.HandlerFunc } @@ -124,7 +124,9 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error return nil, err } - defer file.Close() + defer func() { + _ = file.Close() + }() file.Write(fileBytes) diff --git a/pkg/apiserver-impl/api_default_service.go b/pkg/apiserver-impl/api_default_service.go index 40471fc35f2..4801fec2163 100644 --- a/pkg/apiserver-impl/api_default_service.go +++ b/pkg/apiserver-impl/api_default_service.go @@ -18,7 +18,7 @@ func NewDefaultApiService() openapi.DefaultApiServicer { return &DefaultApiService{} } -// ComponentCommandPost - +// ComponentCommandPost - func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) { // TODO - update ComponentCommandPost with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. @@ -29,7 +29,7 @@ func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentC return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentCommandPost method not implemented") } -// ComponentGet - +// ComponentGet - func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update ComponentGet with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. @@ -40,7 +40,7 @@ func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplRespo return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentGet method not implemented") } -// InstanceDelete - +// InstanceDelete - func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update InstanceDelete with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. @@ -51,7 +51,7 @@ func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplRes return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceDelete method not implemented") } -// InstanceGet - +// InstanceGet - func (s *DefaultApiService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update InstanceGet with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. diff --git a/pkg/apiserver-impl/starterserver.go b/pkg/apiserver-impl/starterserver.go index 436eb49c5b2..f1910672da9 100644 --- a/pkg/apiserver-impl/starterserver.go +++ b/pkg/apiserver-impl/starterserver.go @@ -2,35 +2,55 @@ package apiserver_impl import ( "context" + "fmt" openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go" + "github.com/redhat-developer/odo/pkg/state" + "github.com/redhat-developer/odo/pkg/util" "k8s.io/klog" - "log" "net/http" ) -func StartServer(ctx context.Context, cancelFunc context.CancelFunc) { - log.Printf("Server started") +func StartServer(ctx context.Context, cancelFunc context.CancelFunc, port int, stateClient state.Client) { DefaultApiService := NewDefaultApiService() DefaultApiController := openapi.NewDefaultApiController(DefaultApiService) router := openapi.NewRouter(DefaultApiController) - server := &http.Server{Addr: ":20000", Handler: router} + + var err error + + if port == 0 { + port, err = util.NextFreePort(20000, 30001, nil, "") + if err != nil { + klog.V(0).Infof("Unable to start the API server; encountered error: %v", err) + cancelFunc() + } + } + + err = stateClient.SetAPIServerPort(ctx, port) + if err != nil { + klog.V(0).Infof("Unable to start the API server; encountered error: %v", err) + cancelFunc() + } + + klog.V(0).Infof("API Server started at localhost:%d/api/v1", port) + + server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: router} var errChan = make(chan error) go func() { - err := server.ListenAndServe() + err = server.ListenAndServe() errChan <- err }() go func() { select { case <-ctx.Done(): - err := server.Shutdown(ctx) - klog.V(1).Infof("Shutting down the server: %v", ctx.Err()) + klog.V(0).Infof("Shutting down the API server: %v", ctx.Err()) + err = server.Shutdown(ctx) if err != nil { - klog.V(1).Infof("Error while shutting down the server: %v", err) + klog.V(1).Infof("Error while shutting down the API server: %v", err) } - case err := <-errChan: - log.Printf("Stopping the server; encounter error: %v", err) + case err = <-errChan: + klog.V(0).Infof("Stopping the API server; encountered error: %v", err) cancelFunc() } }() diff --git a/pkg/odo/cli/cli.go b/pkg/odo/cli/cli.go index 67f02f77638..6f706ad138a 100644 --- a/pkg/odo/cli/cli.go +++ b/pkg/odo/cli/cli.go @@ -189,7 +189,7 @@ func odoRootCmd(ctx context.Context, name, fullName string, testClientset client _delete.NewCmdDelete(ctx, _delete.RecommendedCommandName, util.GetFullName(fullName, _delete.RecommendedCommandName), testClientset), add.NewCmdAdd(add.RecommendedCommandName, util.GetFullName(fullName, add.RecommendedCommandName), testClientset), remove.NewCmdRemove(remove.RecommendedCommandName, util.GetFullName(fullName, remove.RecommendedCommandName), testClientset), - dev.NewCmdDev(dev.RecommendedCommandName, util.GetFullName(fullName, dev.RecommendedCommandName), testClientset), + dev.NewCmdDev(ctx, dev.RecommendedCommandName, util.GetFullName(fullName, dev.RecommendedCommandName), testClientset), alizer.NewCmdAlizer(alizer.RecommendedCommandName, util.GetFullName(fullName, alizer.RecommendedCommandName), testClientset), describe.NewCmdDescribe(ctx, describe.RecommendedCommandName, util.GetFullName(fullName, describe.RecommendedCommandName), testClientset), registry.NewCmdRegistry(registry.RecommendedCommandName, util.GetFullName(fullName, registry.RecommendedCommandName), testClientset), diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index 3224bf494f6..9f258960a4d 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" apiserver_impl "github.com/redhat-developer/odo/pkg/apiserver-impl" + "github.com/redhat-developer/odo/pkg/odo/cli/feature" "io" "path/filepath" "regexp" @@ -71,6 +72,8 @@ type DevOptions struct { portForwardFlag []string addressFlag string noCommandsFlag bool + apiServerFlag bool + apiServerPortFlag int } var _ genericclioptions.Runnable = (*DevOptions)(nil) @@ -174,6 +177,12 @@ func (o *DevOptions) Validate(ctx context.Context) error { return err } + if o.apiServerFlag && o.apiServerPortFlag != 0 { + if !util.IsPortFree(o.apiServerPortFlag, "") { + return fmt.Errorf("port %d is not free; please try another port", o.apiServerPortFlag) + } + } + return nil } @@ -208,8 +217,6 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { genericclioptions.WarnIfDefaultNamespace(odocontext.GetNamespace(ctx), o.clientset.KubernetesClient) } - // Start the server here; it will be shutdown when context is cancelled; or if the server encounters an error - apiserver_impl.StartServer(ctx, o.cancel) // check for .gitignore file and add odo-file-index.json to .gitignore. // In case the .gitignore was created by odo, it is purposely not reported as candidate for deletion (via a call to files.ReportLocalFileGeneratedByOdo) // because a .gitignore file is more likely to be modified by the user afterward (for another usage). @@ -245,6 +252,11 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { return err } + if o.apiServerFlag { + // Start the server here; it will be shutdown when context is cancelled; or if the server encounters an error + apiserver_impl.StartServer(ctx, o.cancel, o.apiServerPortFlag, o.clientset.StateClient) + } + return o.clientset.DevClient.Start( o.ctx, dev.StartOptions{ @@ -285,7 +297,7 @@ func (o *DevOptions) Cleanup(ctx context.Context, commandError error) { } // NewCmdDev implements the odo dev command -func NewCmdDev(name, fullName string, testClientset clientset.Clientset) *cobra.Command { +func NewCmdDev(ctx context.Context, name, fullName string, testClientset clientset.Clientset) *cobra.Command { o := NewDevOptions() devCmd := &cobra.Command{ Use: name, @@ -314,6 +326,10 @@ It forwards endpoints with any exposure values ('public', 'internal' or 'none') devCmd.Flags().StringVar(&o.addressFlag, "address", "127.0.0.1", "Define custom address for port forwarding.") devCmd.Flags().BoolVar(&o.noCommandsFlag, "no-commands", false, "Do not run any commands; just start the development environment.") + if feature.IsExperimentalModeEnabled(ctx) { + devCmd.Flags().BoolVar(&o.apiServerFlag, "api-server", false, "Start the API Server; this is an experimental feature") + devCmd.Flags().IntVar(&o.apiServerPortFlag, "api-server-port", 0, "Define custom port for API Server; this flag should be used in combination with --api-server flag.") + } clientset.Add(devCmd, clientset.BINDING, clientset.DEV, diff --git a/pkg/odo/cli/feature/features.go b/pkg/odo/cli/feature/features.go index 7968b811838..cf4137eb33c 100644 --- a/pkg/odo/cli/feature/features.go +++ b/pkg/odo/cli/feature/features.go @@ -17,6 +17,10 @@ var ( GenericPlatformFlag = OdoFeature{ isExperimental: false, } + + APIServerFlag = OdoFeature{ + isExperimental: true, + } ) // IsEnabled returns whether the specified feature should be enabled or not. diff --git a/pkg/odo/cmdline/cmdline.go b/pkg/odo/cmdline/cmdline.go index 4afe67b6ccf..0cad31fd8b3 100644 --- a/pkg/odo/cmdline/cmdline.go +++ b/pkg/odo/cmdline/cmdline.go @@ -4,7 +4,6 @@ package cmdline import ( "context" - "github.com/redhat-developer/odo/pkg/kclient" ) @@ -15,7 +14,7 @@ type Cmdline interface { // GetFlags returns a map of flags set GetFlags() map[string]string - // FlagValue returns the value for a flag + // FlagValue returns the string value for a flag FlagValue(flagName string) (string, error) // FlagValueIfSet returns the value for a flag, or an empty string if not set diff --git a/pkg/odo/cmdline/cobra.go b/pkg/odo/cmdline/cobra.go index 3fa24bfdb6b..b58c3851df2 100644 --- a/pkg/odo/cmdline/cobra.go +++ b/pkg/odo/cmdline/cobra.go @@ -59,7 +59,7 @@ func (o *Cobra) GetWorkingDirectory() (string, error) { return dfutil.GetAbsPath(".") } -// FlagValueIfSet retrieves the value of the specified flag if it is set for the given command +// FlagValue retrieves the value of the specified flag if it is set for the given command func (o *Cobra) FlagValue(flagName string) (string, error) { return o.cmd.Flags().GetString(flagName) } diff --git a/pkg/state/interface.go b/pkg/state/interface.go index 9029000786f..fd687c1173e 100644 --- a/pkg/state/interface.go +++ b/pkg/state/interface.go @@ -18,4 +18,7 @@ type Client interface { // SaveExit resets the state file to indicate odo is not running SaveExit(ctx context.Context) error + + // SetAPIServerPort sets the port where API server is listening in the state file and saves it to the file, updating the metadata + SetAPIServerPort(ctx context.Context, port int) error } diff --git a/pkg/state/state.go b/pkg/state/state.go index 71e4d5db109..dc72933ebbc 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -92,6 +92,17 @@ func (o *State) SaveExit(ctx context.Context) error { return o.saveCommonIfOwner(pid) } +func (o *State) SetAPIServerPort(ctx context.Context, port int) error { + var ( + pid = odocontext.GetPID(ctx) + platform = fcontext.GetPlatform(ctx, commonflags.PlatformCluster) + ) + + o.content.APIServerPort = port + o.content.Platform = platform + return o.save(ctx, pid) +} + // save writes the content structure in json format in file func (o *State) save(ctx context.Context, pid int) error { diff --git a/pkg/state/types.go b/pkg/state/types.go index 0aefcca4048..05002f1b7a1 100644 --- a/pkg/state/types.go +++ b/pkg/state/types.go @@ -11,4 +11,5 @@ type Content struct { Platform string `json:"platform"` // ForwardedPorts are the ports forwarded during odo dev session ForwardedPorts []api.ForwardedPort `json:"forwardedPorts"` + APIServerPort int `json:"APIServerPort"` } From b02bd18ead77eb11b6b252f3cf41cb3a3bb85aa3 Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Mon, 12 Jun 2023 09:35:16 +0530 Subject: [PATCH 4/7] Use container image to run openapi-generator-tool instead of a local CLI Co-authored-by: Armel Soro Signed-off-by: Parthvi Vala --- Makefile | 11 +- pkg/apiserver-gen/.openapi-generator/VERSION | 2 +- pkg/apiserver-gen/README.md | 2 +- pkg/apiserver-gen/go/api.go | 12 +- pkg/apiserver-gen/go/api_default.go | 56 +++--- pkg/apiserver-gen/go/helpers.go | 14 +- pkg/apiserver-gen/go/impl.go | 2 +- .../model__component_command_post_request.go | 24 +-- .../go/model__component_get_200_response.go | 24 +-- .../go/model__instance_get_200_response.go | 24 +-- pkg/apiserver-gen/go/model_general_error.go | 24 +-- pkg/apiserver-gen/go/model_general_success.go | 24 +-- pkg/apiserver-gen/go/routers.go | 164 +++++++++++------- pkg/apiserver-impl/api_default_service.go | 32 ++-- pkg/apiserver-impl/starterserver.go | 2 +- 15 files changed, 247 insertions(+), 170 deletions(-) diff --git a/Makefile b/Makefile index 1d6fbf71936..6139c534a71 100644 --- a/Makefile +++ b/Makefile @@ -234,4 +234,13 @@ test-doc-automation: # Actual implementation must be done inside pkg/apiserver-impl .PHONY: generate-apiserver generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen; also go fmt the files - openapi-generator generate -i ododevapispec.yaml -g go-server -o pkg/apiserver-gen --additional-properties=outputAsLibrary=true,onlyInterfaces=true && echo "Formatting generated files:" && go fmt ./pkg/apiserver-gen/... + podman run --rm \ + -v ${PWD}:/local \ + docker.io/openapitools/openapi-generator-cli:v6.6.0 \ + generate \ + -i /local/ododevapispec.yaml \ + -g go-server \ + -o /local/pkg/apiserver-gen \ + --additional-properties=outputAsLibrary=true,onlyInterfaces=true,hideGenerationTimestamp=true && \ + echo "Formatting generated files:" && \ + go fmt ./pkg/apiserver-gen/... \ No newline at end of file diff --git a/pkg/apiserver-gen/.openapi-generator/VERSION b/pkg/apiserver-gen/.openapi-generator/VERSION index 4be2c727ad9..757e6740040 100644 --- a/pkg/apiserver-gen/.openapi-generator/VERSION +++ b/pkg/apiserver-gen/.openapi-generator/VERSION @@ -1 +1 @@ -6.5.0 \ No newline at end of file +7.0.0-SNAPSHOT \ No newline at end of file diff --git a/pkg/apiserver-gen/README.md b/pkg/apiserver-gen/README.md index 1271fa3afea..ae9cc6cf584 100644 --- a/pkg/apiserver-gen/README.md +++ b/pkg/apiserver-gen/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://openapi-generator.tech) - API version: 0.1 -- Build date: 2023-05-26T23:07:17.061089+05:30[Asia/Kolkata] +- Build date: 2023-06-12T03:51:38.118965Z[Etc/UTC] ### Running the server diff --git a/pkg/apiserver-gen/go/api.go b/pkg/apiserver-gen/go/api.go index c8e13b62193..a6aef9f1f9c 100644 --- a/pkg/apiserver-gen/go/api.go +++ b/pkg/apiserver-gen/go/api.go @@ -14,21 +14,21 @@ import ( "net/http" ) -// DefaultApiRouter defines the required methods for binding the api requests to a responses for the DefaultApi -// The DefaultApiRouter implementation should parse necessary information from the http request, -// pass the data to a DefaultApiServicer to perform the required actions, then write the service results to the http response. -type DefaultApiRouter interface { +// DefaultAPIRouter defines the required methods for binding the api requests to a responses for the DefaultAPI +// The DefaultAPIRouter implementation should parse necessary information from the http request, +// pass the data to a DefaultAPIServicer to perform the required actions, then write the service results to the http response. +type DefaultAPIRouter interface { ComponentCommandPost(http.ResponseWriter, *http.Request) ComponentGet(http.ResponseWriter, *http.Request) InstanceDelete(http.ResponseWriter, *http.Request) InstanceGet(http.ResponseWriter, *http.Request) } -// DefaultApiServicer defines the api actions for the DefaultApi service +// DefaultAPIServicer defines the api actions for the DefaultAPI service // This interface intended to stay up to date with the openapi yaml used to generate it, // while the service implementation can be ignored with the .openapi-generator-ignore file // and updated with the logic required for the API. -type DefaultApiServicer interface { +type DefaultAPIServicer interface { ComponentCommandPost(context.Context, ComponentCommandPostRequest) (ImplResponse, error) ComponentGet(context.Context) (ImplResponse, error) InstanceDelete(context.Context) (ImplResponse, error) diff --git a/pkg/apiserver-gen/go/api_default.go b/pkg/apiserver-gen/go/api_default.go index 3d31b4c7160..cedf9c7653e 100644 --- a/pkg/apiserver-gen/go/api_default.go +++ b/pkg/apiserver-gen/go/api_default.go @@ -13,29 +13,27 @@ import ( "encoding/json" "net/http" "strings" - - _ "github.com/gorilla/mux" ) -// DefaultApiController binds http requests to an api service and writes the service results to the http response -type DefaultApiController struct { - service DefaultApiServicer +// DefaultAPIController binds http requests to an api service and writes the service results to the http response +type DefaultAPIController struct { + service DefaultAPIServicer errorHandler ErrorHandler } -// DefaultApiOption for how the controller is set up. -type DefaultApiOption func(*DefaultApiController) +// DefaultAPIOption for how the controller is set up. +type DefaultAPIOption func(*DefaultAPIController) -// WithDefaultApiErrorHandler inject ErrorHandler into controller -func WithDefaultApiErrorHandler(h ErrorHandler) DefaultApiOption { - return func(c *DefaultApiController) { +// WithDefaultAPIErrorHandler inject ErrorHandler into controller +func WithDefaultAPIErrorHandler(h ErrorHandler) DefaultAPIOption { + return func(c *DefaultAPIController) { c.errorHandler = h } } -// NewDefaultApiController creates a default api controller -func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Router { - controller := &DefaultApiController{ +// NewDefaultAPIController creates a default api controller +func NewDefaultAPIController(s DefaultAPIServicer, opts ...DefaultAPIOption) Router { + controller := &DefaultAPIController{ service: s, errorHandler: DefaultErrorHandler, } @@ -47,29 +45,25 @@ func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Rou return controller } -// Routes returns all the api routes for the DefaultApiController -func (c *DefaultApiController) Routes() Routes { +// Routes returns all the api routes for the DefaultAPIController +func (c *DefaultAPIController) Routes() Routes { return Routes{ - { - "ComponentCommandPost", + "ComponentCommandPost": Route{ strings.ToUpper("Post"), "/api/v1/component/command", c.ComponentCommandPost, }, - { - "ComponentGet", + "ComponentGet": Route{ strings.ToUpper("Get"), "/api/v1/component", c.ComponentGet, }, - { - "InstanceDelete", + "InstanceDelete": Route{ strings.ToUpper("Delete"), "/api/v1/instance", c.InstanceDelete, }, - { - "InstanceGet", + "InstanceGet": Route{ strings.ToUpper("Get"), "/api/v1/instance", c.InstanceGet, @@ -78,7 +72,7 @@ func (c *DefaultApiController) Routes() Routes { } // ComponentCommandPost - -func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *http.Request) { +func (c *DefaultAPIController) ComponentCommandPost(w http.ResponseWriter, r *http.Request) { componentCommandPostRequestParam := ComponentCommandPostRequest{} d := json.NewDecoder(r.Body) d.DisallowUnknownFields() @@ -90,6 +84,10 @@ func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *ht c.errorHandler(w, r, err, nil) return } + if err := AssertComponentCommandPostRequestConstraints(componentCommandPostRequestParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } result, err := c.service.ComponentCommandPost(r.Context(), componentCommandPostRequestParam) // If an error occurred, encode the error with the status code if err != nil { @@ -98,11 +96,10 @@ func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *ht } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) - } // ComponentGet - -func (c *DefaultApiController) ComponentGet(w http.ResponseWriter, r *http.Request) { +func (c *DefaultAPIController) ComponentGet(w http.ResponseWriter, r *http.Request) { result, err := c.service.ComponentGet(r.Context()) // If an error occurred, encode the error with the status code if err != nil { @@ -111,11 +108,10 @@ func (c *DefaultApiController) ComponentGet(w http.ResponseWriter, r *http.Reque } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) - } // InstanceDelete - -func (c *DefaultApiController) InstanceDelete(w http.ResponseWriter, r *http.Request) { +func (c *DefaultAPIController) InstanceDelete(w http.ResponseWriter, r *http.Request) { result, err := c.service.InstanceDelete(r.Context()) // If an error occurred, encode the error with the status code if err != nil { @@ -124,11 +120,10 @@ func (c *DefaultApiController) InstanceDelete(w http.ResponseWriter, r *http.Req } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) - } // InstanceGet - -func (c *DefaultApiController) InstanceGet(w http.ResponseWriter, r *http.Request) { +func (c *DefaultAPIController) InstanceGet(w http.ResponseWriter, r *http.Request) { result, err := c.service.InstanceGet(r.Context()) // If an error occurred, encode the error with the status code if err != nil { @@ -137,5 +132,4 @@ func (c *DefaultApiController) InstanceGet(w http.ResponseWriter, r *http.Reques } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) - } diff --git a/pkg/apiserver-gen/go/helpers.go b/pkg/apiserver-gen/go/helpers.go index 48f03265ee0..325e7bfb38a 100644 --- a/pkg/apiserver-gen/go/helpers.go +++ b/pkg/apiserver-gen/go/helpers.go @@ -28,17 +28,23 @@ func IsZeroValue(val interface{}) bool { // AssertRecurseInterfaceRequired recursively checks each struct in a slice against the callback. // This method traverse nested slices in a preorder fashion. -func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { +func AssertRecurseInterfaceRequired[T any](obj interface{}, callback func(T) error) error { return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) } // AssertRecurseValueRequired checks each struct in the nested slice against the callback. -// This method traverse nested slices in a preorder fashion. -func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { +// This method traverse nested slices in a preorder fashion. ErrTypeAssertionError is thrown if +// the underlying struct does not match type T. +func AssertRecurseValueRequired[T any](value reflect.Value, callback func(T) error) error { switch value.Kind() { // If it is a struct we check using callback case reflect.Struct: - if err := callback(value.Interface()); err != nil { + obj, ok := value.Interface().(T) + if !ok { + return ErrTypeAssertionError + } + + if err := callback(obj); err != nil { return err } diff --git a/pkg/apiserver-gen/go/impl.go b/pkg/apiserver-gen/go/impl.go index 9ca7add4e99..33bf2952281 100644 --- a/pkg/apiserver-gen/go/impl.go +++ b/pkg/apiserver-gen/go/impl.go @@ -9,7 +9,7 @@ package openapi -// ImplResponse response defines an error code with the associated body +// ImplResponse defines an implementation response with error code and the associated body type ImplResponse struct { Code int Body interface{} diff --git a/pkg/apiserver-gen/go/model__component_command_post_request.go b/pkg/apiserver-gen/go/model__component_command_post_request.go index 26e4fc63be3..49802444f83 100644 --- a/pkg/apiserver-gen/go/model__component_command_post_request.go +++ b/pkg/apiserver-gen/go/model__component_command_post_request.go @@ -9,25 +9,29 @@ package openapi +import ( + "encoding/json" +) + type ComponentCommandPostRequest struct { // Name of the command that should be executed Name string `json:"name,omitempty"` } +// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. +func (m *ComponentCommandPostRequest) UnmarshalJSON(data []byte) error { + + type Alias ComponentCommandPostRequest // To avoid infinite recursion + return json.Unmarshal(data, (*Alias)(m)) +} + // AssertComponentCommandPostRequestRequired checks if the required fields are not zero-ed func AssertComponentCommandPostRequestRequired(obj ComponentCommandPostRequest) error { return nil } -// AssertRecurseComponentCommandPostRequestRequired recursively checks if required fields are not zero-ed in a nested slice. -// Accepts only nested slice of ComponentCommandPostRequest (e.g. [][]ComponentCommandPostRequest), otherwise ErrTypeAssertionError is thrown. -func AssertRecurseComponentCommandPostRequestRequired(objSlice interface{}) error { - return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { - aComponentCommandPostRequest, ok := obj.(ComponentCommandPostRequest) - if !ok { - return ErrTypeAssertionError - } - return AssertComponentCommandPostRequestRequired(aComponentCommandPostRequest) - }) +// AssertComponentCommandPostRequestConstraints checks if the values respects the defined constraints +func AssertComponentCommandPostRequestConstraints(obj ComponentCommandPostRequest) error { + return nil } diff --git a/pkg/apiserver-gen/go/model__component_get_200_response.go b/pkg/apiserver-gen/go/model__component_get_200_response.go index fc416bd3848..33fa53cc02a 100644 --- a/pkg/apiserver-gen/go/model__component_get_200_response.go +++ b/pkg/apiserver-gen/go/model__component_get_200_response.go @@ -9,25 +9,29 @@ package openapi +import ( + "encoding/json" +) + type ComponentGet200Response struct { // Description of the component. This is the same as output of 'odo describe component -o json' Component map[string]interface{} `json:"component,omitempty"` } +// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. +func (m *ComponentGet200Response) UnmarshalJSON(data []byte) error { + + type Alias ComponentGet200Response // To avoid infinite recursion + return json.Unmarshal(data, (*Alias)(m)) +} + // AssertComponentGet200ResponseRequired checks if the required fields are not zero-ed func AssertComponentGet200ResponseRequired(obj ComponentGet200Response) error { return nil } -// AssertRecurseComponentGet200ResponseRequired recursively checks if required fields are not zero-ed in a nested slice. -// Accepts only nested slice of ComponentGet200Response (e.g. [][]ComponentGet200Response), otherwise ErrTypeAssertionError is thrown. -func AssertRecurseComponentGet200ResponseRequired(objSlice interface{}) error { - return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { - aComponentGet200Response, ok := obj.(ComponentGet200Response) - if !ok { - return ErrTypeAssertionError - } - return AssertComponentGet200ResponseRequired(aComponentGet200Response) - }) +// AssertComponentGet200ResponseConstraints checks if the values respects the defined constraints +func AssertComponentGet200ResponseConstraints(obj ComponentGet200Response) error { + return nil } diff --git a/pkg/apiserver-gen/go/model__instance_get_200_response.go b/pkg/apiserver-gen/go/model__instance_get_200_response.go index 1485bd19b4e..1c9e7a9ae70 100644 --- a/pkg/apiserver-gen/go/model__instance_get_200_response.go +++ b/pkg/apiserver-gen/go/model__instance_get_200_response.go @@ -9,6 +9,10 @@ package openapi +import ( + "encoding/json" +) + type InstanceGet200Response struct { // Directory on which this 'odo dev' instance is running @@ -18,19 +22,19 @@ type InstanceGet200Response struct { Pid int32 `json:"pid,omitempty"` } +// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. +func (m *InstanceGet200Response) UnmarshalJSON(data []byte) error { + + type Alias InstanceGet200Response // To avoid infinite recursion + return json.Unmarshal(data, (*Alias)(m)) +} + // AssertInstanceGet200ResponseRequired checks if the required fields are not zero-ed func AssertInstanceGet200ResponseRequired(obj InstanceGet200Response) error { return nil } -// AssertRecurseInstanceGet200ResponseRequired recursively checks if required fields are not zero-ed in a nested slice. -// Accepts only nested slice of InstanceGet200Response (e.g. [][]InstanceGet200Response), otherwise ErrTypeAssertionError is thrown. -func AssertRecurseInstanceGet200ResponseRequired(objSlice interface{}) error { - return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { - aInstanceGet200Response, ok := obj.(InstanceGet200Response) - if !ok { - return ErrTypeAssertionError - } - return AssertInstanceGet200ResponseRequired(aInstanceGet200Response) - }) +// AssertInstanceGet200ResponseConstraints checks if the values respects the defined constraints +func AssertInstanceGet200ResponseConstraints(obj InstanceGet200Response) error { + return nil } diff --git a/pkg/apiserver-gen/go/model_general_error.go b/pkg/apiserver-gen/go/model_general_error.go index 2d47ac83f40..2308bded693 100644 --- a/pkg/apiserver-gen/go/model_general_error.go +++ b/pkg/apiserver-gen/go/model_general_error.go @@ -9,23 +9,27 @@ package openapi +import ( + "encoding/json" +) + type GeneralError struct { Message string `json:"message,omitempty"` } +// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. +func (m *GeneralError) UnmarshalJSON(data []byte) error { + + type Alias GeneralError // To avoid infinite recursion + return json.Unmarshal(data, (*Alias)(m)) +} + // AssertGeneralErrorRequired checks if the required fields are not zero-ed func AssertGeneralErrorRequired(obj GeneralError) error { return nil } -// AssertRecurseGeneralErrorRequired recursively checks if required fields are not zero-ed in a nested slice. -// Accepts only nested slice of GeneralError (e.g. [][]GeneralError), otherwise ErrTypeAssertionError is thrown. -func AssertRecurseGeneralErrorRequired(objSlice interface{}) error { - return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { - aGeneralError, ok := obj.(GeneralError) - if !ok { - return ErrTypeAssertionError - } - return AssertGeneralErrorRequired(aGeneralError) - }) +// AssertGeneralErrorConstraints checks if the values respects the defined constraints +func AssertGeneralErrorConstraints(obj GeneralError) error { + return nil } diff --git a/pkg/apiserver-gen/go/model_general_success.go b/pkg/apiserver-gen/go/model_general_success.go index 3ef5848b2bb..da95f88efa2 100644 --- a/pkg/apiserver-gen/go/model_general_success.go +++ b/pkg/apiserver-gen/go/model_general_success.go @@ -9,23 +9,27 @@ package openapi +import ( + "encoding/json" +) + type GeneralSuccess struct { Message string `json:"message,omitempty"` } +// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. +func (m *GeneralSuccess) UnmarshalJSON(data []byte) error { + + type Alias GeneralSuccess // To avoid infinite recursion + return json.Unmarshal(data, (*Alias)(m)) +} + // AssertGeneralSuccessRequired checks if the required fields are not zero-ed func AssertGeneralSuccessRequired(obj GeneralSuccess) error { return nil } -// AssertRecurseGeneralSuccessRequired recursively checks if required fields are not zero-ed in a nested slice. -// Accepts only nested slice of GeneralSuccess (e.g. [][]GeneralSuccess), otherwise ErrTypeAssertionError is thrown. -func AssertRecurseGeneralSuccessRequired(objSlice interface{}) error { - return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { - aGeneralSuccess, ok := obj.(GeneralSuccess) - if !ok { - return ErrTypeAssertionError - } - return AssertGeneralSuccessRequired(aGeneralSuccess) - }) +// AssertGeneralSuccessConstraints checks if the values respects the defined constraints +func AssertGeneralSuccessConstraints(obj GeneralSuccess) error { + return nil } diff --git a/pkg/apiserver-gen/go/routers.go b/pkg/apiserver-gen/go/routers.go index 83917012741..806dccff48c 100644 --- a/pkg/apiserver-gen/go/routers.go +++ b/pkg/apiserver-gen/go/routers.go @@ -23,14 +23,13 @@ import ( // A Route defines the parameters for an api endpoint type Route struct { - Name string Method string Pattern string HandlerFunc http.HandlerFunc } -// Routes are a collection of defined api endpoints -type Routes []Route +// Routes is a map of defined api endpoints +type Routes map[string]Route // Router defines the required methods for retrieving api routes type Router interface { @@ -38,20 +37,22 @@ type Router interface { } const errMsgRequiredMissing = "required parameter is missing" +const errMsgMinValueConstraint = "provided parameter is not respecting minimum value constraint" +const errMsgMaxValueConstraint = "provided parameter is not respecting maximum value constraint" // NewRouter creates a new router for any number of api routers func NewRouter(routers ...Router) *mux.Router { router := mux.NewRouter().StrictSlash(true) for _, api := range routers { - for _, route := range api.Routes() { + for name, route := range api.Routes() { var handler http.Handler handler = route.HandlerFunc - handler = Logger(handler, route.Name) + handler = Logger(handler, name) router. Methods(route.Method). Path(route.Pattern). - Name(route.Name). + Name(name). Handler(handler) } } @@ -133,81 +134,119 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error return file, nil } -// parseInt64Parameter parses a string parameter to an int64. -func parseInt64Parameter(param string, required bool) (int64, error) { - if param == "" { - if required { - return 0, errors.New(errMsgRequiredMissing) - } +type Number interface { + ~int32 | ~int64 | ~float32 | ~float64 +} - return 0, nil - } +type ParseString[T Number | string | bool] func(v string) (T, error) - return strconv.ParseInt(param, 10, 64) +// parseFloat64 parses a string parameter to an float64. +func parseFloat64(param string) (float64, error) { + return strconv.ParseFloat(param, 64) } -// parseInt32Parameter parses a string parameter to an int32. -func parseInt32Parameter(param string, required bool) (int32, error) { - if param == "" { - if required { - return 0, errors.New(errMsgRequiredMissing) - } +// parseFloat32 parses a string parameter to an float32. +func parseFloat32(param string) (float32, error) { + v, err := strconv.ParseFloat(param, 32) + return float32(v), err +} - return 0, nil - } +// parseInt64 parses a string parameter to an int64. +func parseInt64(param string) (int64, error) { + return strconv.ParseInt(param, 10, 64) +} +// parseInt32 parses a string parameter to an int32. +func parseInt32(param string) (int32, error) { val, err := strconv.ParseInt(param, 10, 32) - if err != nil { - return -1, err - } + return int32(val), err +} - return int32(val), nil +// parseBool parses a string parameter to an bool. +func parseBool(param string) (bool, error) { + return strconv.ParseBool(param) } -// parseBoolParameter parses a string parameter to a bool -func parseBoolParameter(param string, required bool) (bool, error) { - if param == "" { - if required { - return false, errors.New(errMsgRequiredMissing) +type Operation[T Number | string | bool] func(actual string) (T, bool, error) + +func WithRequire[T Number | string | bool](parse ParseString[T]) Operation[T] { + var empty T + return func(actual string) (T, bool, error) { + if actual == "" { + return empty, false, errors.New(errMsgRequiredMissing) } - return false, nil + v, err := parse(actual) + return v, false, err } +} - val, err := strconv.ParseBool(param) - if err != nil { - return false, err +func WithDefaultOrParse[T Number | string | bool](def T, parse ParseString[T]) Operation[T] { + return func(actual string) (T, bool, error) { + if actual == "" { + return def, true, nil + } + + v, err := parse(actual) + return v, false, err } +} - return bool(val), nil +func WithParse[T Number | string | bool](parse ParseString[T]) Operation[T] { + return func(actual string) (T, bool, error) { + v, err := parse(actual) + return v, false, err + } } -// parseInt64ArrayParameter parses a string parameter containing array of values to []int64. -func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) { - if param == "" { - if required { - return nil, errors.New(errMsgRequiredMissing) +type Constraint[T Number | string | bool] func(actual T) error + +func WithMinimum[T Number](expected T) Constraint[T] { + return func(actual T) error { + if actual < expected { + return errors.New(errMsgMinValueConstraint) } - return nil, nil + return nil } +} - str := strings.Split(param, delim) - ints := make([]int64, len(str)) +func WithMaximum[T Number](expected T) Constraint[T] { + return func(actual T) error { + if actual > expected { + return errors.New(errMsgMaxValueConstraint) + } - for i, s := range str { - if v, err := strconv.ParseInt(s, 10, 64); err != nil { - return nil, err - } else { - ints[i] = v + return nil + } +} + +// parseNumericParameter parses a numeric parameter to its respective type. +func parseNumericParameter[T Number](param string, fn Operation[T], checks ...Constraint[T]) (T, error) { + v, ok, err := fn(param) + if err != nil { + return 0, err + } + + if !ok { + for _, check := range checks { + if err := check(v); err != nil { + return 0, err + } } } - return ints, nil + return v, nil +} + +// parseBoolParameter parses a string parameter to a bool +func parseBoolParameter(param string, fn Operation[bool]) (bool, error) { + v, _, err := fn(param) + return v, err } -// parseInt32ArrayParameter parses a string parameter containing array of values to []int32. -func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) { +// parseNumericArrayParameter parses a string parameter containing array of values to its respective type. +func parseNumericArrayParameter[T Number](param, delim string, required bool, fn Operation[T], checks ...Constraint[T]) ([]T, error) { if param == "" { if required { return nil, errors.New(errMsgRequiredMissing) @@ -217,15 +256,24 @@ func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, erro } str := strings.Split(param, delim) - ints := make([]int32, len(str)) + values := make([]T, len(str)) for i, s := range str { - if v, err := strconv.ParseInt(s, 10, 32); err != nil { + v, ok, err := fn(s) + if err != nil { return nil, err - } else { - ints[i] = int32(v) } + + if !ok { + for _, check := range checks { + if err := check(v); err != nil { + return nil, err + } + } + } + + values[i] = v } - return ints, nil + return values, nil } diff --git a/pkg/apiserver-impl/api_default_service.go b/pkg/apiserver-impl/api_default_service.go index 4801fec2163..80558ae31c1 100644 --- a/pkg/apiserver-impl/api_default_service.go +++ b/pkg/apiserver-impl/api_default_service.go @@ -7,57 +7,57 @@ import ( "net/http" ) -// DefaultApiService is a service that implements the logic for the DefaultApiServicer +// DefaultAPIService is a service that implements the logic for the DefaultApiServicer // This service should implement the business logic for every endpoint for the DefaultApi API. // Include any external packages or services that will be required by this service. -type DefaultApiService struct { +type DefaultAPIService struct { } // NewDefaultApiService creates a default api service -func NewDefaultApiService() openapi.DefaultApiServicer { - return &DefaultApiService{} +func NewDefaultApiService() openapi.DefaultAPIServicer { + return &DefaultAPIService{} } // ComponentCommandPost - -func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) { +func (s *DefaultAPIService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) { // TODO - update ComponentCommandPost with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. - //TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ... - //return Response(200, GeneralSuccess{}), nil + // TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ... + // return Response(200, GeneralSuccess{}), nil return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentCommandPost method not implemented") } // ComponentGet - -func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) { +func (s *DefaultAPIService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update ComponentGet with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. - //TODO: Uncomment the next line to return response Response(200, ComponentGet200Response{}) or use other options such as http.Ok ... - //return Response(200, ComponentGet200Response{}), nil + // TODO: Uncomment the next line to return response Response(200, ComponentGet200Response{}) or use other options such as http.Ok ... + // return Response(200, ComponentGet200Response{}), nil return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentGet method not implemented") } // InstanceDelete - -func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) { +func (s *DefaultAPIService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update InstanceDelete with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. - //TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ... - //return Response(200, GeneralSuccess{}), nil + // TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ... + // return Response(200, GeneralSuccess{}), nil return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceDelete method not implemented") } // InstanceGet - -func (s *DefaultApiService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) { +func (s *DefaultAPIService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update InstanceGet with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. - //TODO: Uncomment the next line to return response Response(200, InstanceGet200Response{}) or use other options such as http.Ok ... - //return Response(200, InstanceGet200Response{}), nil + // TODO: Uncomment the next line to return response Response(200, InstanceGet200Response{}) or use other options such as http.Ok ... + // return Response(200, InstanceGet200Response{}), nil return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceGet method not implemented") } diff --git a/pkg/apiserver-impl/starterserver.go b/pkg/apiserver-impl/starterserver.go index f1910672da9..0ddee76488d 100644 --- a/pkg/apiserver-impl/starterserver.go +++ b/pkg/apiserver-impl/starterserver.go @@ -13,7 +13,7 @@ import ( func StartServer(ctx context.Context, cancelFunc context.CancelFunc, port int, stateClient state.Client) { DefaultApiService := NewDefaultApiService() - DefaultApiController := openapi.NewDefaultApiController(DefaultApiService) + DefaultApiController := openapi.NewDefaultAPIController(DefaultApiService) router := openapi.NewRouter(DefaultApiController) From 442ea1053b2a0d427b735b6bb707bd2a0b56232c Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Sat, 17 Jun 2023 12:22:48 +0530 Subject: [PATCH 5/7] Add integration test Signed-off-by: Parthvi Vala --- tests/helper/helper_dev.go | 37 ++++++++-- tests/integration/cmd_dev_api_server_test.go | 73 ++++++++++++++++++++ 2 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 tests/integration/cmd_dev_api_server_test.go diff --git a/tests/helper/helper_dev.go b/tests/helper/helper_dev.go index 428d71bc74d..2e80dd02c53 100644 --- a/tests/helper/helper_dev.go +++ b/tests/helper/helper_dev.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "regexp" + "strings" "time" "github.com/ActiveState/termtest/expect" @@ -110,13 +111,14 @@ import ( */ type DevSession struct { - session *gexec.Session - stopped bool - console *expect.Console - address string - StdOut string - ErrOut string - Endpoints map[string]string + session *gexec.Session + stopped bool + console *expect.Console + address string + StdOut string + ErrOut string + Endpoints map[string]string + APIServerEndpoint string } type DevSessionOpts struct { @@ -128,6 +130,8 @@ type DevSessionOpts struct { NoWatch bool NoCommands bool CustomAddress string + StartAPIServer bool + APIServerPort int } // StartDevMode starts a dev session with `odo dev` @@ -156,6 +160,12 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) { if options.CustomAddress != "" { args = append(args, "--address", options.CustomAddress) } + if options.StartAPIServer { + args = append(args, "--api-server") + if options.APIServerPort != 0 { + args = append(args, "--api-server-port", fmt.Sprintf("%d", options.APIServerPort)) + } + } args = append(args, options.CmdlineArgs...) cmd := Cmd("odo", args...) cmd.Cmd.Stdin = c.Tty() @@ -186,6 +196,10 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) { result.StdOut = string(outContents) result.ErrOut = string(errContents) result.Endpoints = getPorts(string(outContents), options.CustomAddress) + if options.StartAPIServer { + // errContents because the server message is still printed as a log/warning + result.APIServerEndpoint = getAPIServerPort(string(errContents)) + } return result, nil } @@ -358,3 +372,12 @@ func getPorts(s, address string) map[string]string { } return result } + +// getAPIServerPort returns the address at which api server is running +// +// `I0617 11:40:44.124391 49578 starterserver.go:36] API Server started at localhost:20000/api/v1` +func getAPIServerPort(s string) string { + re := regexp.MustCompile(`(API Server started at localhost:[0-9]+\/api\/v1)`) + matches := re.FindString(s) + return strings.Split(matches, "at ")[1] +} diff --git a/tests/integration/cmd_dev_api_server_test.go b/tests/integration/cmd_dev_api_server_test.go new file mode 100644 index 00000000000..977e472f015 --- /dev/null +++ b/tests/integration/cmd_dev_api_server_test.go @@ -0,0 +1,73 @@ +package integration + +import ( + "fmt" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/redhat-developer/odo/tests/helper" + "net/http" + "path/filepath" +) + +var _ = Describe("odo dev command with api server tests", func() { + var cmpName string + var commonVar helper.CommonVar + + // This is run before every Spec (It) + var _ = BeforeEach(func() { + commonVar = helper.CommonBeforeEach() + cmpName = helper.RandString(6) + helper.Chdir(commonVar.Context) + Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeFalse()) + }) + + // This is run after every Spec (It) + var _ = AfterEach(func() { + helper.CommonAfterEach(commonVar) + }) + for _, podman := range []bool{false, true} { + podman := podman + for _, customPort := range []bool{false, true} { + customPort := customPort + When("the component is bootstrapped", func() { + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"), cmpName) + }) + When(fmt.Sprintf("odo dev is run with --api-server flag (custom api server port=%v)", customPort), func() { + var ( + devSession helper.DevSession + localPort = helper.GetCustomStartPort() + ) + BeforeEach(func() { + opts := helper.DevSessionOpts{ + RunOnPodman: podman, + StartAPIServer: true, + EnvVars: []string{"ODO_EXPERIMENTAL_MODE=true"}, + } + if customPort { + opts.APIServerPort = localPort + } + var err error + devSession, err = helper.StartDevMode(opts) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + It("should start the Dev server when --api-server flag is passed", func() { + if customPort { + Expect(devSession.APIServerEndpoint).To(ContainSubstring(fmt.Sprintf("%d", localPort))) + } + url := fmt.Sprintf("http://%s/instance", devSession.APIServerEndpoint) + resp, err := http.Get(url) + Expect(err).ToNot(HaveOccurred()) + // TODO: Change this once it is implemented + Expect(resp.StatusCode).To(BeEquivalentTo(http.StatusNotImplemented)) + }) + }) + }) + } + } +}) From 92f41068f4249108300b0ab987a10188b458a1c3 Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Mon, 19 Jun 2023 14:29:26 +0530 Subject: [PATCH 6/7] Use label podman Signed-off-by: Parthvi Vala --- tests/integration/cmd_dev_api_server_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/cmd_dev_api_server_test.go b/tests/integration/cmd_dev_api_server_test.go index 977e472f015..237fd4390ca 100644 --- a/tests/integration/cmd_dev_api_server_test.go +++ b/tests/integration/cmd_dev_api_server_test.go @@ -29,7 +29,7 @@ var _ = Describe("odo dev command with api server tests", func() { podman := podman for _, customPort := range []bool{false, true} { customPort := customPort - When("the component is bootstrapped", func() { + When("the component is bootstrapped", helper.LabelPodmanIf(podman, func() { BeforeEach(func() { helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"), cmpName) @@ -67,7 +67,7 @@ var _ = Describe("odo dev command with api server tests", func() { Expect(resp.StatusCode).To(BeEquivalentTo(http.StatusNotImplemented)) }) }) - }) + })) } } }) From 00844bb22d42392246fa240e6014276627e584fc Mon Sep 17 00:00:00 2001 From: Parthvi Vala Date: Mon, 19 Jun 2023 16:45:04 +0530 Subject: [PATCH 7/7] Regenerate the api files with openapitool v6.6.0 and changes from review Signed-off-by: Parthvi Vala Co-authored-by: Armel Soro --- Makefile | 8 +- pkg/apiserver-gen/.openapi-generator/VERSION | 2 +- pkg/apiserver-gen/README.md | 1 - pkg/apiserver-gen/api/openapi.yaml | 184 ---------------- pkg/apiserver-gen/go/api.go | 12 +- pkg/apiserver-gen/go/api_default.go | 54 ++--- pkg/apiserver-gen/go/helpers.go | 14 +- pkg/apiserver-gen/go/impl.go | 2 +- .../model__component_command_post_request.go | 24 +- .../go/model__component_get_200_response.go | 24 +- .../go/model__instance_get_200_response.go | 24 +- pkg/apiserver-gen/go/model_general_error.go | 24 +- pkg/apiserver-gen/go/model_general_success.go | 24 +- pkg/apiserver-gen/go/routers.go | 205 ++++++++++-------- pkg/apiserver-impl/api_default_service.go | 16 +- pkg/apiserver-impl/starterserver.go | 6 +- pkg/state/types.go | 2 +- 17 files changed, 219 insertions(+), 407 deletions(-) delete mode 100644 pkg/apiserver-gen/api/openapi.yaml diff --git a/Makefile b/Makefile index 6139c534a71..e53c7f7e1f8 100644 --- a/Makefile +++ b/Makefile @@ -232,8 +232,10 @@ test-doc-automation: # Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen; this will only generate interfaces # Actual implementation must be done inside pkg/apiserver-impl +# Apart from generating the files, this target also formats the generated files +# and removes openapi.yaml to avoid any confusion regarding ododevapispec.yaml file and which file to use. .PHONY: generate-apiserver -generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen; also go fmt the files +generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml inside pkg/apiserver-gen podman run --rm \ -v ${PWD}:/local \ docker.io/openapitools/openapi-generator-cli:v6.6.0 \ @@ -242,5 +244,5 @@ generate-apiserver: ## Generate OpenAPISpec library based on ododevapispec.yaml -g go-server \ -o /local/pkg/apiserver-gen \ --additional-properties=outputAsLibrary=true,onlyInterfaces=true,hideGenerationTimestamp=true && \ - echo "Formatting generated files:" && \ - go fmt ./pkg/apiserver-gen/... \ No newline at end of file + echo "Formatting generated files:" && go fmt ./pkg/apiserver-gen/... && \ + echo "Removing pkg/apiserver-gen/api/openapi.yaml" && rm ./pkg/apiserver-gen/api/openapi.yaml diff --git a/pkg/apiserver-gen/.openapi-generator/VERSION b/pkg/apiserver-gen/.openapi-generator/VERSION index 757e6740040..cd802a1ec4e 100644 --- a/pkg/apiserver-gen/.openapi-generator/VERSION +++ b/pkg/apiserver-gen/.openapi-generator/VERSION @@ -1 +1 @@ -7.0.0-SNAPSHOT \ No newline at end of file +6.6.0 \ No newline at end of file diff --git a/pkg/apiserver-gen/README.md b/pkg/apiserver-gen/README.md index ae9cc6cf584..b17264fc746 100644 --- a/pkg/apiserver-gen/README.md +++ b/pkg/apiserver-gen/README.md @@ -13,7 +13,6 @@ To see how to make this your own, look here: [README](https://openapi-generator.tech) - API version: 0.1 -- Build date: 2023-06-12T03:51:38.118965Z[Etc/UTC] ### Running the server diff --git a/pkg/apiserver-gen/api/openapi.yaml b/pkg/apiserver-gen/api/openapi.yaml deleted file mode 100644 index d6a392cc77f..00000000000 --- a/pkg/apiserver-gen/api/openapi.yaml +++ /dev/null @@ -1,184 +0,0 @@ -openapi: 3.0.2 -info: - description: API interface for 'odo dev' - title: odo dev - version: "0.1" -servers: -- url: /api/v1 -paths: - /instance: - delete: - description: Stop this 'odo dev' instance - responses: - "200": - content: - application/json: - example: - message: "'odo dev' instance with pid: 42 is shuting down." - schema: - $ref: '#/components/schemas/GeneralSuccess' - description: '''odo dev'' instance will shutdown.' - get: - description: Get information about the this 'odo dev' instance. - responses: - "200": - content: - application/json: - example: - componentDirectory: /Users/user/Documents/myproject - pid: 42 - schema: - $ref: '#/components/schemas/_instance_get_200_response' - description: Information about the this 'odo dev' instance. - /component: - get: - description: Get the Information about the component controlled by this 'odo - dev' instance. - responses: - "200": - content: - application/json: - example: - devfilePath: /home/tomas/Code/odo-examples/java-maven/devfile.yaml - devfileData: - devfile: - schemaVersion: 2.1.0 - metadata: - name: demo - version: 1.1.1 - displayName: Maven Java - description: Upstream Maven and OpenJDK 11 - tags: - - Java - - Maven - icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg - projectType: Maven - language: Java - components: - - name: tools - container: - image: quay.io/eclipse/che-java11-maven:next - env: - - name: DEBUG_PORT - value: "5858" - volumeMounts: - - name: m2 - path: /home/user/.m2 - memoryLimit: 512Mi - mountSources: true - dedicatedPod: false - endpoints: - - name: http-maven - targetPort: 8080 - secure: false - - name: m2 - volume: - ephemeral: false - starterProjects: - - name: springbootproject - git: - remotes: - origin: https://github.com/odo-devfiles/springboot-ex.git - commands: - - id: mvn-package - exec: - group: - kind: build - isDefault: true - commandLine: mvn -Dmaven.repo.local=/home/user/.m2/repository - package - component: tools - workingDir: "${PROJECT_SOURCE}" - hotReloadCapable: false - - id: run - exec: - group: - kind: run - isDefault: true - commandLine: java -jar target/*.jar - component: tools - workingDir: "${PROJECT_SOURCE}" - hotReloadCapable: false - - id: debug - exec: - group: - kind: debug - isDefault: true - commandLine: "java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n\ - \ -jar target/*.jar" - component: tools - workingDir: "${PROJECT_SOURCE}" - hotReloadCapable: false - supportedOdoFeatures: - dev: true - deploy: false - debug: true - runningIn: - deploy: false - dev: true - managedBy: odo - schema: - $ref: '#/components/schemas/_component_get_200_response' - description: Information about the component. - /component/command: - post: - description: Instruct 'odo dev' to perform given command on the component - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/_component_command_post_request' - responses: - "200": - content: - application/json: - example: - message: push was successfully executed - schema: - $ref: '#/components/schemas/GeneralSuccess' - description: command was successfully executed -components: - schemas: - GeneralSuccess: - example: - message: message - properties: - message: - type: string - type: object - GeneralError: - properties: - message: - type: string - type: object - _instance_get_200_response: - example: - componentDirectory: componentDirectory - pid: 0 - properties: - componentDirectory: - description: Directory on which this 'odo dev' instance is running - type: string - pid: - description: PID of the this 'odo dev' instance. - type: integer - type: object - _component_get_200_response: - example: - component: "{}" - properties: - component: - description: Description of the component. This is the same as output of - 'odo describe component -o json' - type: object - type: object - _component_command_post_request: - example: - action: push - properties: - name: - description: Name of the command that should be executed - enum: - - push - type: string - type: object diff --git a/pkg/apiserver-gen/go/api.go b/pkg/apiserver-gen/go/api.go index a6aef9f1f9c..c8e13b62193 100644 --- a/pkg/apiserver-gen/go/api.go +++ b/pkg/apiserver-gen/go/api.go @@ -14,21 +14,21 @@ import ( "net/http" ) -// DefaultAPIRouter defines the required methods for binding the api requests to a responses for the DefaultAPI -// The DefaultAPIRouter implementation should parse necessary information from the http request, -// pass the data to a DefaultAPIServicer to perform the required actions, then write the service results to the http response. -type DefaultAPIRouter interface { +// DefaultApiRouter defines the required methods for binding the api requests to a responses for the DefaultApi +// The DefaultApiRouter implementation should parse necessary information from the http request, +// pass the data to a DefaultApiServicer to perform the required actions, then write the service results to the http response. +type DefaultApiRouter interface { ComponentCommandPost(http.ResponseWriter, *http.Request) ComponentGet(http.ResponseWriter, *http.Request) InstanceDelete(http.ResponseWriter, *http.Request) InstanceGet(http.ResponseWriter, *http.Request) } -// DefaultAPIServicer defines the api actions for the DefaultAPI service +// DefaultApiServicer defines the api actions for the DefaultApi service // This interface intended to stay up to date with the openapi yaml used to generate it, // while the service implementation can be ignored with the .openapi-generator-ignore file // and updated with the logic required for the API. -type DefaultAPIServicer interface { +type DefaultApiServicer interface { ComponentCommandPost(context.Context, ComponentCommandPostRequest) (ImplResponse, error) ComponentGet(context.Context) (ImplResponse, error) InstanceDelete(context.Context) (ImplResponse, error) diff --git a/pkg/apiserver-gen/go/api_default.go b/pkg/apiserver-gen/go/api_default.go index cedf9c7653e..18988caae88 100644 --- a/pkg/apiserver-gen/go/api_default.go +++ b/pkg/apiserver-gen/go/api_default.go @@ -15,25 +15,25 @@ import ( "strings" ) -// DefaultAPIController binds http requests to an api service and writes the service results to the http response -type DefaultAPIController struct { - service DefaultAPIServicer +// DefaultApiController binds http requests to an api service and writes the service results to the http response +type DefaultApiController struct { + service DefaultApiServicer errorHandler ErrorHandler } -// DefaultAPIOption for how the controller is set up. -type DefaultAPIOption func(*DefaultAPIController) +// DefaultApiOption for how the controller is set up. +type DefaultApiOption func(*DefaultApiController) -// WithDefaultAPIErrorHandler inject ErrorHandler into controller -func WithDefaultAPIErrorHandler(h ErrorHandler) DefaultAPIOption { - return func(c *DefaultAPIController) { +// WithDefaultApiErrorHandler inject ErrorHandler into controller +func WithDefaultApiErrorHandler(h ErrorHandler) DefaultApiOption { + return func(c *DefaultApiController) { c.errorHandler = h } } -// NewDefaultAPIController creates a default api controller -func NewDefaultAPIController(s DefaultAPIServicer, opts ...DefaultAPIOption) Router { - controller := &DefaultAPIController{ +// NewDefaultApiController creates a default api controller +func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Router { + controller := &DefaultApiController{ service: s, errorHandler: DefaultErrorHandler, } @@ -45,25 +45,29 @@ func NewDefaultAPIController(s DefaultAPIServicer, opts ...DefaultAPIOption) Rou return controller } -// Routes returns all the api routes for the DefaultAPIController -func (c *DefaultAPIController) Routes() Routes { +// Routes returns all the api routes for the DefaultApiController +func (c *DefaultApiController) Routes() Routes { return Routes{ - "ComponentCommandPost": Route{ + { + "ComponentCommandPost", strings.ToUpper("Post"), "/api/v1/component/command", c.ComponentCommandPost, }, - "ComponentGet": Route{ + { + "ComponentGet", strings.ToUpper("Get"), "/api/v1/component", c.ComponentGet, }, - "InstanceDelete": Route{ + { + "InstanceDelete", strings.ToUpper("Delete"), "/api/v1/instance", c.InstanceDelete, }, - "InstanceGet": Route{ + { + "InstanceGet", strings.ToUpper("Get"), "/api/v1/instance", c.InstanceGet, @@ -72,7 +76,7 @@ func (c *DefaultAPIController) Routes() Routes { } // ComponentCommandPost - -func (c *DefaultAPIController) ComponentCommandPost(w http.ResponseWriter, r *http.Request) { +func (c *DefaultApiController) ComponentCommandPost(w http.ResponseWriter, r *http.Request) { componentCommandPostRequestParam := ComponentCommandPostRequest{} d := json.NewDecoder(r.Body) d.DisallowUnknownFields() @@ -84,10 +88,6 @@ func (c *DefaultAPIController) ComponentCommandPost(w http.ResponseWriter, r *ht c.errorHandler(w, r, err, nil) return } - if err := AssertComponentCommandPostRequestConstraints(componentCommandPostRequestParam); err != nil { - c.errorHandler(w, r, err, nil) - return - } result, err := c.service.ComponentCommandPost(r.Context(), componentCommandPostRequestParam) // If an error occurred, encode the error with the status code if err != nil { @@ -96,10 +96,11 @@ func (c *DefaultAPIController) ComponentCommandPost(w http.ResponseWriter, r *ht } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) + } // ComponentGet - -func (c *DefaultAPIController) ComponentGet(w http.ResponseWriter, r *http.Request) { +func (c *DefaultApiController) ComponentGet(w http.ResponseWriter, r *http.Request) { result, err := c.service.ComponentGet(r.Context()) // If an error occurred, encode the error with the status code if err != nil { @@ -108,10 +109,11 @@ func (c *DefaultAPIController) ComponentGet(w http.ResponseWriter, r *http.Reque } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) + } // InstanceDelete - -func (c *DefaultAPIController) InstanceDelete(w http.ResponseWriter, r *http.Request) { +func (c *DefaultApiController) InstanceDelete(w http.ResponseWriter, r *http.Request) { result, err := c.service.InstanceDelete(r.Context()) // If an error occurred, encode the error with the status code if err != nil { @@ -120,10 +122,11 @@ func (c *DefaultAPIController) InstanceDelete(w http.ResponseWriter, r *http.Req } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) + } // InstanceGet - -func (c *DefaultAPIController) InstanceGet(w http.ResponseWriter, r *http.Request) { +func (c *DefaultApiController) InstanceGet(w http.ResponseWriter, r *http.Request) { result, err := c.service.InstanceGet(r.Context()) // If an error occurred, encode the error with the status code if err != nil { @@ -132,4 +135,5 @@ func (c *DefaultAPIController) InstanceGet(w http.ResponseWriter, r *http.Reques } // If no error, encode the body and the result code EncodeJSONResponse(result.Body, &result.Code, w) + } diff --git a/pkg/apiserver-gen/go/helpers.go b/pkg/apiserver-gen/go/helpers.go index 325e7bfb38a..48f03265ee0 100644 --- a/pkg/apiserver-gen/go/helpers.go +++ b/pkg/apiserver-gen/go/helpers.go @@ -28,23 +28,17 @@ func IsZeroValue(val interface{}) bool { // AssertRecurseInterfaceRequired recursively checks each struct in a slice against the callback. // This method traverse nested slices in a preorder fashion. -func AssertRecurseInterfaceRequired[T any](obj interface{}, callback func(T) error) error { +func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) } // AssertRecurseValueRequired checks each struct in the nested slice against the callback. -// This method traverse nested slices in a preorder fashion. ErrTypeAssertionError is thrown if -// the underlying struct does not match type T. -func AssertRecurseValueRequired[T any](value reflect.Value, callback func(T) error) error { +// This method traverse nested slices in a preorder fashion. +func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { switch value.Kind() { // If it is a struct we check using callback case reflect.Struct: - obj, ok := value.Interface().(T) - if !ok { - return ErrTypeAssertionError - } - - if err := callback(obj); err != nil { + if err := callback(value.Interface()); err != nil { return err } diff --git a/pkg/apiserver-gen/go/impl.go b/pkg/apiserver-gen/go/impl.go index 33bf2952281..9ca7add4e99 100644 --- a/pkg/apiserver-gen/go/impl.go +++ b/pkg/apiserver-gen/go/impl.go @@ -9,7 +9,7 @@ package openapi -// ImplResponse defines an implementation response with error code and the associated body +// ImplResponse response defines an error code with the associated body type ImplResponse struct { Code int Body interface{} diff --git a/pkg/apiserver-gen/go/model__component_command_post_request.go b/pkg/apiserver-gen/go/model__component_command_post_request.go index 49802444f83..26e4fc63be3 100644 --- a/pkg/apiserver-gen/go/model__component_command_post_request.go +++ b/pkg/apiserver-gen/go/model__component_command_post_request.go @@ -9,29 +9,25 @@ package openapi -import ( - "encoding/json" -) - type ComponentCommandPostRequest struct { // Name of the command that should be executed Name string `json:"name,omitempty"` } -// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. -func (m *ComponentCommandPostRequest) UnmarshalJSON(data []byte) error { - - type Alias ComponentCommandPostRequest // To avoid infinite recursion - return json.Unmarshal(data, (*Alias)(m)) -} - // AssertComponentCommandPostRequestRequired checks if the required fields are not zero-ed func AssertComponentCommandPostRequestRequired(obj ComponentCommandPostRequest) error { return nil } -// AssertComponentCommandPostRequestConstraints checks if the values respects the defined constraints -func AssertComponentCommandPostRequestConstraints(obj ComponentCommandPostRequest) error { - return nil +// AssertRecurseComponentCommandPostRequestRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ComponentCommandPostRequest (e.g. [][]ComponentCommandPostRequest), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseComponentCommandPostRequestRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aComponentCommandPostRequest, ok := obj.(ComponentCommandPostRequest) + if !ok { + return ErrTypeAssertionError + } + return AssertComponentCommandPostRequestRequired(aComponentCommandPostRequest) + }) } diff --git a/pkg/apiserver-gen/go/model__component_get_200_response.go b/pkg/apiserver-gen/go/model__component_get_200_response.go index 33fa53cc02a..fc416bd3848 100644 --- a/pkg/apiserver-gen/go/model__component_get_200_response.go +++ b/pkg/apiserver-gen/go/model__component_get_200_response.go @@ -9,29 +9,25 @@ package openapi -import ( - "encoding/json" -) - type ComponentGet200Response struct { // Description of the component. This is the same as output of 'odo describe component -o json' Component map[string]interface{} `json:"component,omitempty"` } -// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. -func (m *ComponentGet200Response) UnmarshalJSON(data []byte) error { - - type Alias ComponentGet200Response // To avoid infinite recursion - return json.Unmarshal(data, (*Alias)(m)) -} - // AssertComponentGet200ResponseRequired checks if the required fields are not zero-ed func AssertComponentGet200ResponseRequired(obj ComponentGet200Response) error { return nil } -// AssertComponentGet200ResponseConstraints checks if the values respects the defined constraints -func AssertComponentGet200ResponseConstraints(obj ComponentGet200Response) error { - return nil +// AssertRecurseComponentGet200ResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ComponentGet200Response (e.g. [][]ComponentGet200Response), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseComponentGet200ResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aComponentGet200Response, ok := obj.(ComponentGet200Response) + if !ok { + return ErrTypeAssertionError + } + return AssertComponentGet200ResponseRequired(aComponentGet200Response) + }) } diff --git a/pkg/apiserver-gen/go/model__instance_get_200_response.go b/pkg/apiserver-gen/go/model__instance_get_200_response.go index 1c9e7a9ae70..1485bd19b4e 100644 --- a/pkg/apiserver-gen/go/model__instance_get_200_response.go +++ b/pkg/apiserver-gen/go/model__instance_get_200_response.go @@ -9,10 +9,6 @@ package openapi -import ( - "encoding/json" -) - type InstanceGet200Response struct { // Directory on which this 'odo dev' instance is running @@ -22,19 +18,19 @@ type InstanceGet200Response struct { Pid int32 `json:"pid,omitempty"` } -// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. -func (m *InstanceGet200Response) UnmarshalJSON(data []byte) error { - - type Alias InstanceGet200Response // To avoid infinite recursion - return json.Unmarshal(data, (*Alias)(m)) -} - // AssertInstanceGet200ResponseRequired checks if the required fields are not zero-ed func AssertInstanceGet200ResponseRequired(obj InstanceGet200Response) error { return nil } -// AssertInstanceGet200ResponseConstraints checks if the values respects the defined constraints -func AssertInstanceGet200ResponseConstraints(obj InstanceGet200Response) error { - return nil +// AssertRecurseInstanceGet200ResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of InstanceGet200Response (e.g. [][]InstanceGet200Response), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseInstanceGet200ResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aInstanceGet200Response, ok := obj.(InstanceGet200Response) + if !ok { + return ErrTypeAssertionError + } + return AssertInstanceGet200ResponseRequired(aInstanceGet200Response) + }) } diff --git a/pkg/apiserver-gen/go/model_general_error.go b/pkg/apiserver-gen/go/model_general_error.go index 2308bded693..2d47ac83f40 100644 --- a/pkg/apiserver-gen/go/model_general_error.go +++ b/pkg/apiserver-gen/go/model_general_error.go @@ -9,27 +9,23 @@ package openapi -import ( - "encoding/json" -) - type GeneralError struct { Message string `json:"message,omitempty"` } -// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. -func (m *GeneralError) UnmarshalJSON(data []byte) error { - - type Alias GeneralError // To avoid infinite recursion - return json.Unmarshal(data, (*Alias)(m)) -} - // AssertGeneralErrorRequired checks if the required fields are not zero-ed func AssertGeneralErrorRequired(obj GeneralError) error { return nil } -// AssertGeneralErrorConstraints checks if the values respects the defined constraints -func AssertGeneralErrorConstraints(obj GeneralError) error { - return nil +// AssertRecurseGeneralErrorRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of GeneralError (e.g. [][]GeneralError), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseGeneralErrorRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aGeneralError, ok := obj.(GeneralError) + if !ok { + return ErrTypeAssertionError + } + return AssertGeneralErrorRequired(aGeneralError) + }) } diff --git a/pkg/apiserver-gen/go/model_general_success.go b/pkg/apiserver-gen/go/model_general_success.go index da95f88efa2..3ef5848b2bb 100644 --- a/pkg/apiserver-gen/go/model_general_success.go +++ b/pkg/apiserver-gen/go/model_general_success.go @@ -9,27 +9,23 @@ package openapi -import ( - "encoding/json" -) - type GeneralSuccess struct { Message string `json:"message,omitempty"` } -// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified. -func (m *GeneralSuccess) UnmarshalJSON(data []byte) error { - - type Alias GeneralSuccess // To avoid infinite recursion - return json.Unmarshal(data, (*Alias)(m)) -} - // AssertGeneralSuccessRequired checks if the required fields are not zero-ed func AssertGeneralSuccessRequired(obj GeneralSuccess) error { return nil } -// AssertGeneralSuccessConstraints checks if the values respects the defined constraints -func AssertGeneralSuccessConstraints(obj GeneralSuccess) error { - return nil +// AssertRecurseGeneralSuccessRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of GeneralSuccess (e.g. [][]GeneralSuccess), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseGeneralSuccessRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aGeneralSuccess, ok := obj.(GeneralSuccess) + if !ok { + return ErrTypeAssertionError + } + return AssertGeneralSuccessRequired(aGeneralSuccess) + }) } diff --git a/pkg/apiserver-gen/go/routers.go b/pkg/apiserver-gen/go/routers.go index 806dccff48c..e698f8ce1e9 100644 --- a/pkg/apiserver-gen/go/routers.go +++ b/pkg/apiserver-gen/go/routers.go @@ -23,13 +23,14 @@ import ( // A Route defines the parameters for an api endpoint type Route struct { + Name string Method string Pattern string HandlerFunc http.HandlerFunc } -// Routes is a map of defined api endpoints -type Routes map[string]Route +// Routes are a collection of defined api endpoints +type Routes []Route // Router defines the required methods for retrieving api routes type Router interface { @@ -37,22 +38,20 @@ type Router interface { } const errMsgRequiredMissing = "required parameter is missing" -const errMsgMinValueConstraint = "provided parameter is not respecting minimum value constraint" -const errMsgMaxValueConstraint = "provided parameter is not respecting maximum value constraint" // NewRouter creates a new router for any number of api routers func NewRouter(routers ...Router) *mux.Router { router := mux.NewRouter().StrictSlash(true) for _, api := range routers { - for name, route := range api.Routes() { + for _, route := range api.Routes() { var handler http.Handler handler = route.HandlerFunc - handler = Logger(handler, name) + handler = Logger(handler, route.Name) router. Methods(route.Method). Path(route.Pattern). - Name(name). + Name(route.Name). Handler(handler) } } @@ -134,119 +133,122 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error return file, nil } -type Number interface { - ~int32 | ~int64 | ~float32 | ~float64 -} +// parseFloatParameter parses a string parameter to an int64. +func parseFloatParameter(param string, bitSize int, required bool) (float64, error) { + if param == "" { + if required { + return 0, errors.New(errMsgRequiredMissing) + } -type ParseString[T Number | string | bool] func(v string) (T, error) + return 0, nil + } -// parseFloat64 parses a string parameter to an float64. -func parseFloat64(param string) (float64, error) { - return strconv.ParseFloat(param, 64) + return strconv.ParseFloat(param, bitSize) } -// parseFloat32 parses a string parameter to an float32. -func parseFloat32(param string) (float32, error) { - v, err := strconv.ParseFloat(param, 32) - return float32(v), err +// parseFloat64Parameter parses a string parameter to an float64. +func parseFloat64Parameter(param string, required bool) (float64, error) { + return parseFloatParameter(param, 64, required) } -// parseInt64 parses a string parameter to an int64. -func parseInt64(param string) (int64, error) { - return strconv.ParseInt(param, 10, 64) +// parseFloat32Parameter parses a string parameter to an float32. +func parseFloat32Parameter(param string, required bool) (float32, error) { + val, err := parseFloatParameter(param, 32, required) + return float32(val), err } -// parseInt32 parses a string parameter to an int32. -func parseInt32(param string) (int32, error) { - val, err := strconv.ParseInt(param, 10, 32) - return int32(val), err +// parseIntParameter parses a string parameter to an int64. +func parseIntParameter(param string, bitSize int, required bool) (int64, error) { + if param == "" { + if required { + return 0, errors.New(errMsgRequiredMissing) + } + + return 0, nil + } + + return strconv.ParseInt(param, 10, bitSize) } -// parseBool parses a string parameter to an bool. -func parseBool(param string) (bool, error) { - return strconv.ParseBool(param) +// parseInt64Parameter parses a string parameter to an int64. +func parseInt64Parameter(param string, required bool) (int64, error) { + return parseIntParameter(param, 64, required) } -type Operation[T Number | string | bool] func(actual string) (T, bool, error) +// parseInt32Parameter parses a string parameter to an int32. +func parseInt32Parameter(param string, required bool) (int32, error) { + val, err := parseIntParameter(param, 32, required) + return int32(val), err +} -func WithRequire[T Number | string | bool](parse ParseString[T]) Operation[T] { - var empty T - return func(actual string) (T, bool, error) { - if actual == "" { - return empty, false, errors.New(errMsgRequiredMissing) +// parseBoolParameter parses a string parameter to a bool +func parseBoolParameter(param string, required bool) (bool, error) { + if param == "" { + if required { + return false, errors.New(errMsgRequiredMissing) } - v, err := parse(actual) - return v, false, err + return false, nil } -} - -func WithDefaultOrParse[T Number | string | bool](def T, parse ParseString[T]) Operation[T] { - return func(actual string) (T, bool, error) { - if actual == "" { - return def, true, nil - } - v, err := parse(actual) - return v, false, err + val, err := strconv.ParseBool(param) + if err != nil { + return false, err } + + return bool(val), nil } -func WithParse[T Number | string | bool](parse ParseString[T]) Operation[T] { - return func(actual string) (T, bool, error) { - v, err := parse(actual) - return v, false, err +// parseFloat64ArrayParameter parses a string parameter containing array of values to []Float64. +func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64, error) { + if param == "" { + if required { + return nil, errors.New(errMsgRequiredMissing) + } + + return nil, nil } -} -type Constraint[T Number | string | bool] func(actual T) error + str := strings.Split(param, delim) + floats := make([]float64, len(str)) -func WithMinimum[T Number](expected T) Constraint[T] { - return func(actual T) error { - if actual < expected { - return errors.New(errMsgMinValueConstraint) + for i, s := range str { + if v, err := strconv.ParseFloat(s, 64); err != nil { + return nil, err + } else { + floats[i] = v } - - return nil } + + return floats, nil } -func WithMaximum[T Number](expected T) Constraint[T] { - return func(actual T) error { - if actual > expected { - return errors.New(errMsgMaxValueConstraint) +// parseFloat32ArrayParameter parses a string parameter containing array of values to []float32. +func parseFloat32ArrayParameter(param, delim string, required bool) ([]float32, error) { + if param == "" { + if required { + return nil, errors.New(errMsgRequiredMissing) } - return nil + return nil, nil } -} -// parseNumericParameter parses a numeric parameter to its respective type. -func parseNumericParameter[T Number](param string, fn Operation[T], checks ...Constraint[T]) (T, error) { - v, ok, err := fn(param) - if err != nil { - return 0, err - } + str := strings.Split(param, delim) + floats := make([]float32, len(str)) - if !ok { - for _, check := range checks { - if err := check(v); err != nil { - return 0, err - } + for i, s := range str { + if v, err := strconv.ParseFloat(s, 32); err != nil { + return nil, err + } else { + floats[i] = float32(v) } } - return v, nil -} - -// parseBoolParameter parses a string parameter to a bool -func parseBoolParameter(param string, fn Operation[bool]) (bool, error) { - v, _, err := fn(param) - return v, err + return floats, nil } -// parseNumericArrayParameter parses a string parameter containing array of values to its respective type. -func parseNumericArrayParameter[T Number](param, delim string, required bool, fn Operation[T], checks ...Constraint[T]) ([]T, error) { +// parseInt64ArrayParameter parses a string parameter containing array of values to []int64. +func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) { if param == "" { if required { return nil, errors.New(errMsgRequiredMissing) @@ -256,24 +258,39 @@ func parseNumericArrayParameter[T Number](param, delim string, required bool, fn } str := strings.Split(param, delim) - values := make([]T, len(str)) + ints := make([]int64, len(str)) for i, s := range str { - v, ok, err := fn(s) - if err != nil { + if v, err := strconv.ParseInt(s, 10, 64); err != nil { return nil, err + } else { + ints[i] = v } + } + + return ints, nil +} - if !ok { - for _, check := range checks { - if err := check(v); err != nil { - return nil, err - } - } +// parseInt32ArrayParameter parses a string parameter containing array of values to []int32. +func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) { + if param == "" { + if required { + return nil, errors.New(errMsgRequiredMissing) } - values[i] = v + return nil, nil + } + + str := strings.Split(param, delim) + ints := make([]int32, len(str)) + + for i, s := range str { + if v, err := strconv.ParseInt(s, 10, 32); err != nil { + return nil, err + } else { + ints[i] = int32(v) + } } - return values, nil + return ints, nil } diff --git a/pkg/apiserver-impl/api_default_service.go b/pkg/apiserver-impl/api_default_service.go index 80558ae31c1..3af09574950 100644 --- a/pkg/apiserver-impl/api_default_service.go +++ b/pkg/apiserver-impl/api_default_service.go @@ -7,19 +7,19 @@ import ( "net/http" ) -// DefaultAPIService is a service that implements the logic for the DefaultApiServicer +// DefaultApiService is a service that implements the logic for the DefaultApiServicer // This service should implement the business logic for every endpoint for the DefaultApi API. // Include any external packages or services that will be required by this service. -type DefaultAPIService struct { +type DefaultApiService struct { } // NewDefaultApiService creates a default api service -func NewDefaultApiService() openapi.DefaultAPIServicer { - return &DefaultAPIService{} +func NewDefaultApiService() openapi.DefaultApiServicer { + return &DefaultApiService{} } // ComponentCommandPost - -func (s *DefaultAPIService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) { +func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) { // TODO - update ComponentCommandPost with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. @@ -30,7 +30,7 @@ func (s *DefaultAPIService) ComponentCommandPost(ctx context.Context, componentC } // ComponentGet - -func (s *DefaultAPIService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) { +func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update ComponentGet with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. @@ -41,7 +41,7 @@ func (s *DefaultAPIService) ComponentGet(ctx context.Context) (openapi.ImplRespo } // InstanceDelete - -func (s *DefaultAPIService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) { +func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update InstanceDelete with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. @@ -52,7 +52,7 @@ func (s *DefaultAPIService) InstanceDelete(ctx context.Context) (openapi.ImplRes } // InstanceGet - -func (s *DefaultAPIService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) { +func (s *DefaultApiService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) { // TODO - update InstanceGet with the required logic for this service method. // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. diff --git a/pkg/apiserver-impl/starterserver.go b/pkg/apiserver-impl/starterserver.go index 0ddee76488d..09174e2d329 100644 --- a/pkg/apiserver-impl/starterserver.go +++ b/pkg/apiserver-impl/starterserver.go @@ -12,10 +12,10 @@ import ( func StartServer(ctx context.Context, cancelFunc context.CancelFunc, port int, stateClient state.Client) { - DefaultApiService := NewDefaultApiService() - DefaultApiController := openapi.NewDefaultAPIController(DefaultApiService) + defaultApiService := NewDefaultApiService() + defaultApiController := openapi.NewDefaultApiController(defaultApiService) - router := openapi.NewRouter(DefaultApiController) + router := openapi.NewRouter(defaultApiController) var err error diff --git a/pkg/state/types.go b/pkg/state/types.go index 05002f1b7a1..feedf52b7b2 100644 --- a/pkg/state/types.go +++ b/pkg/state/types.go @@ -11,5 +11,5 @@ type Content struct { Platform string `json:"platform"` // ForwardedPorts are the ports forwarded during odo dev session ForwardedPorts []api.ForwardedPort `json:"forwardedPorts"` - APIServerPort int `json:"APIServerPort"` + APIServerPort int `json:"apiServerPort"` }