From 71e4d3bf814b0d5c703a2969e7e3d0dc413067d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Dageli=C4=87?= Date: Wed, 24 Apr 2024 18:12:21 +0200 Subject: [PATCH] fix: required project name (#426) Signed-off-by: Ivan Dagelic --- pkg/api/docs/docs.go | 9 +- pkg/api/docs/swagger.json | 9 +- pkg/api/docs/swagger.yaml | 6 +- pkg/api/server.go | 3 + pkg/api/validator.go | 53 +++++++++ pkg/cmd/workspace/create.go | 3 +- pkg/server/workspaces/dto/workspace.go | 5 +- pkg/server/workspaces/service_test.go | 1 - pkg/serverapiclient/api/openapi.yaml | 9 +- .../docs/CreateWorkspaceRequest.md | 9 +- .../docs/CreateWorkspaceRequestProject.md | 35 +----- pkg/serverapiclient/docs/WorkspaceAPI.md | 2 +- .../model_create_workspace_request.go | 70 ++++++++--- .../model_create_workspace_request_project.go | 112 +++++++++--------- 14 files changed, 189 insertions(+), 137 deletions(-) create mode 100644 pkg/api/validator.go diff --git a/pkg/api/docs/docs.go b/pkg/api/docs/docs.go index 75b21b5d99..e07f77a94d 100644 --- a/pkg/api/docs/docs.go +++ b/pkg/api/docs/docs.go @@ -1084,6 +1084,9 @@ const docTemplate = `{ }, "CreateWorkspaceRequest": { "type": "object", + "required": [ + "projects" + ], "properties": { "id": { "type": "string" @@ -1104,6 +1107,9 @@ const docTemplate = `{ }, "CreateWorkspaceRequestProject": { "type": "object", + "required": [ + "name" + ], "properties": { "envVars": { "type": "object", @@ -1111,9 +1117,6 @@ const docTemplate = `{ "type": "string" } }, - "id": { - "type": "string" - }, "image": { "type": "string" }, diff --git a/pkg/api/docs/swagger.json b/pkg/api/docs/swagger.json index bc3e317656..ba84d862ef 100644 --- a/pkg/api/docs/swagger.json +++ b/pkg/api/docs/swagger.json @@ -1081,6 +1081,9 @@ }, "CreateWorkspaceRequest": { "type": "object", + "required": [ + "projects" + ], "properties": { "id": { "type": "string" @@ -1101,6 +1104,9 @@ }, "CreateWorkspaceRequestProject": { "type": "object", + "required": [ + "name" + ], "properties": { "envVars": { "type": "object", @@ -1108,9 +1114,6 @@ "type": "string" } }, - "id": { - "type": "string" - }, "image": { "type": "string" }, diff --git a/pkg/api/docs/swagger.yaml b/pkg/api/docs/swagger.yaml index 0946ad323f..4687a13b2e 100644 --- a/pkg/api/docs/swagger.yaml +++ b/pkg/api/docs/swagger.yaml @@ -31,6 +31,8 @@ definitions: type: array target: type: string + required: + - projects type: object CreateWorkspaceRequestProject: properties: @@ -38,8 +40,6 @@ definitions: additionalProperties: type: string type: object - id: - type: string image: type: string name: @@ -48,6 +48,8 @@ definitions: $ref: '#/definitions/CreateWorkspaceRequestProjectSource' user: type: string + required: + - name type: object CreateWorkspaceRequestProjectSource: properties: diff --git a/pkg/api/server.go b/pkg/api/server.go index 56813b5c9c..96e1dac113 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -41,6 +41,7 @@ import ( "github.com/daytonaio/daytona/pkg/api/controllers/workspace" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" log "github.com/sirupsen/logrus" swaggerfiles "github.com/swaggo/files" @@ -69,6 +70,8 @@ func (a *ApiServer) Start() error { docs.SwaggerInfo.Description = "Daytona Server API" docs.SwaggerInfo.Title = "Daytona Server API" + binding.Validator = new(defaultValidator) + if mode, ok := os.LookupEnv("DAYTONA_SERVER_MODE"); ok && mode == "development" { a.router = gin.Default() a.router.Use(cors.New(cors.Config{ diff --git a/pkg/api/validator.go b/pkg/api/validator.go new file mode 100644 index 0000000000..a7c1493bcd --- /dev/null +++ b/pkg/api/validator.go @@ -0,0 +1,53 @@ +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package api + +import ( + "reflect" + "sync" + + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" +) + +type defaultValidator struct { + once sync.Once + validate *validator.Validate +} + +var _ binding.StructValidator = &defaultValidator{} + +func (v *defaultValidator) ValidateStruct(obj interface{}) error { + if kindOfData(obj) == reflect.Struct { + v.lazyinit() + + if err := v.validate.Struct(obj); err != nil { + return err + } + } + + return nil +} + +func (v *defaultValidator) Engine() interface{} { + v.lazyinit() + return v.validate +} + +func (v *defaultValidator) lazyinit() { + v.once.Do(func() { + v.validate = validator.New(validator.WithRequiredStructEnabled()) + }) +} + +func kindOfData(data interface{}) reflect.Kind { + value := reflect.ValueOf(data) + valueType := value.Kind() + + if valueType == reflect.Ptr { + valueType = value.Elem().Kind() + } + + return valueType +} diff --git a/pkg/cmd/workspace/create.go b/pkg/cmd/workspace/create.go index ce823715ee..38b2c93895 100644 --- a/pkg/cmd/workspace/create.go +++ b/pkg/cmd/workspace/create.go @@ -123,8 +123,7 @@ var CreateCmd = &cobra.Command{ projectNameSlugRegex := regexp.MustCompile(`[^a-zA-Z0-9-]`) projectName := projectNameSlugRegex.ReplaceAllString(strings.TrimSuffix(strings.ToLower(filepath.Base(*repo.Url)), ".git"), "-") projects = append(projects, serverapiclient.CreateWorkspaceRequestProject{ - Id: &projectName, - Name: &projectName, + Name: projectName, Source: &serverapiclient.CreateWorkspaceRequestProjectSource{ Repository: &repo, }, diff --git a/pkg/server/workspaces/dto/workspace.go b/pkg/server/workspaces/dto/workspace.go index 97f0dc1811..0af6351511 100644 --- a/pkg/server/workspaces/dto/workspace.go +++ b/pkg/server/workspaces/dto/workspace.go @@ -23,8 +23,7 @@ type CreateWorkspaceRequestProjectSource struct { } // @name CreateWorkspaceRequestProjectSource type CreateWorkspaceRequestProject struct { - Id string `json:"id"` - Name string `json:"name"` + Name string `json:"name" validate:"required,gt=0"` Image *string `json:"image,omitempty"` User *string `json:"user,omitempty"` Source CreateWorkspaceRequestProjectSource `json:"source"` @@ -35,5 +34,5 @@ type CreateWorkspaceRequest struct { Id string `json:"id"` Name string `json:"name"` Target string `json:"target"` - Projects []CreateWorkspaceRequestProject `json:"projects"` + Projects []CreateWorkspaceRequestProject `json:"projects" validate:"required,gt=0,dive"` } // @name CreateWorkspaceRequest diff --git a/pkg/server/workspaces/service_test.go b/pkg/server/workspaces/service_test.go index 2211ae2a3f..67231adc36 100644 --- a/pkg/server/workspaces/service_test.go +++ b/pkg/server/workspaces/service_test.go @@ -53,7 +53,6 @@ var createWorkspaceRequest = dto.CreateWorkspaceRequest{ Target: target.Name, Projects: []dto.CreateWorkspaceRequestProject{ { - Id: "project1", Name: "project1", Source: dto.CreateWorkspaceRequestProjectSource{ Repository: &gitprovider.GitRepository{ diff --git a/pkg/serverapiclient/api/openapi.yaml b/pkg/serverapiclient/api/openapi.yaml index 13484a931d..c5c11ab29e 100644 --- a/pkg/serverapiclient/api/openapi.yaml +++ b/pkg/serverapiclient/api/openapi.yaml @@ -787,7 +787,6 @@ components: envVars: key: envVars name: name - id: id source: repository: owner: owner @@ -804,7 +803,6 @@ components: envVars: key: envVars name: name - id: id source: repository: owner: owner @@ -831,6 +829,8 @@ components: type: array target: type: string + required: + - projects type: object CreateWorkspaceRequestProject: example: @@ -838,7 +838,6 @@ components: envVars: key: envVars name: name - id: id source: repository: owner: owner @@ -856,8 +855,6 @@ components: additionalProperties: type: string type: object - id: - type: string image: type: string name: @@ -866,6 +863,8 @@ components: $ref: '#/components/schemas/CreateWorkspaceRequestProjectSource' user: type: string + required: + - name type: object CreateWorkspaceRequestProjectSource: example: diff --git a/pkg/serverapiclient/docs/CreateWorkspaceRequest.md b/pkg/serverapiclient/docs/CreateWorkspaceRequest.md index 98bb5e8a7b..162f036cfd 100644 --- a/pkg/serverapiclient/docs/CreateWorkspaceRequest.md +++ b/pkg/serverapiclient/docs/CreateWorkspaceRequest.md @@ -6,14 +6,14 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **Id** | Pointer to **string** | | [optional] **Name** | Pointer to **string** | | [optional] -**Projects** | Pointer to [**[]CreateWorkspaceRequestProject**](CreateWorkspaceRequestProject.md) | | [optional] +**Projects** | [**[]CreateWorkspaceRequestProject**](CreateWorkspaceRequestProject.md) | | **Target** | Pointer to **string** | | [optional] ## Methods ### NewCreateWorkspaceRequest -`func NewCreateWorkspaceRequest() *CreateWorkspaceRequest` +`func NewCreateWorkspaceRequest(projects []CreateWorkspaceRequestProject, ) *CreateWorkspaceRequest` NewCreateWorkspaceRequest instantiates a new CreateWorkspaceRequest object This constructor will assign default values to properties that have it defined, @@ -97,11 +97,6 @@ and a boolean to check if the value has been set. SetProjects sets Projects field to given value. -### HasProjects - -`func (o *CreateWorkspaceRequest) HasProjects() bool` - -HasProjects returns a boolean if a field has been set. ### GetTarget diff --git a/pkg/serverapiclient/docs/CreateWorkspaceRequestProject.md b/pkg/serverapiclient/docs/CreateWorkspaceRequestProject.md index 46e3aa1abe..6f1fa5ce92 100644 --- a/pkg/serverapiclient/docs/CreateWorkspaceRequestProject.md +++ b/pkg/serverapiclient/docs/CreateWorkspaceRequestProject.md @@ -5,9 +5,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **EnvVars** | Pointer to **map[string]string** | | [optional] -**Id** | Pointer to **string** | | [optional] **Image** | Pointer to **string** | | [optional] -**Name** | Pointer to **string** | | [optional] +**Name** | **string** | | **Source** | Pointer to [**CreateWorkspaceRequestProjectSource**](CreateWorkspaceRequestProjectSource.md) | | [optional] **User** | Pointer to **string** | | [optional] @@ -15,7 +14,7 @@ Name | Type | Description | Notes ### NewCreateWorkspaceRequestProject -`func NewCreateWorkspaceRequestProject() *CreateWorkspaceRequestProject` +`func NewCreateWorkspaceRequestProject(name string, ) *CreateWorkspaceRequestProject` NewCreateWorkspaceRequestProject instantiates a new CreateWorkspaceRequestProject object This constructor will assign default values to properties that have it defined, @@ -55,31 +54,6 @@ SetEnvVars sets EnvVars field to given value. HasEnvVars returns a boolean if a field has been set. -### GetId - -`func (o *CreateWorkspaceRequestProject) GetId() string` - -GetId returns the Id field if non-nil, zero value otherwise. - -### GetIdOk - -`func (o *CreateWorkspaceRequestProject) GetIdOk() (*string, bool)` - -GetIdOk returns a tuple with the Id field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetId - -`func (o *CreateWorkspaceRequestProject) SetId(v string)` - -SetId sets Id field to given value. - -### HasId - -`func (o *CreateWorkspaceRequestProject) HasId() bool` - -HasId returns a boolean if a field has been set. - ### GetImage `func (o *CreateWorkspaceRequestProject) GetImage() string` @@ -124,11 +98,6 @@ and a boolean to check if the value has been set. SetName sets Name field to given value. -### HasName - -`func (o *CreateWorkspaceRequestProject) HasName() bool` - -HasName returns a boolean if a field has been set. ### GetSource diff --git a/pkg/serverapiclient/docs/WorkspaceAPI.md b/pkg/serverapiclient/docs/WorkspaceAPI.md index bd43eb43a9..0bb2457d20 100644 --- a/pkg/serverapiclient/docs/WorkspaceAPI.md +++ b/pkg/serverapiclient/docs/WorkspaceAPI.md @@ -37,7 +37,7 @@ import ( ) func main() { - workspace := *openapiclient.NewCreateWorkspaceRequest() // CreateWorkspaceRequest | Create workspace + workspace := *openapiclient.NewCreateWorkspaceRequest([]openapiclient.CreateWorkspaceRequestProject{*openapiclient.NewCreateWorkspaceRequestProject("Name_example")}) // CreateWorkspaceRequest | Create workspace configuration := openapiclient.NewConfiguration() apiClient := openapiclient.NewAPIClient(configuration) diff --git a/pkg/serverapiclient/model_create_workspace_request.go b/pkg/serverapiclient/model_create_workspace_request.go index b4216427fd..d86d0e8391 100644 --- a/pkg/serverapiclient/model_create_workspace_request.go +++ b/pkg/serverapiclient/model_create_workspace_request.go @@ -11,7 +11,9 @@ API version: 0.1.0 package serverapiclient import ( + "bytes" "encoding/json" + "fmt" ) // checks if the CreateWorkspaceRequest type satisfies the MappedNullable interface at compile time @@ -21,16 +23,19 @@ var _ MappedNullable = &CreateWorkspaceRequest{} type CreateWorkspaceRequest struct { Id *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` - Projects []CreateWorkspaceRequestProject `json:"projects,omitempty"` + Projects []CreateWorkspaceRequestProject `json:"projects"` Target *string `json:"target,omitempty"` } +type _CreateWorkspaceRequest CreateWorkspaceRequest + // NewCreateWorkspaceRequest instantiates a new CreateWorkspaceRequest object // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewCreateWorkspaceRequest() *CreateWorkspaceRequest { +func NewCreateWorkspaceRequest(projects []CreateWorkspaceRequestProject) *CreateWorkspaceRequest { this := CreateWorkspaceRequest{} + this.Projects = projects return &this } @@ -106,34 +111,26 @@ func (o *CreateWorkspaceRequest) SetName(v string) { o.Name = &v } -// GetProjects returns the Projects field value if set, zero value otherwise. +// GetProjects returns the Projects field value func (o *CreateWorkspaceRequest) GetProjects() []CreateWorkspaceRequestProject { - if o == nil || IsNil(o.Projects) { + if o == nil { var ret []CreateWorkspaceRequestProject return ret } + return o.Projects } -// GetProjectsOk returns a tuple with the Projects field value if set, nil otherwise +// GetProjectsOk returns a tuple with the Projects field value // and a boolean to check if the value has been set. func (o *CreateWorkspaceRequest) GetProjectsOk() ([]CreateWorkspaceRequestProject, bool) { - if o == nil || IsNil(o.Projects) { + if o == nil { return nil, false } return o.Projects, true } -// HasProjects returns a boolean if a field has been set. -func (o *CreateWorkspaceRequest) HasProjects() bool { - if o != nil && !IsNil(o.Projects) { - return true - } - - return false -} - -// SetProjects gets a reference to the given []CreateWorkspaceRequestProject and assigns it to the Projects field. +// SetProjects sets field value func (o *CreateWorkspaceRequest) SetProjects(v []CreateWorkspaceRequestProject) { o.Projects = v } @@ -186,15 +183,50 @@ func (o CreateWorkspaceRequest) ToMap() (map[string]interface{}, error) { if !IsNil(o.Name) { toSerialize["name"] = o.Name } - if !IsNil(o.Projects) { - toSerialize["projects"] = o.Projects - } + toSerialize["projects"] = o.Projects if !IsNil(o.Target) { toSerialize["target"] = o.Target } return toSerialize, nil } +func (o *CreateWorkspaceRequest) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "projects", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err + } + + for _, requiredProperty := range requiredProperties { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varCreateWorkspaceRequest := _CreateWorkspaceRequest{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varCreateWorkspaceRequest) + + if err != nil { + return err + } + + *o = CreateWorkspaceRequest(varCreateWorkspaceRequest) + + return err +} + type NullableCreateWorkspaceRequest struct { value *CreateWorkspaceRequest isSet bool diff --git a/pkg/serverapiclient/model_create_workspace_request_project.go b/pkg/serverapiclient/model_create_workspace_request_project.go index 6f10255ddc..b6dfb4dc5b 100644 --- a/pkg/serverapiclient/model_create_workspace_request_project.go +++ b/pkg/serverapiclient/model_create_workspace_request_project.go @@ -11,7 +11,9 @@ API version: 0.1.0 package serverapiclient import ( + "bytes" "encoding/json" + "fmt" ) // checks if the CreateWorkspaceRequestProject type satisfies the MappedNullable interface at compile time @@ -20,19 +22,21 @@ var _ MappedNullable = &CreateWorkspaceRequestProject{} // CreateWorkspaceRequestProject struct for CreateWorkspaceRequestProject type CreateWorkspaceRequestProject struct { EnvVars *map[string]string `json:"envVars,omitempty"` - Id *string `json:"id,omitempty"` Image *string `json:"image,omitempty"` - Name *string `json:"name,omitempty"` + Name string `json:"name"` Source *CreateWorkspaceRequestProjectSource `json:"source,omitempty"` User *string `json:"user,omitempty"` } +type _CreateWorkspaceRequestProject CreateWorkspaceRequestProject + // NewCreateWorkspaceRequestProject instantiates a new CreateWorkspaceRequestProject object // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewCreateWorkspaceRequestProject() *CreateWorkspaceRequestProject { +func NewCreateWorkspaceRequestProject(name string) *CreateWorkspaceRequestProject { this := CreateWorkspaceRequestProject{} + this.Name = name return &this } @@ -76,38 +80,6 @@ func (o *CreateWorkspaceRequestProject) SetEnvVars(v map[string]string) { o.EnvVars = &v } -// GetId returns the Id field value if set, zero value otherwise. -func (o *CreateWorkspaceRequestProject) GetId() string { - if o == nil || IsNil(o.Id) { - var ret string - return ret - } - return *o.Id -} - -// GetIdOk returns a tuple with the Id field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspaceRequestProject) GetIdOk() (*string, bool) { - if o == nil || IsNil(o.Id) { - return nil, false - } - return o.Id, true -} - -// HasId returns a boolean if a field has been set. -func (o *CreateWorkspaceRequestProject) HasId() bool { - if o != nil && !IsNil(o.Id) { - return true - } - - return false -} - -// SetId gets a reference to the given string and assigns it to the Id field. -func (o *CreateWorkspaceRequestProject) SetId(v string) { - o.Id = &v -} - // GetImage returns the Image field value if set, zero value otherwise. func (o *CreateWorkspaceRequestProject) GetImage() string { if o == nil || IsNil(o.Image) { @@ -140,36 +112,28 @@ func (o *CreateWorkspaceRequestProject) SetImage(v string) { o.Image = &v } -// GetName returns the Name field value if set, zero value otherwise. +// GetName returns the Name field value func (o *CreateWorkspaceRequestProject) GetName() string { - if o == nil || IsNil(o.Name) { + if o == nil { var ret string return ret } - return *o.Name + + return o.Name } -// GetNameOk returns a tuple with the Name field value if set, nil otherwise +// GetNameOk returns a tuple with the Name field value // and a boolean to check if the value has been set. func (o *CreateWorkspaceRequestProject) GetNameOk() (*string, bool) { - if o == nil || IsNil(o.Name) { + if o == nil { return nil, false } - return o.Name, true + return &o.Name, true } -// HasName returns a boolean if a field has been set. -func (o *CreateWorkspaceRequestProject) HasName() bool { - if o != nil && !IsNil(o.Name) { - return true - } - - return false -} - -// SetName gets a reference to the given string and assigns it to the Name field. +// SetName sets field value func (o *CreateWorkspaceRequestProject) SetName(v string) { - o.Name = &v + o.Name = v } // GetSource returns the Source field value if set, zero value otherwise. @@ -249,15 +213,10 @@ func (o CreateWorkspaceRequestProject) ToMap() (map[string]interface{}, error) { if !IsNil(o.EnvVars) { toSerialize["envVars"] = o.EnvVars } - if !IsNil(o.Id) { - toSerialize["id"] = o.Id - } if !IsNil(o.Image) { toSerialize["image"] = o.Image } - if !IsNil(o.Name) { - toSerialize["name"] = o.Name - } + toSerialize["name"] = o.Name if !IsNil(o.Source) { toSerialize["source"] = o.Source } @@ -267,6 +226,43 @@ func (o CreateWorkspaceRequestProject) ToMap() (map[string]interface{}, error) { return toSerialize, nil } +func (o *CreateWorkspaceRequestProject) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "name", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err + } + + for _, requiredProperty := range requiredProperties { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varCreateWorkspaceRequestProject := _CreateWorkspaceRequestProject{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varCreateWorkspaceRequestProject) + + if err != nil { + return err + } + + *o = CreateWorkspaceRequestProject(varCreateWorkspaceRequestProject) + + return err +} + type NullableCreateWorkspaceRequestProject struct { value *CreateWorkspaceRequestProject isSet bool