diff --git a/api/v1alpha1/openapi.yaml b/api/v1alpha1/openapi.yaml index d224ed117..36fd91b4e 100644 --- a/api/v1alpha1/openapi.yaml +++ b/api/v1alpha1/openapi.yaml @@ -680,6 +680,107 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /api/v1/assessments/{id}/enhancement-data: + post: + tags: + - assessment + description: Save enhancement data for an assessment + operationId: saveAssessmentEnhancementData + parameters: + - name: id + in: path + description: ID of the assessment + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/EnhancementData" + required: true + responses: + "200": + description: User inputs saved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/EnhancementData" + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Assessment not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: Internal error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + get: + tags: + - assessment + description: Get stored enhancement data for an assessment + operationId: getAssessmentEnhancementData + parameters: + - name: id + in: path + description: ID of the assessment + required: true + schema: + type: string + format: uuid + responses: + "200": + description: User inputs found + content: + application/json: + schema: + $ref: "#/components/schemas/EnhancementData" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Assessment not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: Internal error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" /api/v1/assessments/{id}/cluster-requirements: post: tags: @@ -3327,6 +3428,163 @@ components: required: - clusterId + EnhancementData: + type: object + description: VMA enhancement data — fields that cannot be auto-collected by Agent or RVTools + properties: + deployedEnvironment: + $ref: "#/components/schemas/DeployedEnvironmentInput" + vmwareVersionCounts: + $ref: "#/components/schemas/VMwareVersionCountsInput" + activeEnvironments: + $ref: "#/components/schemas/ActiveEnvironmentsInput" + vmwareSubscription: + $ref: "#/components/schemas/VMwareSubscriptionInput" + vsphereCore: + $ref: "#/components/schemas/VsphereCoreInput" + nsx: + $ref: "#/components/schemas/NsxInput" + ariaOps: + $ref: "#/components/schemas/AriaOpsInput" + ariaAutomation: + $ref: "#/components/schemas/AriaAutomationInput" + ariaSecure: + $ref: "#/components/schemas/AriaSecureInput" + customerDetails: + $ref: "#/components/schemas/CustomerDetailsInput" + + DeployedEnvironmentInput: + type: object + properties: + environment: + type: string + enum: [on_premises, on_cloud, managed_services, not_assessed] + x-oapi-codegen-extra-tags: + validate: "omitempty,oneof=on_premises on_cloud managed_services not_assessed" + + VMwareVersionCountsInput: + type: object + properties: + perpetualLicensesCount: + type: integer + minimum: 0 + x-oapi-codegen-extra-tags: + validate: "omitempty,min=0" + coresLicensingCount: + type: integer + minimum: 0 + x-oapi-codegen-extra-tags: + validate: "omitempty,min=0" + environmentCount: + type: integer + minimum: 0 + x-oapi-codegen-extra-tags: + validate: "omitempty,min=0" + otherNodesCount: + type: integer + minimum: 0 + x-oapi-codegen-extra-tags: + validate: "omitempty,min=0" + + ActiveEnvironmentsInput: + type: object + properties: + environments: + type: array + uniqueItems: true + items: + type: string + enum: [production, qa, dev, other, not_assessed] + x-oapi-codegen-extra-tags: + validate: "omitempty,unique,dive,oneof=production qa dev other not_assessed" + + VMwareSubscriptionInput: + type: object + properties: + level: + type: array + uniqueItems: true + items: + type: string + enum: [vcf, vvf, vvs, vvep, vsphere, not_assessed] + x-oapi-codegen-extra-tags: + validate: "omitempty,unique,dive,oneof=vcf vvf vvs vvep vsphere not_assessed" + + VsphereCoreInput: + type: object + properties: + vmEncryptionEnabled: + type: boolean + vmEncryptionPolicy: + type: string + maxLength: 1000 + x-oapi-codegen-extra-tags: + validate: "omitempty,max=1000" + srmEnabled: + type: boolean + + NsxInput: + type: object + properties: + features: + type: array + uniqueItems: true + items: + type: string + enum: [microsegmentation, multi_cloud, tunnels, dynamic_routing, central_mgmt, mpls, qos, not_assessed, not_available, not_in_use] + x-oapi-codegen-extra-tags: + validate: "omitempty,unique,dive,oneof=microsegmentation multi_cloud tunnels dynamic_routing central_mgmt mpls qos not_assessed not_available not_in_use" + + AriaOpsInput: + type: object + properties: + features: + type: array + uniqueItems: true + items: + type: string + enum: [performance_analytics, health_score, alerting, log_insight, itsm_integration, rightsizing, auto_optimization, true_visibility, capacity_planning, anomaly_detection, not_assessed, not_available, not_in_use] + x-oapi-codegen-extra-tags: + validate: "omitempty,unique,dive,oneof=performance_analytics health_score alerting log_insight itsm_integration rightsizing auto_optimization true_visibility capacity_planning anomaly_detection not_assessed not_available not_in_use" + + AriaAutomationInput: + type: object + properties: + features: + type: array + uniqueItems: true + items: + type: string + enum: [infra_provisioning, cloud_assembly, service_catalog, blueprints, config_mgmt, hcx, orchestrator, terraform, dsm, tanzu_k8s, tanzu_app, k8s_namespaces, not_assessed, not_available, not_in_use] + x-oapi-codegen-extra-tags: + validate: "omitempty,unique,dive,oneof=infra_provisioning cloud_assembly service_catalog blueprints config_mgmt hcx orchestrator terraform dsm tanzu_k8s tanzu_app k8s_namespaces not_assessed not_available not_in_use" + + AriaSecureInput: + type: object + properties: + features: + type: array + uniqueItems: true + items: + type: string + enum: [policy_governance, compliance_monitoring, not_assessed, not_available, not_in_use] + x-oapi-codegen-extra-tags: + validate: "omitempty,unique,dive,oneof=policy_governance compliance_monitoring not_assessed not_available not_in_use" + + CustomerDetailsInput: + type: object + properties: + physicalLocationsCount: + type: integer + minimum: 0 + x-oapi-codegen-extra-tags: + validate: "omitempty,min=0" + targetHardware: + type: string + maxLength: 1000 + x-oapi-codegen-extra-tags: + validate: "omitempty,max=1000" + ClusterRequirementsResponse: type: object description: Cluster requirements calculation results diff --git a/api/v1alpha1/spec.gen.go b/api/v1alpha1/spec.gen.go index faccde290..007f2bfc0 100644 --- a/api/v1alpha1/spec.gen.go +++ b/api/v1alpha1/spec.gen.go @@ -18,193 +18,214 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "UfqKd+A9fHn4/wEAAP//l/awyWlVAQA=", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/v1alpha1/types.gen.go b/api/v1alpha1/types.gen.go index 8b37ca022..0bff341a0 100644 --- a/api/v1alpha1/types.gen.go +++ b/api/v1alpha1/types.gen.go @@ -9,6 +9,15 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) +// Defines values for ActiveEnvironmentsInputEnvironments. +const ( + ActiveEnvironmentsInputEnvironmentsDev ActiveEnvironmentsInputEnvironments = "dev" + ActiveEnvironmentsInputEnvironmentsNotAssessed ActiveEnvironmentsInputEnvironments = "not_assessed" + ActiveEnvironmentsInputEnvironmentsOther ActiveEnvironmentsInputEnvironments = "other" + ActiveEnvironmentsInputEnvironmentsProduction ActiveEnvironmentsInputEnvironments = "production" + ActiveEnvironmentsInputEnvironmentsQa ActiveEnvironmentsInputEnvironments = "qa" +) + // Defines values for AgentStatus. const ( AgentStatusError AgentStatus = "error" @@ -19,6 +28,51 @@ const ( AgentStatusWaitingForCredentials AgentStatus = "waiting-for-credentials" ) +// Defines values for AriaAutomationInputFeatures. +const ( + AriaAutomationInputFeaturesBlueprints AriaAutomationInputFeatures = "blueprints" + AriaAutomationInputFeaturesCloudAssembly AriaAutomationInputFeatures = "cloud_assembly" + AriaAutomationInputFeaturesConfigMgmt AriaAutomationInputFeatures = "config_mgmt" + AriaAutomationInputFeaturesDsm AriaAutomationInputFeatures = "dsm" + AriaAutomationInputFeaturesHcx AriaAutomationInputFeatures = "hcx" + AriaAutomationInputFeaturesInfraProvisioning AriaAutomationInputFeatures = "infra_provisioning" + AriaAutomationInputFeaturesK8sNamespaces AriaAutomationInputFeatures = "k8s_namespaces" + AriaAutomationInputFeaturesNotAssessed AriaAutomationInputFeatures = "not_assessed" + AriaAutomationInputFeaturesNotAvailable AriaAutomationInputFeatures = "not_available" + AriaAutomationInputFeaturesNotInUse AriaAutomationInputFeatures = "not_in_use" + AriaAutomationInputFeaturesOrchestrator AriaAutomationInputFeatures = "orchestrator" + AriaAutomationInputFeaturesServiceCatalog AriaAutomationInputFeatures = "service_catalog" + AriaAutomationInputFeaturesTanzuApp AriaAutomationInputFeatures = "tanzu_app" + AriaAutomationInputFeaturesTanzuK8s AriaAutomationInputFeatures = "tanzu_k8s" + AriaAutomationInputFeaturesTerraform AriaAutomationInputFeatures = "terraform" +) + +// Defines values for AriaOpsInputFeatures. +const ( + AriaOpsInputFeaturesAlerting AriaOpsInputFeatures = "alerting" + AriaOpsInputFeaturesAnomalyDetection AriaOpsInputFeatures = "anomaly_detection" + AriaOpsInputFeaturesAutoOptimization AriaOpsInputFeatures = "auto_optimization" + AriaOpsInputFeaturesCapacityPlanning AriaOpsInputFeatures = "capacity_planning" + AriaOpsInputFeaturesHealthScore AriaOpsInputFeatures = "health_score" + AriaOpsInputFeaturesItsmIntegration AriaOpsInputFeatures = "itsm_integration" + AriaOpsInputFeaturesLogInsight AriaOpsInputFeatures = "log_insight" + AriaOpsInputFeaturesNotAssessed AriaOpsInputFeatures = "not_assessed" + AriaOpsInputFeaturesNotAvailable AriaOpsInputFeatures = "not_available" + AriaOpsInputFeaturesNotInUse AriaOpsInputFeatures = "not_in_use" + AriaOpsInputFeaturesPerformanceAnalytics AriaOpsInputFeatures = "performance_analytics" + AriaOpsInputFeaturesRightsizing AriaOpsInputFeatures = "rightsizing" + AriaOpsInputFeaturesTrueVisibility AriaOpsInputFeatures = "true_visibility" +) + +// Defines values for AriaSecureInputFeatures. +const ( + AriaSecureInputFeaturesComplianceMonitoring AriaSecureInputFeatures = "compliance_monitoring" + AriaSecureInputFeaturesNotAssessed AriaSecureInputFeatures = "not_assessed" + AriaSecureInputFeaturesNotAvailable AriaSecureInputFeatures = "not_available" + AriaSecureInputFeaturesNotInUse AriaSecureInputFeatures = "not_in_use" + AriaSecureInputFeaturesPolicyGovernance AriaSecureInputFeatures = "policy_governance" +) + // Defines values for AssessmentPermissions. const ( Delete AssessmentPermissions = "delete" @@ -78,6 +132,14 @@ const ( CpuOneToTwo CpuOverCommitRatio = "1:2" ) +// Defines values for DeployedEnvironmentInputEnvironment. +const ( + DeployedEnvironmentInputEnvironmentManagedServices DeployedEnvironmentInputEnvironment = "managed_services" + DeployedEnvironmentInputEnvironmentNotAssessed DeployedEnvironmentInputEnvironment = "not_assessed" + DeployedEnvironmentInputEnvironmentOnCloud DeployedEnvironmentInputEnvironment = "on_cloud" + DeployedEnvironmentInputEnvironmentOnPremises DeployedEnvironmentInputEnvironment = "on_premises" +) + // Defines values for GroupKind. const ( GroupKindAdmin GroupKind = "admin" @@ -123,6 +185,20 @@ const ( Unsupported NetworkType = "unsupported" ) +// Defines values for NsxInputFeatures. +const ( + NsxInputFeaturesCentralMgmt NsxInputFeatures = "central_mgmt" + NsxInputFeaturesDynamicRouting NsxInputFeatures = "dynamic_routing" + NsxInputFeaturesMicrosegmentation NsxInputFeatures = "microsegmentation" + NsxInputFeaturesMpls NsxInputFeatures = "mpls" + NsxInputFeaturesMultiCloud NsxInputFeatures = "multi_cloud" + NsxInputFeaturesNotAssessed NsxInputFeatures = "not_assessed" + NsxInputFeaturesNotAvailable NsxInputFeatures = "not_available" + NsxInputFeaturesNotInUse NsxInputFeatures = "not_in_use" + NsxInputFeaturesQos NsxInputFeatures = "qos" + NsxInputFeaturesTunnels NsxInputFeatures = "tunnels" +) + // Defines values for OptimizationStatusReason. const ( CalculationError OptimizationStatusReason = "calculation_error" @@ -169,6 +245,16 @@ const ( Manual StorageIoConfigurationCongestionThresholdMode = "manual" ) +// Defines values for VMwareSubscriptionInputLevel. +const ( + NotAssessed VMwareSubscriptionInputLevel = "not_assessed" + Vcf VMwareSubscriptionInputLevel = "vcf" + Vsphere VMwareSubscriptionInputLevel = "vsphere" + Vvep VMwareSubscriptionInputLevel = "vvep" + Vvf VMwareSubscriptionInputLevel = "vvf" + Vvs VMwareSubscriptionInputLevel = "vvs" +) + // Defines values for OsInfoSupportTier. const ( Certified OsInfoSupportTier = "certified" @@ -183,6 +269,14 @@ const ( ListGroupsParamsKindPartner ListGroupsParamsKind = "partner" ) +// ActiveEnvironmentsInput defines model for ActiveEnvironmentsInput. +type ActiveEnvironmentsInput struct { + Environments *[]ActiveEnvironmentsInputEnvironments `json:"environments,omitempty" validate:"omitempty,unique,dive,oneof=production qa dev other not_assessed"` +} + +// ActiveEnvironmentsInputEnvironments defines model for ActiveEnvironmentsInput.Environments. +type ActiveEnvironmentsInputEnvironments string + // Agent defines model for Agent. type Agent struct { CreatedAt time.Time `json:"createdAt"` @@ -204,6 +298,30 @@ type AgentProxy struct { NoProxy *string `json:"noProxy" validate:"omitnil,max=1000"` } +// AriaAutomationInput defines model for AriaAutomationInput. +type AriaAutomationInput struct { + Features *[]AriaAutomationInputFeatures `json:"features,omitempty" validate:"omitempty,unique,dive,oneof=infra_provisioning cloud_assembly service_catalog blueprints config_mgmt hcx orchestrator terraform dsm tanzu_k8s tanzu_app k8s_namespaces not_assessed not_available not_in_use"` +} + +// AriaAutomationInputFeatures defines model for AriaAutomationInput.Features. +type AriaAutomationInputFeatures string + +// AriaOpsInput defines model for AriaOpsInput. +type AriaOpsInput struct { + Features *[]AriaOpsInputFeatures `json:"features,omitempty" validate:"omitempty,unique,dive,oneof=performance_analytics health_score alerting log_insight itsm_integration rightsizing auto_optimization true_visibility capacity_planning anomaly_detection not_assessed not_available not_in_use"` +} + +// AriaOpsInputFeatures defines model for AriaOpsInput.Features. +type AriaOpsInputFeatures string + +// AriaSecureInput defines model for AriaSecureInput. +type AriaSecureInput struct { + Features *[]AriaSecureInputFeatures `json:"features,omitempty" validate:"omitempty,unique,dive,oneof=policy_governance compliance_monitoring not_assessed not_available not_in_use"` +} + +// AriaSecureInputFeatures defines model for AriaSecureInput.Features. +type AriaSecureInputFeatures string + // Assessment defines model for Assessment. type Assessment struct { CreatedAt time.Time `json:"createdAt"` @@ -501,6 +619,12 @@ type Customer struct { Username string `json:"username"` } +// CustomerDetailsInput defines model for CustomerDetailsInput. +type CustomerDetailsInput struct { + PhysicalLocationsCount *int `json:"physicalLocationsCount,omitempty" validate:"omitempty,min=0"` + TargetHardware *string `json:"targetHardware,omitempty" validate:"omitempty,max=1000"` +} + // CustomerList defines model for CustomerList. type CustomerList = []Customer @@ -520,6 +644,28 @@ type Datastore struct { Vendor string `json:"vendor"` } +// DeployedEnvironmentInput defines model for DeployedEnvironmentInput. +type DeployedEnvironmentInput struct { + Environment *DeployedEnvironmentInputEnvironment `json:"environment,omitempty" validate:"omitempty,oneof=on_premises on_cloud managed_services not_assessed"` +} + +// DeployedEnvironmentInputEnvironment defines model for DeployedEnvironmentInput.Environment. +type DeployedEnvironmentInputEnvironment string + +// EnhancementData VMA enhancement data — fields that cannot be auto-collected by Agent or RVTools +type EnhancementData struct { + ActiveEnvironments *ActiveEnvironmentsInput `json:"activeEnvironments,omitempty"` + AriaAutomation *AriaAutomationInput `json:"ariaAutomation,omitempty"` + AriaOps *AriaOpsInput `json:"ariaOps,omitempty"` + AriaSecure *AriaSecureInput `json:"ariaSecure,omitempty"` + CustomerDetails *CustomerDetailsInput `json:"customerDetails,omitempty"` + DeployedEnvironment *DeployedEnvironmentInput `json:"deployedEnvironment,omitempty"` + Nsx *NsxInput `json:"nsx,omitempty"` + VmwareSubscription *VMwareSubscriptionInput `json:"vmwareSubscription,omitempty"` + VmwareVersionCounts *VMwareVersionCountsInput `json:"vmwareVersionCounts,omitempty"` + VsphereCore *VsphereCoreInput `json:"vsphereCore,omitempty"` +} + // Error defines model for Error. type Error struct { // Message Error message @@ -862,6 +1008,14 @@ type Network struct { // NetworkType defines model for Network.Type. type NetworkType string +// NsxInput defines model for NsxInput. +type NsxInput struct { + Features *[]NsxInputFeatures `json:"features,omitempty" validate:"omitempty,unique,dive,oneof=microsegmentation multi_cloud tunnels dynamic_routing central_mgmt mpls qos not_assessed not_available not_in_use"` +} + +// NsxInputFeatures defines model for NsxInput.Features. +type NsxInputFeatures string + // OptimizationStatus Status of utilization-based optimization attempt type OptimizationStatus struct { // Attempted Whether optimization was attempted based on available utilization data @@ -1228,6 +1382,22 @@ type VMs struct { TotalWithSharedDisks *int `json:"totalWithSharedDisks,omitempty"` } +// VMwareSubscriptionInput defines model for VMwareSubscriptionInput. +type VMwareSubscriptionInput struct { + Level *[]VMwareSubscriptionInputLevel `json:"level,omitempty" validate:"omitempty,unique,dive,oneof=vcf vvf vvs vvep vsphere not_assessed"` +} + +// VMwareSubscriptionInputLevel defines model for VMwareSubscriptionInput.Level. +type VMwareSubscriptionInputLevel string + +// VMwareVersionCountsInput defines model for VMwareVersionCountsInput. +type VMwareVersionCountsInput struct { + CoresLicensingCount *int `json:"coresLicensingCount,omitempty" validate:"omitempty,min=0"` + EnvironmentCount *int `json:"environmentCount,omitempty" validate:"omitempty,min=0"` + OtherNodesCount *int `json:"otherNodesCount,omitempty" validate:"omitempty,min=0"` + PerpetualLicensesCount *int `json:"perpetualLicensesCount,omitempty" validate:"omitempty,min=0"` +} + // ValidatedCertificateChain defines model for ValidatedCertificateChain. type ValidatedCertificateChain = string @@ -1245,6 +1415,13 @@ type VmNetwork struct { Ipv4 *Ipv4Config `json:"ipv4,omitempty"` } +// VsphereCoreInput defines model for VsphereCoreInput. +type VsphereCoreInput struct { + SrmEnabled *bool `json:"srmEnabled,omitempty"` + VmEncryptionEnabled *bool `json:"vmEncryptionEnabled,omitempty"` + VmEncryptionPolicy *string `json:"vmEncryptionPolicy,omitempty" validate:"omitempty,max=1000"` +} + // DiskSizeTierSummary defines model for diskSizeTierSummary. type DiskSizeTierSummary struct { // TotalSizeTB Total disk size in TB for this tier @@ -1329,6 +1506,9 @@ type CalculateAssessmentClusterRequirementsJSONRequestBody = ClusterRequirements // CalculateMigrationComplexityJSONRequestBody defines body for CalculateMigrationComplexity for application/json ContentType. type CalculateMigrationComplexityJSONRequestBody = MigrationComplexityRequest +// SaveAssessmentEnhancementDataJSONRequestBody defines body for SaveAssessmentEnhancementData for application/json ContentType. +type SaveAssessmentEnhancementDataJSONRequestBody = EnhancementData + // CalculateMigrationEstimationJSONRequestBody defines body for CalculateMigrationEstimation for application/json ContentType. type CalculateMigrationEstimationJSONRequestBody = MigrationEstimationRequest diff --git a/internal/api/client/client.gen.go b/internal/api/client/client.gen.go index 3f50e4558..bbd821291 100644 --- a/internal/api/client/client.gen.go +++ b/internal/api/client/client.gen.go @@ -132,6 +132,14 @@ type ClientInterface interface { CalculateMigrationComplexity(ctx context.Context, id openapi_types.UUID, body CalculateMigrationComplexityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetAssessmentEnhancementData request + GetAssessmentEnhancementData(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SaveAssessmentEnhancementDataWithBody request with any body + SaveAssessmentEnhancementDataWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + SaveAssessmentEnhancementData(ctx context.Context, id openapi_types.UUID, body SaveAssessmentEnhancementDataJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // CalculateMigrationEstimationWithBody request with any body CalculateMigrationEstimationWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -439,6 +447,42 @@ func (c *Client) CalculateMigrationComplexity(ctx context.Context, id openapi_ty return c.Client.Do(req) } +func (c *Client) GetAssessmentEnhancementData(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAssessmentEnhancementDataRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) SaveAssessmentEnhancementDataWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSaveAssessmentEnhancementDataRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) SaveAssessmentEnhancementData(ctx context.Context, id openapi_types.UUID, body SaveAssessmentEnhancementDataJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSaveAssessmentEnhancementDataRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) CalculateMigrationEstimationWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCalculateMigrationEstimationRequestWithBody(c.Server, id, contentType, body) if err != nil { @@ -1438,6 +1482,87 @@ func NewCalculateMigrationComplexityRequestWithBody(server string, id openapi_ty return req, nil } +// NewGetAssessmentEnhancementDataRequest generates requests for GetAssessmentEnhancementData +func NewGetAssessmentEnhancementDataRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/assessments/%s/enhancement-data", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSaveAssessmentEnhancementDataRequest calls the generic SaveAssessmentEnhancementData builder with application/json body +func NewSaveAssessmentEnhancementDataRequest(server string, id openapi_types.UUID, body SaveAssessmentEnhancementDataJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSaveAssessmentEnhancementDataRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewSaveAssessmentEnhancementDataRequestWithBody generates requests for SaveAssessmentEnhancementData with any type of body +func NewSaveAssessmentEnhancementDataRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/assessments/%s/enhancement-data", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewCalculateMigrationEstimationRequest calls the generic CalculateMigrationEstimation builder with application/json body func NewCalculateMigrationEstimationRequest(server string, id openapi_types.UUID, body CalculateMigrationEstimationJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -2825,6 +2950,14 @@ type ClientWithResponsesInterface interface { CalculateMigrationComplexityWithResponse(ctx context.Context, id openapi_types.UUID, body CalculateMigrationComplexityJSONRequestBody, reqEditors ...RequestEditorFn) (*CalculateMigrationComplexityResponse, error) + // GetAssessmentEnhancementDataWithResponse request + GetAssessmentEnhancementDataWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetAssessmentEnhancementDataResponse, error) + + // SaveAssessmentEnhancementDataWithBodyWithResponse request with any body + SaveAssessmentEnhancementDataWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveAssessmentEnhancementDataResponse, error) + + SaveAssessmentEnhancementDataWithResponse(ctx context.Context, id openapi_types.UUID, body SaveAssessmentEnhancementDataJSONRequestBody, reqEditors ...RequestEditorFn) (*SaveAssessmentEnhancementDataResponse, error) + // CalculateMigrationEstimationWithBodyWithResponse request with any body CalculateMigrationEstimationWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CalculateMigrationEstimationResponse, error) @@ -3244,6 +3377,59 @@ func (r CalculateMigrationComplexityResponse) StatusCode() int { return 0 } +type GetAssessmentEnhancementDataResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *EnhancementData + JSON401 *Error + JSON403 *Error + JSON404 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r GetAssessmentEnhancementDataResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetAssessmentEnhancementDataResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type SaveAssessmentEnhancementDataResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *EnhancementData + JSON400 *Error + JSON401 *Error + JSON403 *Error + JSON404 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r SaveAssessmentEnhancementDataResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SaveAssessmentEnhancementDataResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type CalculateMigrationEstimationResponse struct { Body []byte HTTPResponse *http.Response @@ -4248,6 +4434,32 @@ func (c *ClientWithResponses) CalculateMigrationComplexityWithResponse(ctx conte return ParseCalculateMigrationComplexityResponse(rsp) } +// GetAssessmentEnhancementDataWithResponse request returning *GetAssessmentEnhancementDataResponse +func (c *ClientWithResponses) GetAssessmentEnhancementDataWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetAssessmentEnhancementDataResponse, error) { + rsp, err := c.GetAssessmentEnhancementData(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAssessmentEnhancementDataResponse(rsp) +} + +// SaveAssessmentEnhancementDataWithBodyWithResponse request with arbitrary body returning *SaveAssessmentEnhancementDataResponse +func (c *ClientWithResponses) SaveAssessmentEnhancementDataWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveAssessmentEnhancementDataResponse, error) { + rsp, err := c.SaveAssessmentEnhancementDataWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSaveAssessmentEnhancementDataResponse(rsp) +} + +func (c *ClientWithResponses) SaveAssessmentEnhancementDataWithResponse(ctx context.Context, id openapi_types.UUID, body SaveAssessmentEnhancementDataJSONRequestBody, reqEditors ...RequestEditorFn) (*SaveAssessmentEnhancementDataResponse, error) { + rsp, err := c.SaveAssessmentEnhancementData(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSaveAssessmentEnhancementDataResponse(rsp) +} + // CalculateMigrationEstimationWithBodyWithResponse request with arbitrary body returning *CalculateMigrationEstimationResponse func (c *ClientWithResponses) CalculateMigrationEstimationWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CalculateMigrationEstimationResponse, error) { rsp, err := c.CalculateMigrationEstimationWithBody(ctx, id, contentType, body, reqEditors...) @@ -5286,6 +5498,121 @@ func ParseCalculateMigrationComplexityResponse(rsp *http.Response) (*CalculateMi return response, nil } +// ParseGetAssessmentEnhancementDataResponse parses an HTTP response from a GetAssessmentEnhancementDataWithResponse call +func ParseGetAssessmentEnhancementDataResponse(rsp *http.Response) (*GetAssessmentEnhancementDataResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetAssessmentEnhancementDataResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnhancementData + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseSaveAssessmentEnhancementDataResponse parses an HTTP response from a SaveAssessmentEnhancementDataWithResponse call +func ParseSaveAssessmentEnhancementDataResponse(rsp *http.Response) (*SaveAssessmentEnhancementDataResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SaveAssessmentEnhancementDataResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnhancementData + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseCalculateMigrationEstimationResponse parses an HTTP response from a CalculateMigrationEstimationWithResponse call func ParseCalculateMigrationEstimationResponse(rsp *http.Response) (*CalculateMigrationEstimationResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/internal/api/server/server.gen.go b/internal/api/server/server.gen.go index 18bee54e5..1ef51f734 100644 --- a/internal/api/server/server.gen.go +++ b/internal/api/server/server.gen.go @@ -53,6 +53,12 @@ type ServerInterface interface { // (POST /api/v1/assessments/{id}/complexity-estimation) CalculateMigrationComplexity(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // (GET /api/v1/assessments/{id}/enhancement-data) + GetAssessmentEnhancementData(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + + // (POST /api/v1/assessments/{id}/enhancement-data) + SaveAssessmentEnhancementData(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // (POST /api/v1/assessments/{id}/migration-estimation) CalculateMigrationEstimation(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) @@ -215,6 +221,16 @@ func (_ Unimplemented) CalculateMigrationComplexity(w http.ResponseWriter, r *ht w.WriteHeader(http.StatusNotImplemented) } +// (GET /api/v1/assessments/{id}/enhancement-data) +func (_ Unimplemented) GetAssessmentEnhancementData(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + w.WriteHeader(http.StatusNotImplemented) +} + +// (POST /api/v1/assessments/{id}/enhancement-data) +func (_ Unimplemented) SaveAssessmentEnhancementData(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + w.WriteHeader(http.StatusNotImplemented) +} + // (POST /api/v1/assessments/{id}/migration-estimation) func (_ Unimplemented) CalculateMigrationEstimation(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { w.WriteHeader(http.StatusNotImplemented) @@ -678,6 +694,58 @@ func (siw *ServerInterfaceWrapper) CalculateMigrationComplexity(w http.ResponseW handler.ServeHTTP(w, r.WithContext(ctx)) } +// GetAssessmentEnhancementData operation middleware +func (siw *ServerInterfaceWrapper) GetAssessmentEnhancementData(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetAssessmentEnhancementData(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// SaveAssessmentEnhancementData operation middleware +func (siw *ServerInterfaceWrapper) SaveAssessmentEnhancementData(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.SaveAssessmentEnhancementData(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // CalculateMigrationEstimation operation middleware func (siw *ServerInterfaceWrapper) CalculateMigrationEstimation(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -1634,6 +1702,12 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/api/v1/assessments/{id}/complexity-estimation", wrapper.CalculateMigrationComplexity) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/api/v1/assessments/{id}/enhancement-data", wrapper.GetAssessmentEnhancementData) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/api/v1/assessments/{id}/enhancement-data", wrapper.SaveAssessmentEnhancementData) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/api/v1/assessments/{id}/migration-estimation", wrapper.CalculateMigrationEstimation) }) @@ -2381,6 +2455,122 @@ func (response CalculateMigrationComplexity500JSONResponse) VisitCalculateMigrat return json.NewEncoder(w).Encode(response) } +type GetAssessmentEnhancementDataRequestObject struct { + Id openapi_types.UUID `json:"id"` +} + +type GetAssessmentEnhancementDataResponseObject interface { + VisitGetAssessmentEnhancementDataResponse(w http.ResponseWriter) error +} + +type GetAssessmentEnhancementData200JSONResponse EnhancementData + +func (response GetAssessmentEnhancementData200JSONResponse) VisitGetAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetAssessmentEnhancementData401JSONResponse Error + +func (response GetAssessmentEnhancementData401JSONResponse) VisitGetAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type GetAssessmentEnhancementData403JSONResponse Error + +func (response GetAssessmentEnhancementData403JSONResponse) VisitGetAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type GetAssessmentEnhancementData404JSONResponse Error + +func (response GetAssessmentEnhancementData404JSONResponse) VisitGetAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type GetAssessmentEnhancementData500JSONResponse Error + +func (response GetAssessmentEnhancementData500JSONResponse) VisitGetAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type SaveAssessmentEnhancementDataRequestObject struct { + Id openapi_types.UUID `json:"id"` + Body *SaveAssessmentEnhancementDataJSONRequestBody +} + +type SaveAssessmentEnhancementDataResponseObject interface { + VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error +} + +type SaveAssessmentEnhancementData200JSONResponse EnhancementData + +func (response SaveAssessmentEnhancementData200JSONResponse) VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type SaveAssessmentEnhancementData400JSONResponse Error + +func (response SaveAssessmentEnhancementData400JSONResponse) VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type SaveAssessmentEnhancementData401JSONResponse Error + +func (response SaveAssessmentEnhancementData401JSONResponse) VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type SaveAssessmentEnhancementData403JSONResponse Error + +func (response SaveAssessmentEnhancementData403JSONResponse) VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type SaveAssessmentEnhancementData404JSONResponse Error + +func (response SaveAssessmentEnhancementData404JSONResponse) VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type SaveAssessmentEnhancementData500JSONResponse Error + +func (response SaveAssessmentEnhancementData500JSONResponse) VisitSaveAssessmentEnhancementDataResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type CalculateMigrationEstimationRequestObject struct { Id openapi_types.UUID `json:"id"` Body *CalculateMigrationEstimationJSONRequestBody @@ -4212,6 +4402,12 @@ type StrictServerInterface interface { // (POST /api/v1/assessments/{id}/complexity-estimation) CalculateMigrationComplexity(ctx context.Context, request CalculateMigrationComplexityRequestObject) (CalculateMigrationComplexityResponseObject, error) + // (GET /api/v1/assessments/{id}/enhancement-data) + GetAssessmentEnhancementData(ctx context.Context, request GetAssessmentEnhancementDataRequestObject) (GetAssessmentEnhancementDataResponseObject, error) + + // (POST /api/v1/assessments/{id}/enhancement-data) + SaveAssessmentEnhancementData(ctx context.Context, request SaveAssessmentEnhancementDataRequestObject) (SaveAssessmentEnhancementDataResponseObject, error) + // (POST /api/v1/assessments/{id}/migration-estimation) CalculateMigrationEstimation(ctx context.Context, request CalculateMigrationEstimationRequestObject) (CalculateMigrationEstimationResponseObject, error) @@ -4662,6 +4858,65 @@ func (sh *strictHandler) CalculateMigrationComplexity(w http.ResponseWriter, r * } } +// GetAssessmentEnhancementData operation middleware +func (sh *strictHandler) GetAssessmentEnhancementData(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + var request GetAssessmentEnhancementDataRequestObject + + request.Id = id + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetAssessmentEnhancementData(ctx, request.(GetAssessmentEnhancementDataRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetAssessmentEnhancementData") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetAssessmentEnhancementDataResponseObject); ok { + if err := validResponse.VisitGetAssessmentEnhancementDataResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// SaveAssessmentEnhancementData operation middleware +func (sh *strictHandler) SaveAssessmentEnhancementData(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + var request SaveAssessmentEnhancementDataRequestObject + + request.Id = id + + var body SaveAssessmentEnhancementDataJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.SaveAssessmentEnhancementData(ctx, request.(SaveAssessmentEnhancementDataRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SaveAssessmentEnhancementData") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(SaveAssessmentEnhancementDataResponseObject); ok { + if err := validResponse.VisitSaveAssessmentEnhancementDataResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // CalculateMigrationEstimation operation middleware func (sh *strictHandler) CalculateMigrationEstimation(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { var request CalculateMigrationEstimationRequestObject diff --git a/internal/api_server/server.go b/internal/api_server/server.go index a8a371e4b..c14713b15 100644 --- a/internal/api_server/server.go +++ b/internal/api_server/server.go @@ -240,6 +240,8 @@ func (s *Server) Run(ctx context.Context) error { accountsSvc = service.NewAuthzAccountsService(accountsSvc) } + enhancementDataSvc := service.NewAssessmentEnhancementDataService(s.store) + h := handlers.NewServiceHandler( service.NewSourceService(s.store, s.opaValidator), assessmentSvc, @@ -248,6 +250,7 @@ func (s *Server) Run(ctx context.Context) error { eventwrap.NewEventEstimationService(service.NewEstimationService(s.store), s.store), partnerSvc, accountsSvc, + enhancementDataSvc, ) server.HandlerFromMux(server.NewStrictHandler(h, nil), router) diff --git a/internal/handlers/v1alpha1/accounts_test.go b/internal/handlers/v1alpha1/accounts_test.go index 06d178252..659e50ed4 100644 --- a/internal/handlers/v1alpha1/accounts_test.go +++ b/internal/handlers/v1alpha1/accounts_test.go @@ -40,7 +40,7 @@ var _ = Describe("accounts handler", Ordered, func() { s = store.NewStore(db) gormdb = db accountsSvc := service.NewAccountsService(s) - srv = handlers.NewServiceHandler(nil, nil, nil, nil, nil, service.NewPartnerService(s, accountsSvc), accountsSvc) + srv = handlers.NewServiceHandler(nil, nil, nil, nil, nil, service.NewPartnerService(s, accountsSvc), accountsSvc, nil) }) AfterAll(func() { @@ -678,7 +678,7 @@ var _ = Describe("accounts handler", Ordered, func() { BeforeAll(func() { accountsSvc := service.NewAccountsService(s) authzAccountsSvc := service.NewAuthzAccountsService(accountsSvc) - authzSrv = handlers.NewServiceHandler(nil, nil, nil, nil, nil, service.NewPartnerService(s, accountsSvc), authzAccountsSvc) + authzSrv = handlers.NewServiceHandler(nil, nil, nil, nil, nil, service.NewPartnerService(s, accountsSvc), authzAccountsSvc, nil) }) nonAdminCtx := func() context.Context { diff --git a/internal/handlers/v1alpha1/assessment_enhancement_data.go b/internal/handlers/v1alpha1/assessment_enhancement_data.go new file mode 100644 index 000000000..6a131c623 --- /dev/null +++ b/internal/handlers/v1alpha1/assessment_enhancement_data.go @@ -0,0 +1,91 @@ +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/kubev2v/migration-planner/internal/api/server" + "github.com/kubev2v/migration-planner/internal/handlers/validator" + "github.com/kubev2v/migration-planner/internal/service" + "github.com/kubev2v/migration-planner/pkg/log" +) + +// validateEnhancementData validates the enhancement data using the generated struct's validate tags +func validateEnhancementData(data interface{}) error { + v := validator.NewValidator() + return validator.TransformValidationError(v.Struct(data)) +} + +// (POST /api/v1/assessments/{id}/enhancement-data) +func (h *ServiceHandler) SaveAssessmentEnhancementData(ctx context.Context, request server.SaveAssessmentEnhancementDataRequestObject) (server.SaveAssessmentEnhancementDataResponseObject, error) { + logger := log.NewDebugLogger("enhancement_data_handler"). + WithContext(ctx). + Operation("save_enhancement_data"). + WithUUID("assessment_id", request.Id). + Build() + + if request.Body == nil { + logger.Error(fmt.Errorf("empty request body")).Log() + return server.SaveAssessmentEnhancementData400JSONResponse{Message: "empty body"}, nil + } + + _, err := h.assessmentSrv.GetAssessment(ctx, request.Id) + if err != nil { + switch err.(type) { + case *service.ErrForbidden: + logger.Error(err).Log() + return server.SaveAssessmentEnhancementData403JSONResponse{Message: err.Error()}, nil + case *service.ErrResourceNotFound: + logger.Error(err).Log() + return server.SaveAssessmentEnhancementData404JSONResponse{Message: err.Error()}, nil + default: + logger.Error(err).Log() + return server.SaveAssessmentEnhancementData500JSONResponse{Message: fmt.Sprintf("failed to get assessment: %v", err)}, nil + } + } + + if err := validateEnhancementData(*request.Body); err != nil { + logger.Error(err).Log() + return server.SaveAssessmentEnhancementData400JSONResponse{Message: err.Error()}, nil + } + + stored, err := h.enhancementDataSrv.SaveEnhancementData(ctx, request.Id, *request.Body) + if err != nil { + logger.Error(err).Log() + return server.SaveAssessmentEnhancementData500JSONResponse{Message: fmt.Sprintf("failed to save enhancement data: %v", err)}, nil + } + + return server.SaveAssessmentEnhancementData200JSONResponse(*stored), nil +} + +// (GET /api/v1/assessments/{id}/enhancement-data) +func (h *ServiceHandler) GetAssessmentEnhancementData(ctx context.Context, request server.GetAssessmentEnhancementDataRequestObject) (server.GetAssessmentEnhancementDataResponseObject, error) { + logger := log.NewDebugLogger("enhancement_data_handler"). + WithContext(ctx). + Operation("get_enhancement_data"). + WithUUID("assessment_id", request.Id). + Build() + + _, err := h.assessmentSrv.GetAssessment(ctx, request.Id) + if err != nil { + switch err.(type) { + case *service.ErrForbidden: + logger.Error(err).Log() + return server.GetAssessmentEnhancementData403JSONResponse{Message: err.Error()}, nil + case *service.ErrResourceNotFound: + logger.Error(err).Log() + return server.GetAssessmentEnhancementData404JSONResponse{Message: err.Error()}, nil + default: + logger.Error(err).Log() + return server.GetAssessmentEnhancementData500JSONResponse{Message: fmt.Sprintf("failed to get assessment: %v", err)}, nil + } + } + + stored, err := h.enhancementDataSrv.GetEnhancementData(ctx, request.Id) + if err != nil { + logger.Error(err).Log() + return server.GetAssessmentEnhancementData500JSONResponse{Message: fmt.Sprintf("failed to get enhancement data: %v", err)}, nil + } + + return server.GetAssessmentEnhancementData200JSONResponse(*stored), nil +} diff --git a/internal/handlers/v1alpha1/assessment_enhancement_data_test.go b/internal/handlers/v1alpha1/assessment_enhancement_data_test.go new file mode 100644 index 000000000..21302eed2 --- /dev/null +++ b/internal/handlers/v1alpha1/assessment_enhancement_data_test.go @@ -0,0 +1,276 @@ +package v1alpha1_test + +import ( + "context" + + "github.com/google/uuid" + api "github.com/kubev2v/migration-planner/api/v1alpha1" + "github.com/kubev2v/migration-planner/internal/api/server" + "github.com/kubev2v/migration-planner/internal/auth" + handlers "github.com/kubev2v/migration-planner/internal/handlers/v1alpha1" + "github.com/kubev2v/migration-planner/internal/service" + "github.com/kubev2v/migration-planner/internal/store/model" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func createTestAssessmentForEnhancementDataHandler(id uuid.UUID, username, orgID string) *model.Assessment { + return &model.Assessment{ + ID: id, + Name: "test-assessment", + OrgID: orgID, + Username: username, + } +} + +var _ = Describe("enhancement data handler", func() { + var ( + mockStore *MockStore + handler *handlers.ServiceHandler + ctx context.Context + user auth.User + assessmentID uuid.UUID + ) + + BeforeEach(func() { + mockStore = NewMockStore() + user = auth.User{ + Username: "test-user", + Organization: "test-org", + } + ctx = auth.NewTokenContext(context.Background(), user) + assessmentID = uuid.New() + + mockStore.assessments[assessmentID] = createTestAssessmentForEnhancementDataHandler(assessmentID, user.Username, user.Organization) + handler = handlers.NewServiceHandler( + nil, + service.NewAssessmentService(mockStore, nil, nil), + nil, + nil, + nil, + nil, + nil, + service.NewAssessmentEnhancementDataService(mockStore), + ) + }) + + Describe("SaveAssessmentEnhancementData", func() { + It("returns 200 with valid request", func() { + env := api.DeployedEnvironmentInputEnvironment("on_premises") + request := &api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: &env, + }, + } + + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: request, + }) + + Expect(err).To(BeNil()) + Expect(resp).NotTo(BeNil()) + response, ok := resp.(server.SaveAssessmentEnhancementData200JSONResponse) + Expect(ok).To(BeTrue()) + Expect(response.DeployedEnvironment).NotTo(BeNil()) + Expect(*response.DeployedEnvironment.Environment).To(Equal(api.DeployedEnvironmentInputEnvironment("on_premises"))) + }) + + It("returns 200 with empty data", func() { + request := &api.EnhancementData{} + + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: request, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.SaveAssessmentEnhancementData200JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("returns 400 when body is nil", func() { + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: nil, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.SaveAssessmentEnhancementData400JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("returns 404 when assessment does not exist", func() { + request := &api.EnhancementData{} + + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: uuid.New(), + Body: request, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.SaveAssessmentEnhancementData404JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("returns 400 when an enum field has an invalid value", func() { + env := api.DeployedEnvironmentInputEnvironment("not_a_real_environment") + request := &api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: &env, + }, + } + + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: request, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.SaveAssessmentEnhancementData400JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("returns 400 when a counter is negative", func() { + negative := -1 + request := &api.EnhancementData{ + VmwareVersionCounts: &api.VMwareVersionCountsInput{ + PerpetualLicensesCount: &negative, + }, + } + + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: request, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.SaveAssessmentEnhancementData400JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("returns 403 when authz denies access", func() { + forbiddenHandler := handlers.NewServiceHandler( + nil, + &ForbiddenAssessmentService{}, + nil, nil, nil, nil, nil, + service.NewAssessmentEnhancementDataService(mockStore), + ) + + request := &api.EnhancementData{} + + resp, err := forbiddenHandler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: request, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.SaveAssessmentEnhancementData403JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("overwrites previous data on second save", func() { + env1 := api.DeployedEnvironmentInputEnvironment("on_premises") + first := &api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: &env1, + }, + } + + _, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: first, + }) + Expect(err).To(BeNil()) + + env2 := api.DeployedEnvironmentInputEnvironment("on_cloud") + second := &api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: &env2, + }, + } + + resp, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: second, + }) + Expect(err).To(BeNil()) + response, ok := resp.(server.SaveAssessmentEnhancementData200JSONResponse) + Expect(ok).To(BeTrue()) + Expect(*response.DeployedEnvironment.Environment).To(Equal(api.DeployedEnvironmentInputEnvironment("on_cloud"))) + + getResp, err := handler.GetAssessmentEnhancementData(ctx, server.GetAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + }) + Expect(err).To(BeNil()) + getResponse, ok := getResp.(server.GetAssessmentEnhancementData200JSONResponse) + Expect(ok).To(BeTrue()) + Expect(*getResponse.DeployedEnvironment.Environment).To(Equal(api.DeployedEnvironmentInputEnvironment("on_cloud"))) + }) + }) + + Describe("GetAssessmentEnhancementData", func() { + It("returns 200 with empty data when no inputs saved", func() { + resp, err := handler.GetAssessmentEnhancementData(ctx, server.GetAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + }) + + Expect(err).To(BeNil()) + response, ok := resp.(server.GetAssessmentEnhancementData200JSONResponse) + Expect(ok).To(BeTrue()) + Expect(response.DeployedEnvironment).To(BeNil()) + }) + + It("returns 200 with stored data after save", func() { + env := api.DeployedEnvironmentInputEnvironment("managed_services") + request := &api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: &env, + }, + } + + _, err := handler.SaveAssessmentEnhancementData(ctx, server.SaveAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + Body: request, + }) + Expect(err).To(BeNil()) + + resp, err := handler.GetAssessmentEnhancementData(ctx, server.GetAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + }) + + Expect(err).To(BeNil()) + response, ok := resp.(server.GetAssessmentEnhancementData200JSONResponse) + Expect(ok).To(BeTrue()) + Expect(response.DeployedEnvironment).NotTo(BeNil()) + Expect(*response.DeployedEnvironment.Environment).To(Equal(api.DeployedEnvironmentInputEnvironment("managed_services"))) + }) + + It("returns 404 when assessment does not exist", func() { + resp, err := handler.GetAssessmentEnhancementData(ctx, server.GetAssessmentEnhancementDataRequestObject{ + Id: uuid.New(), + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.GetAssessmentEnhancementData404JSONResponse) + Expect(ok).To(BeTrue()) + }) + + It("returns 403 when authz denies access", func() { + forbiddenHandler := handlers.NewServiceHandler( + nil, + &ForbiddenAssessmentService{}, + nil, nil, nil, nil, nil, + service.NewAssessmentEnhancementDataService(mockStore), + ) + + resp, err := forbiddenHandler.GetAssessmentEnhancementData(ctx, server.GetAssessmentEnhancementDataRequestObject{ + Id: assessmentID, + }) + + Expect(err).To(BeNil()) + _, ok := resp.(server.GetAssessmentEnhancementData403JSONResponse) + Expect(ok).To(BeTrue()) + }) + }) +}) diff --git a/internal/handlers/v1alpha1/assessment_test.go b/internal/handlers/v1alpha1/assessment_test.go index 5d921453d..432daad30 100644 --- a/internal/handlers/v1alpha1/assessment_test.go +++ b/internal/handlers/v1alpha1/assessment_test.go @@ -95,7 +95,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.ListAssessments(ctx, server.ListAssessmentsRequestObject{}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.ListAssessments200JSONResponse{}).String())) @@ -119,7 +119,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.ListAssessments(ctx, server.ListAssessmentsRequestObject{}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.ListAssessments200JSONResponse{}).String())) @@ -170,7 +170,7 @@ var _ = Describe("assessment handler", Ordered, func() { SourceId: &sourceIDOpenAPI, } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.ListAssessments(ctx, server.ListAssessmentsRequestObject{ Params: params, }) @@ -216,7 +216,7 @@ var _ = Describe("assessment handler", Ordered, func() { SourceId: &sourceIDOpenAPI, } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.ListAssessments(ctx, server.ListAssessmentsRequestObject{ Params: params, }) @@ -248,7 +248,7 @@ var _ = Describe("assessment handler", Ordered, func() { inventory := createMinimalInventory() - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateAssessment(ctx, server.CreateAssessmentRequestObject{ Body: &v1alpha1.AssessmentForm{ Name: "test-assessment", @@ -283,7 +283,7 @@ var _ = Describe("assessment handler", Ordered, func() { inventory := createMinimalInventory() - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) // Note: AssessmentForm schema deliberately excludes owner fields // This prevents users from spoofing owner information via API requests @@ -323,7 +323,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateAssessment(ctx, server.CreateAssessmentRequestObject{ Body: &v1alpha1.AssessmentForm{ Name: "agent-assessment", @@ -348,7 +348,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateAssessment(ctx, server.CreateAssessmentRequestObject{}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.CreateAssessment400JSONResponse{}).String())) @@ -370,7 +370,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateAssessment(ctx, server.CreateAssessmentRequestObject{ Body: &v1alpha1.AssessmentForm{ Name: "forbidden-assessment", @@ -395,7 +395,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateAssessment(ctx, server.CreateAssessmentRequestObject{ Body: &v1alpha1.AssessmentForm{ Name: "no-inventory-assessment", @@ -426,7 +426,7 @@ var _ = Describe("assessment handler", Ordered, func() { EmailDomain: "admin.example.com", } ctx = auth.NewTokenContext(context.TODO(), user) - srv = handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv = handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) }) Context("name validation", func() { @@ -750,7 +750,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetAssessment(ctx, server.GetAssessmentRequestObject{Id: assessmentID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetAssessment200JSONResponse{}).String())) @@ -771,7 +771,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetAssessment(ctx, server.GetAssessmentRequestObject{Id: nonExistentID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetAssessment404JSONResponse{}).String())) @@ -787,7 +787,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), &ForbiddenAssessmentService{}, nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), &ForbiddenAssessmentService{}, nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetAssessment(ctx, server.GetAssessmentRequestObject{Id: assessmentID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetAssessment403JSONResponse{}).String())) @@ -816,7 +816,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) updatedName := "updated-name" resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: assessmentID, @@ -841,7 +841,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: assessmentID, Body: nil, @@ -863,7 +863,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) newName := "new-name" resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: nonExistentID, @@ -885,7 +885,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), &ForbiddenAssessmentService{}, nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), &ForbiddenAssessmentService{}, nil, service.NewSizerService(nil, s), nil, nil, nil, nil) hackedName := "hacked-name" resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: assessmentID, @@ -913,7 +913,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) updatedName := "updated-inventory-name" resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: assessmentID, @@ -949,7 +949,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) updatedName := "updated-rvtools-name" resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: assessmentID, @@ -994,7 +994,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) updatedName := "updated-agent-name" resp, err := srv.UpdateAssessment(ctx, server.UpdateAssessmentRequestObject{ Id: assessmentID, @@ -1035,7 +1035,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.DeleteAssessment(ctx, server.DeleteAssessmentRequestObject{Id: assessmentID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.DeleteAssessment200JSONResponse{}).String())) @@ -1062,7 +1062,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.DeleteAssessment(ctx, server.DeleteAssessmentRequestObject{Id: nonExistentID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.DeleteAssessment404JSONResponse{}).String())) @@ -1078,7 +1078,7 @@ var _ = Describe("assessment handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), &ForbiddenAssessmentService{}, nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), &ForbiddenAssessmentService{}, nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.DeleteAssessment(ctx, server.DeleteAssessmentRequestObject{Id: assessmentID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.DeleteAssessment403JSONResponse{}).String())) diff --git a/internal/handlers/v1alpha1/estimation_test.go b/internal/handlers/v1alpha1/estimation_test.go index b6fe8243e..d608a25ca 100644 --- a/internal/handlers/v1alpha1/estimation_test.go +++ b/internal/handlers/v1alpha1/estimation_test.go @@ -187,6 +187,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -226,6 +227,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -273,6 +275,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -304,6 +307,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -328,6 +332,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -350,6 +355,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -375,6 +381,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) // transfer_rate_mbps has Min=0.1 @@ -405,6 +412,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -432,6 +440,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -460,6 +469,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -487,6 +497,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -514,6 +525,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -540,6 +552,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -567,6 +580,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -595,6 +609,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -629,6 +644,7 @@ var _ = Describe("estimation handler", func() { service.NewEstimationService(mockStore), nil, nil, + nil, ) resp, err := handler.CalculateMigrationEstimation(ctx, server.CalculateMigrationEstimationRequestObject{ @@ -654,6 +670,7 @@ var _ = Describe("estimation handler", func() { svc, nil, nil, + nil, ) // Baseline: no param override, default transfer rate @@ -701,7 +718,7 @@ var _ = Describe("estimation handler", func() { It("returns 200 with complexityByDisk (4 entries) and complexityByOS (5 entries)", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -718,7 +735,7 @@ var _ = Describe("estimation handler", func() { It("returns diskSizeRatings with range-only keys and correct scores", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -737,7 +754,7 @@ var _ = Describe("estimation handler", func() { It("returns osRatings with one entry per OS in the cluster inventory", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -756,7 +773,7 @@ var _ = Describe("estimation handler", func() { It("returns disk scores in canonical order 1 through 4", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -773,7 +790,7 @@ var _ = Describe("estimation handler", func() { It("returns OS scores in canonical order 0 through 4", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -790,7 +807,7 @@ var _ = Describe("estimation handler", func() { It("returns complexityByOSName with one entry per distinct OS name", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -806,7 +823,7 @@ var _ = Describe("estimation handler", func() { It("returns complexityByOSName with correct osName, score and vmCount for a known OS", func() { request := &api.MigrationComplexityRequest{ClusterId: clusterID} mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -830,7 +847,7 @@ var _ = Describe("estimation handler", func() { Context("request validation errors", func() { It("returns 400 when request body is nil", func() { - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -844,7 +861,7 @@ var _ = Describe("estimation handler", func() { }) It("returns 400 when clusterId is empty", func() { - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -860,7 +877,7 @@ var _ = Describe("estimation handler", func() { Context("assessment not found errors", func() { It("returns 404 when assessment does not exist", func() { - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: uuid.New(), @@ -874,7 +891,7 @@ var _ = Describe("estimation handler", func() { It("returns 500 when store returns a non-NotFound error", func() { mockStore.getError = errors.New("database error") - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -889,7 +906,7 @@ var _ = Describe("estimation handler", func() { Context("authorization errors", func() { It("returns 403 when user has a different username", func() { - handler = handlers.NewServiceHandler(nil, &ForbiddenAssessmentService{}, nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, &ForbiddenAssessmentService{}, nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, Body: &api.MigrationComplexityRequest{ClusterId: clusterID}, @@ -902,7 +919,7 @@ var _ = Describe("estimation handler", func() { }) It("returns 403 when user belongs to a different organisation", func() { - handler = handlers.NewServiceHandler(nil, &ForbiddenAssessmentService{}, nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, &ForbiddenAssessmentService{}, nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -919,7 +936,7 @@ var _ = Describe("estimation handler", func() { Context("complexity service errors", func() { It("returns 404 when cluster ID is not found in inventory", func() { mockStore.assessments[assessmentID] = createTestAssessmentForComplexityHandler(assessmentID, user.Username, user.Organization, clusterID) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -940,7 +957,7 @@ var _ = Describe("estimation handler", func() { Username: user.Username, Snapshots: []model.Snapshot{}, } - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationComplexity(ctx, server.CalculateMigrationComplexityRequestObject{ Id: assessmentID, @@ -960,7 +977,7 @@ var _ = Describe("estimation handler", func() { mockStore.assessments[assessmentID] = createTestAssessmentForByComplexityHandler( assessmentID, user.Username, user.Organization, clusterID, ) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationEstimationByComplexity(ctx, server.CalculateMigrationEstimationByComplexityRequestObject{ @@ -979,7 +996,7 @@ var _ = Describe("estimation handler", func() { mockStore.assessments[assessmentID] = createTestAssessmentForByComplexityHandler( assessmentID, user.Username, user.Organization, clusterID, ) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationEstimationByComplexity(ctx, server.CalculateMigrationEstimationByComplexityRequestObject{ @@ -1003,7 +1020,7 @@ var _ = Describe("estimation handler", func() { mockStore.assessments[assessmentID] = createTestAssessmentForByComplexityHandler( assessmentID, user.Username, user.Organization, clusterID, ) - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationEstimationByComplexity(ctx, server.CalculateMigrationEstimationByComplexityRequestObject{ @@ -1041,7 +1058,7 @@ var _ = Describe("estimation handler", func() { Context("param validation errors", func() { It("returns 400 when an unknown param key is provided", func() { - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) resp, err := handler.CalculateMigrationEstimationByComplexity(ctx, server.CalculateMigrationEstimationByComplexityRequestObject{ @@ -1059,7 +1076,7 @@ var _ = Describe("estimation handler", func() { }) It("returns 400 when a param value is below its minimum", func() { - handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil) + handler = handlers.NewServiceHandler(nil, service.NewAssessmentService(mockStore, nil, nil), nil, nil, service.NewEstimationService(mockStore), nil, nil, nil) // transfer_rate_mbps has Min=0.1 resp, err := handler.CalculateMigrationEstimationByComplexity(ctx, diff --git a/internal/handlers/v1alpha1/handler.go b/internal/handlers/v1alpha1/handler.go index a772717a5..0c8a94f03 100644 --- a/internal/handlers/v1alpha1/handler.go +++ b/internal/handlers/v1alpha1/handler.go @@ -3,13 +3,14 @@ package v1alpha1 import "github.com/kubev2v/migration-planner/internal/service" type ServiceHandler struct { - sourceSrv *service.SourceService - assessmentSrv service.AssessmentServicer - jobSrv *service.JobService - sizerSrv service.SizerServicer - estimationSrv service.EstimationServicer - partnerSrv service.PartnerServicer - accountsSrv service.AccountsServicer + sourceSrv *service.SourceService + assessmentSrv service.AssessmentServicer + jobSrv *service.JobService + sizerSrv service.SizerServicer + estimationSrv service.EstimationServicer + partnerSrv service.PartnerServicer + accountsSrv service.AccountsServicer + enhancementDataSrv service.AssessmentEnhancementDataServicer } func NewServiceHandler( @@ -20,14 +21,16 @@ func NewServiceHandler( estimation service.EstimationServicer, partner service.PartnerServicer, accounts service.AccountsServicer, + enhancementData service.AssessmentEnhancementDataServicer, ) *ServiceHandler { return &ServiceHandler{ - sourceSrv: sourceService, - assessmentSrv: a, - jobSrv: j, - sizerSrv: sizer, - estimationSrv: estimation, - accountsSrv: accounts, - partnerSrv: partner, + sourceSrv: sourceService, + assessmentSrv: a, + jobSrv: j, + sizerSrv: sizer, + estimationSrv: estimation, + accountsSrv: accounts, + partnerSrv: partner, + enhancementDataSrv: enhancementData, } } diff --git a/internal/handlers/v1alpha1/job_test.go b/internal/handlers/v1alpha1/job_test.go index b4f66dc98..5cca09dea 100644 --- a/internal/handlers/v1alpha1/job_test.go +++ b/internal/handlers/v1alpha1/job_test.go @@ -63,7 +63,7 @@ var _ = Describe("job handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), service.NewJobService(s, nil, ""), service.NewSizerService(sizerClient, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), service.NewJobService(s, nil, ""), service.NewSizerService(sizerClient, s), nil, nil, nil, nil) resp, err := srv.GetJob(ctx, server.GetJobRequestObject{Id: 123}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetJob404JSONResponse{}).String())) @@ -78,7 +78,7 @@ var _ = Describe("job handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), service.NewJobService(s, nil, ""), service.NewSizerService(sizerClient, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), service.NewJobService(s, nil, ""), service.NewSizerService(sizerClient, s), nil, nil, nil, nil) resp, err := srv.CancelJob(ctx, server.CancelJobRequestObject{Id: 123}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.CancelJob404JSONResponse{}).String())) @@ -118,7 +118,7 @@ var _ = Describe("job handler", Ordered, func() { LastName: "User", } ctx = auth.NewTokenContext(context.TODO(), user) - srv = handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), service.NewJobService(s, nil, ""), service.NewSizerService(sizerClient, s), nil, nil, nil) + srv = handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), service.NewJobService(s, nil, ""), service.NewSizerService(sizerClient, s), nil, nil, nil, nil) }) It("returns 400 when name is empty", func() { diff --git a/internal/handlers/v1alpha1/partner_test.go b/internal/handlers/v1alpha1/partner_test.go index e8932b89f..3b5181f4d 100644 --- a/internal/handlers/v1alpha1/partner_test.go +++ b/internal/handlers/v1alpha1/partner_test.go @@ -42,7 +42,7 @@ var _ = Describe("partner handler", Ordered, func() { gormdb = db accountsSvc := service.NewAccountsService(s) partnerSvc := service.NewAuthzPartnerService(service.NewPartnerService(s, accountsSvc), accountsSvc, s) - srv = handlers.NewServiceHandler(nil, nil, nil, nil, nil, partnerSvc, accountsSvc) + srv = handlers.NewServiceHandler(nil, nil, nil, nil, nil, partnerSvc, accountsSvc, nil) }) AfterAll(func() { diff --git a/internal/handlers/v1alpha1/sizer_test.go b/internal/handlers/v1alpha1/sizer_test.go index fb97fac3b..a23f09e0a 100644 --- a/internal/handlers/v1alpha1/sizer_test.go +++ b/internal/handlers/v1alpha1/sizer_test.go @@ -25,15 +25,17 @@ import ( // MockStore is a mock implementation of store.Store type MockStore struct { - assessments map[uuid.UUID]*model.Assessment - clusterInputs map[string]*model.AssessmentClusterSizingInput - getError error + assessments map[uuid.UUID]*model.Assessment + clusterInputs map[string]*model.AssessmentClusterSizingInput + enhancementData map[string]*model.AssessmentEnhancementData + getError error } func NewMockStore() *MockStore { return &MockStore{ - assessments: make(map[uuid.UUID]*model.Assessment), - clusterInputs: make(map[string]*model.AssessmentClusterSizingInput), + assessments: make(map[uuid.UUID]*model.Assessment), + clusterInputs: make(map[string]*model.AssessmentClusterSizingInput), + enhancementData: make(map[string]*model.AssessmentEnhancementData), } } @@ -117,6 +119,10 @@ func (m *MockStore) ClusterSizingInput() store.ClusterSizingInput { return &MockClusterSizingInputStore{store: m} } +func (m *MockStore) AssessmentEnhancementData() store.AssessmentEnhancementData { + return &MockAssessmentEnhancementDataStore{store: m} +} + func (m *MockStore) NewTransactionContext(ctx context.Context) (context.Context, error) { return ctx, nil } @@ -191,6 +197,24 @@ func (m *MockClusterSizingInputStore) Get(ctx context.Context, assessmentID uuid return &copied, nil } +type MockAssessmentEnhancementDataStore struct { + store *MockStore +} + +func (m *MockAssessmentEnhancementDataStore) Upsert(ctx context.Context, input model.AssessmentEnhancementData) (*model.AssessmentEnhancementData, error) { + m.store.enhancementData[input.AssessmentID.String()] = &input + return &input, nil +} + +func (m *MockAssessmentEnhancementDataStore) Get(ctx context.Context, assessmentID uuid.UUID) (*model.AssessmentEnhancementData, error) { + input, exists := m.store.enhancementData[assessmentID.String()] + if !exists { + return nil, store.ErrRecordNotFound + } + copied := *input + return &copied, nil +} + // createTestSizerServer creates an HTTP test server that mocks the sizer service func createTestSizerServer(response *client.SizerResponse, healthStatus int, healthError bool) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -274,6 +298,7 @@ func setupTestHandler(store *MockStore, sizerResponse *client.SizerResponse, ass nil, nil, nil, + nil, ) return handler, testServer } @@ -377,6 +402,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -411,6 +437,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -444,6 +471,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -502,6 +530,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -582,6 +611,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -632,6 +662,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -778,6 +809,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -846,6 +878,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -974,6 +1007,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1005,6 +1039,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1038,6 +1073,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1070,6 +1106,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1103,6 +1140,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1135,6 +1173,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1168,6 +1207,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1201,6 +1241,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1238,6 +1279,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1273,6 +1315,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1308,6 +1351,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1344,6 +1388,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1381,6 +1426,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1415,6 +1461,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1447,6 +1494,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1480,6 +1528,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1514,6 +1563,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1550,6 +1600,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1587,6 +1638,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1620,6 +1672,7 @@ var _ = Describe("sizer handler", func() { nil, // estimationService nil, nil, + nil, ) resp, err := handler.CalculateAssessmentClusterRequirements(ctx, server.CalculateAssessmentClusterRequirementsRequestObject{ @@ -1660,6 +1713,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateClusterRequirements(ctx, server.CalculateClusterRequirementsRequestObject{ @@ -1689,6 +1743,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) }) @@ -1772,6 +1827,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) resp, err := handler.CalculateClusterRequirements(ctx, server.CalculateClusterRequirementsRequestObject{ @@ -1807,6 +1863,7 @@ var _ = Describe("sizer handler", func() { nil, nil, nil, + nil, ) }) diff --git a/internal/handlers/v1alpha1/source_test.go b/internal/handlers/v1alpha1/source_test.go index 37334bf78..12e3417d0 100644 --- a/internal/handlers/v1alpha1/source_test.go +++ b/internal/handlers/v1alpha1/source_test.go @@ -90,7 +90,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.ListSources(ctx, server.ListSourcesRequestObject{}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.ListSources200JSONResponse{}).String())) @@ -117,7 +117,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.ListSources(ctx, server.ListSourcesRequestObject{}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.ListSources200JSONResponse{}).String())) @@ -144,7 +144,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ Body: &v1alpha1.CreateSourceJSONRequestBody{ Name: "test", @@ -178,7 +178,7 @@ var _ = Describe("source handler", Ordered, func() { return &s } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ Body: &v1alpha1.CreateSourceJSONRequestBody{ Name: "test", @@ -217,7 +217,7 @@ var _ = Describe("source handler", Ordered, func() { return &s } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ Body: &v1alpha1.CreateSourceJSONRequestBody{ Name: "full-infra", @@ -261,7 +261,7 @@ var _ = Describe("source handler", Ordered, func() { return &s } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ Body: &v1alpha1.CreateSourceJSONRequestBody{ Name: "test", @@ -294,7 +294,7 @@ var _ = Describe("source handler", Ordered, func() { return &s } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ Body: &v1alpha1.CreateSourceJSONRequestBody{ Name: "test", @@ -324,7 +324,7 @@ var _ = Describe("source handler", Ordered, func() { return &s } - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ Body: &v1alpha1.CreateSourceJSONRequestBody{ Name: "test", @@ -349,7 +349,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) // First create succeeds resp1, err := srv.CreateSource(ctx, server.CreateSourceRequestObject{ @@ -401,7 +401,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -434,7 +434,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -491,7 +491,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -535,7 +535,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -570,7 +570,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -614,7 +614,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -652,7 +652,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -688,7 +688,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: uuid.New()}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource404JSONResponse{}).String())) @@ -715,7 +715,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource403JSONResponse{}).String())) @@ -741,7 +741,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: sourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -781,7 +781,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSource(ctx, server.GetSourceRequestObject{Id: sourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSource200JSONResponse{}).String())) @@ -823,7 +823,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) _, err := srv.DeleteSource(ctx, server.DeleteSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) @@ -853,7 +853,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.DeleteSource(ctx, server.DeleteSourceRequestObject{Id: firstSourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.DeleteSource403JSONResponse{}).String())) @@ -877,7 +877,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) invalidLabels := []v1alpha1.Label{ {Key: "-invalid-key", Value: "valid-value"}, } @@ -902,7 +902,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) invalidLabels := []v1alpha1.Label{ {Key: "valid-key", Value: "invalid value with space"}, } @@ -936,7 +936,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.UpdateInventory(ctx, server.UpdateInventoryRequestObject{ Id: firstSourceID, Body: &v1alpha1.UpdateInventory{ @@ -978,7 +978,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.UpdateInventory(ctx, server.UpdateInventoryRequestObject{ Id: firstSourceID, Body: &v1alpha1.UpdateInventory{ @@ -1026,7 +1026,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.UpdateInventory(ctx, server.UpdateInventoryRequestObject{ Id: firstSourceID, Body: &v1alpha1.UpdateInventory{ @@ -1086,7 +1086,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSourceDownloadURL(ctx, server.GetSourceDownloadURLRequestObject{Id: sourceID}) Expect(err).To(BeNil()) Expect(reflect.TypeOf(resp).String()).To(Equal(reflect.TypeOf(server.GetSourceDownloadURL200JSONResponse{}).String())) @@ -1114,7 +1114,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), attackerUser) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSourceDownloadURL(ctx, server.GetSourceDownloadURLRequestObject{Id: victimSourceID}) Expect(err).To(BeNil()) @@ -1139,7 +1139,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), attackerUser) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSourceDownloadURL(ctx, server.GetSourceDownloadURLRequestObject{Id: victimSourceID}) Expect(err).To(BeNil()) @@ -1154,7 +1154,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSourceDownloadURL(ctx, server.GetSourceDownloadURLRequestObject{Id: uuid.New()}) Expect(err).To(BeNil()) @@ -1176,7 +1176,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.GetSourceDownloadURL(ctx, server.GetSourceDownloadURLRequestObject{Id: sourceID}) Expect(err).To(BeNil()) diff --git a/internal/service/assessment_enhancement_data.go b/internal/service/assessment_enhancement_data.go new file mode 100644 index 000000000..beb52a387 --- /dev/null +++ b/internal/service/assessment_enhancement_data.go @@ -0,0 +1,213 @@ +package service + +import ( + "context" + "errors" + "fmt" + + "github.com/google/uuid" + api "github.com/kubev2v/migration-planner/api/v1alpha1" + "github.com/kubev2v/migration-planner/internal/store" + "github.com/kubev2v/migration-planner/internal/store/model" + "github.com/kubev2v/migration-planner/pkg/log" +) + +type AssessmentEnhancementDataServicer interface { + SaveEnhancementData(ctx context.Context, assessmentID uuid.UUID, data api.EnhancementData) (*api.EnhancementData, error) + GetEnhancementData(ctx context.Context, assessmentID uuid.UUID) (*api.EnhancementData, error) +} + +type AssessmentEnhancementDataService struct { + store store.Store + logger *log.StructuredLogger +} + +func NewAssessmentEnhancementDataService(s store.Store) *AssessmentEnhancementDataService { + return &AssessmentEnhancementDataService{ + store: s, + logger: log.NewDebugLogger("assessment_enhancement_data_service"), + } +} + +func (s *AssessmentEnhancementDataService) SaveEnhancementData( + ctx context.Context, + assessmentID uuid.UUID, + data api.EnhancementData, +) (*api.EnhancementData, error) { + input := enhancementDataAPIToModel(assessmentID, data) + result, err := s.store.AssessmentEnhancementData().Upsert(ctx, input) + if err != nil { + return nil, fmt.Errorf("failed to save enhancement data: %w", err) + } + + out := enhancementDataModelToAPI(result) + return &out, nil +} + +func (s *AssessmentEnhancementDataService) GetEnhancementData( + ctx context.Context, + assessmentID uuid.UUID, +) (*api.EnhancementData, error) { + input, err := s.store.AssessmentEnhancementData().Get(ctx, assessmentID) + if err != nil { + if errors.Is(err, store.ErrRecordNotFound) { + empty := api.EnhancementData{} + return &empty, nil + } + return nil, fmt.Errorf("failed to get enhancement data: %w", err) + } + + out := enhancementDataModelToAPI(input) + return &out, nil +} + +func enhancementDataAPIToModel(assessmentID uuid.UUID, data api.EnhancementData) model.AssessmentEnhancementData { + m := model.AssessmentEnhancementData{ + AssessmentID: assessmentID, + } + + if data.DeployedEnvironment != nil { + m.DeployedEnvEnvironment = typedStringPtr(data.DeployedEnvironment.Environment) + } + + if data.VmwareVersionCounts != nil { + m.VMwareVerPerpetualLicensesCount = data.VmwareVersionCounts.PerpetualLicensesCount + m.VMwareVerCoresLicensingCount = data.VmwareVersionCounts.CoresLicensingCount + m.VMwareVerEnvironmentCount = data.VmwareVersionCounts.EnvironmentCount + m.VMwareVerOtherNodesCount = data.VmwareVersionCounts.OtherNodesCount + } + + if data.ActiveEnvironments != nil { + m.ActiveEnvEnvironments = typedStringSlice(data.ActiveEnvironments.Environments) + } + + if data.VmwareSubscription != nil { + m.VMwareSubLevel = typedStringSlice(data.VmwareSubscription.Level) + } + + if data.VsphereCore != nil { + m.VsphereVmEncryptionEnabled = data.VsphereCore.VmEncryptionEnabled + m.VsphereVmEncryptionPolicy = data.VsphereCore.VmEncryptionPolicy + m.VsphereSrmEnabled = data.VsphereCore.SrmEnabled + } + + if data.Nsx != nil { + m.NsxFeatures = typedStringSlice(data.Nsx.Features) + } + + if data.AriaOps != nil { + m.AriaOpsFeatures = typedStringSlice(data.AriaOps.Features) + } + + if data.AriaAutomation != nil { + m.AriaAutomationFeatures = typedStringSlice(data.AriaAutomation.Features) + } + + if data.AriaSecure != nil { + m.AriaSecureFeatures = typedStringSlice(data.AriaSecure.Features) + } + + if data.CustomerDetails != nil { + m.CustomerPhysicalLocationsCount = data.CustomerDetails.PhysicalLocationsCount + m.CustomerTargetHardware = data.CustomerDetails.TargetHardware + } + + return m +} + +func enhancementDataModelToAPI(m *model.AssessmentEnhancementData) api.EnhancementData { + var data api.EnhancementData + + if m.DeployedEnvEnvironment != nil { + env := api.DeployedEnvironmentInputEnvironment(*m.DeployedEnvEnvironment) + data.DeployedEnvironment = &api.DeployedEnvironmentInput{Environment: &env} + } + + if m.VMwareVerPerpetualLicensesCount != nil || m.VMwareVerCoresLicensingCount != nil || + m.VMwareVerEnvironmentCount != nil || m.VMwareVerOtherNodesCount != nil { + data.VmwareVersionCounts = &api.VMwareVersionCountsInput{ + PerpetualLicensesCount: m.VMwareVerPerpetualLicensesCount, + CoresLicensingCount: m.VMwareVerCoresLicensingCount, + EnvironmentCount: m.VMwareVerEnvironmentCount, + OtherNodesCount: m.VMwareVerOtherNodesCount, + } + } + + if m.ActiveEnvEnvironments != nil { + envs := toTypedSlice[api.ActiveEnvironmentsInputEnvironments](m.ActiveEnvEnvironments) + data.ActiveEnvironments = &api.ActiveEnvironmentsInput{Environments: &envs} + } + + if m.VMwareSubLevel != nil { + levels := toTypedSlice[api.VMwareSubscriptionInputLevel](m.VMwareSubLevel) + data.VmwareSubscription = &api.VMwareSubscriptionInput{Level: &levels} + } + + if m.VsphereVmEncryptionEnabled != nil || m.VsphereVmEncryptionPolicy != nil || + m.VsphereSrmEnabled != nil { + data.VsphereCore = &api.VsphereCoreInput{ + VmEncryptionEnabled: m.VsphereVmEncryptionEnabled, + VmEncryptionPolicy: m.VsphereVmEncryptionPolicy, + SrmEnabled: m.VsphereSrmEnabled, + } + } + + if m.NsxFeatures != nil { + features := toTypedSlice[api.NsxInputFeatures](m.NsxFeatures) + data.Nsx = &api.NsxInput{Features: &features} + } + + if m.AriaOpsFeatures != nil { + features := toTypedSlice[api.AriaOpsInputFeatures](m.AriaOpsFeatures) + data.AriaOps = &api.AriaOpsInput{Features: &features} + } + + if m.AriaAutomationFeatures != nil { + features := toTypedSlice[api.AriaAutomationInputFeatures](m.AriaAutomationFeatures) + data.AriaAutomation = &api.AriaAutomationInput{Features: &features} + } + + if m.AriaSecureFeatures != nil { + features := toTypedSlice[api.AriaSecureInputFeatures](m.AriaSecureFeatures) + data.AriaSecure = &api.AriaSecureInput{Features: &features} + } + + if m.CustomerPhysicalLocationsCount != nil || m.CustomerTargetHardware != nil { + data.CustomerDetails = &api.CustomerDetailsInput{ + PhysicalLocationsCount: m.CustomerPhysicalLocationsCount, + TargetHardware: m.CustomerTargetHardware, + } + } + + return data +} + +// typedStringPtr converts a typed string pointer (e.g. *DeployedEnvironmentInputEnvironment) to *string. +func typedStringPtr[T ~string](v *T) *string { + if v == nil { + return nil + } + s := string(*v) + return &s +} + +// typedStringSlice converts a typed string slice pointer (e.g. *[]NsxInputFeatures) to model.StringArray. +func typedStringSlice[T ~string](v *[]T) model.StringArray { + if v == nil { + return nil + } + result := make(model.StringArray, len(*v)) + for i, item := range *v { + result[i] = string(item) + } + return result +} + +// toTypedSlice converts model.StringArray back to a typed string slice (e.g. []NsxInputFeatures). +func toTypedSlice[T ~string](arr model.StringArray) []T { + result := make([]T, len(arr)) + for i, s := range arr { + result[i] = T(s) + } + return result +} diff --git a/internal/service/assessment_enhancement_data_test.go b/internal/service/assessment_enhancement_data_test.go new file mode 100644 index 000000000..c28b7ded9 --- /dev/null +++ b/internal/service/assessment_enhancement_data_test.go @@ -0,0 +1,180 @@ +package service_test + +import ( + "context" + + "github.com/google/uuid" + api "github.com/kubev2v/migration-planner/api/v1alpha1" + "github.com/kubev2v/migration-planner/internal/service" + "github.com/kubev2v/migration-planner/internal/util" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func ptr[T any](v T) *T { return &v } + +var _ = Describe("AssessmentEnhancementDataService", func() { + var ( + mockStore *MockStore + svc service.AssessmentEnhancementDataServicer + assessmentID uuid.UUID + ) + + BeforeEach(func() { + mockStore = NewMockStore() + svc = service.NewAssessmentEnhancementDataService(mockStore) + assessmentID = uuid.New() + }) + + DescribeTable("round-trips every optional section through save and get", + func(input api.EnhancementData, verify func(api.EnhancementData)) { + saved, err := svc.SaveEnhancementData(context.Background(), assessmentID, input) + Expect(err).To(BeNil()) + verify(*saved) + + fetched, err := svc.GetEnhancementData(context.Background(), assessmentID) + Expect(err).To(BeNil()) + verify(*fetched) + }, + Entry("deployed environment", api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: ptr(api.DeployedEnvironmentInputEnvironment("on_premises")), + }, + }, func(out api.EnhancementData) { + Expect(out.DeployedEnvironment).NotTo(BeNil()) + Expect(*out.DeployedEnvironment.Environment).To(Equal(api.DeployedEnvironmentInputEnvironment("on_premises"))) + }), + Entry("vmware version counts", api.EnhancementData{ + VmwareVersionCounts: &api.VMwareVersionCountsInput{ + PerpetualLicensesCount: util.IntPtr(1), + CoresLicensingCount: util.IntPtr(2), + EnvironmentCount: util.IntPtr(3), + OtherNodesCount: util.IntPtr(4), + }, + }, func(out api.EnhancementData) { + Expect(out.VmwareVersionCounts).NotTo(BeNil()) + Expect(*out.VmwareVersionCounts.PerpetualLicensesCount).To(Equal(1)) + Expect(*out.VmwareVersionCounts.CoresLicensingCount).To(Equal(2)) + Expect(*out.VmwareVersionCounts.EnvironmentCount).To(Equal(3)) + Expect(*out.VmwareVersionCounts.OtherNodesCount).To(Equal(4)) + }), + Entry("active environments with multiple values", api.EnhancementData{ + ActiveEnvironments: &api.ActiveEnvironmentsInput{ + Environments: ptr([]api.ActiveEnvironmentsInputEnvironments{"production", "dev"}), + }, + }, func(out api.EnhancementData) { + Expect(out.ActiveEnvironments).NotTo(BeNil()) + Expect(*out.ActiveEnvironments.Environments).To(Equal([]api.ActiveEnvironmentsInputEnvironments{"production", "dev"})) + }), + Entry("active environments with empty slice", api.EnhancementData{ + ActiveEnvironments: &api.ActiveEnvironmentsInput{ + Environments: ptr([]api.ActiveEnvironmentsInputEnvironments{}), + }, + }, func(out api.EnhancementData) { + Expect(out.ActiveEnvironments).NotTo(BeNil()) + Expect(*out.ActiveEnvironments.Environments).To(BeEmpty()) + }), + Entry("vmware subscription level", api.EnhancementData{ + VmwareSubscription: &api.VMwareSubscriptionInput{ + Level: ptr([]api.VMwareSubscriptionInputLevel{"vcf", "vsphere"}), + }, + }, func(out api.EnhancementData) { + Expect(out.VmwareSubscription).NotTo(BeNil()) + Expect(*out.VmwareSubscription.Level).To(Equal([]api.VMwareSubscriptionInputLevel{"vcf", "vsphere"})) + }), + Entry("vsphere core", api.EnhancementData{ + VsphereCore: &api.VsphereCoreInput{ + VmEncryptionEnabled: util.BoolPtr(true), + VmEncryptionPolicy: util.ToStrPtr("strict"), + SrmEnabled: util.BoolPtr(false), + }, + }, func(out api.EnhancementData) { + Expect(out.VsphereCore).NotTo(BeNil()) + Expect(*out.VsphereCore.VmEncryptionEnabled).To(BeTrue()) + Expect(*out.VsphereCore.VmEncryptionPolicy).To(Equal("strict")) + Expect(*out.VsphereCore.SrmEnabled).To(BeFalse()) + }), + Entry("nsx features", api.EnhancementData{ + Nsx: &api.NsxInput{ + Features: ptr([]api.NsxInputFeatures{"microsegmentation"}), + }, + }, func(out api.EnhancementData) { + Expect(out.Nsx).NotTo(BeNil()) + Expect(*out.Nsx.Features).To(Equal([]api.NsxInputFeatures{"microsegmentation"})) + }), + Entry("aria ops features", api.EnhancementData{ + AriaOps: &api.AriaOpsInput{ + Features: ptr([]api.AriaOpsInputFeatures{"performance_analytics"}), + }, + }, func(out api.EnhancementData) { + Expect(out.AriaOps).NotTo(BeNil()) + Expect(*out.AriaOps.Features).To(Equal([]api.AriaOpsInputFeatures{"performance_analytics"})) + }), + Entry("aria automation features", api.EnhancementData{ + AriaAutomation: &api.AriaAutomationInput{ + Features: ptr([]api.AriaAutomationInputFeatures{"orchestrator"}), + }, + }, func(out api.EnhancementData) { + Expect(out.AriaAutomation).NotTo(BeNil()) + Expect(*out.AriaAutomation.Features).To(Equal([]api.AriaAutomationInputFeatures{"orchestrator"})) + }), + Entry("aria secure features", api.EnhancementData{ + AriaSecure: &api.AriaSecureInput{ + Features: ptr([]api.AriaSecureInputFeatures{"compliance_monitoring"}), + }, + }, func(out api.EnhancementData) { + Expect(out.AriaSecure).NotTo(BeNil()) + Expect(*out.AriaSecure.Features).To(Equal([]api.AriaSecureInputFeatures{"compliance_monitoring"})) + }), + Entry("customer details", api.EnhancementData{ + CustomerDetails: &api.CustomerDetailsInput{ + PhysicalLocationsCount: util.IntPtr(5), + TargetHardware: util.ToStrPtr("Dell PowerEdge R750"), + }, + }, func(out api.EnhancementData) { + Expect(out.CustomerDetails).NotTo(BeNil()) + Expect(*out.CustomerDetails.PhysicalLocationsCount).To(Equal(5)) + Expect(*out.CustomerDetails.TargetHardware).To(Equal("Dell PowerEdge R750")) + }), + ) + + It("omits every section when no fields are set", func() { + saved, err := svc.SaveEnhancementData(context.Background(), assessmentID, api.EnhancementData{}) + Expect(err).To(BeNil()) + Expect(saved.DeployedEnvironment).To(BeNil()) + Expect(saved.VmwareVersionCounts).To(BeNil()) + Expect(saved.ActiveEnvironments).To(BeNil()) + Expect(saved.VmwareSubscription).To(BeNil()) + Expect(saved.VsphereCore).To(BeNil()) + Expect(saved.Nsx).To(BeNil()) + Expect(saved.AriaOps).To(BeNil()) + Expect(saved.AriaAutomation).To(BeNil()) + Expect(saved.AriaSecure).To(BeNil()) + Expect(saved.CustomerDetails).To(BeNil()) + }) + + It("returns empty data for an assessment with nothing saved yet", func() { + fetched, err := svc.GetEnhancementData(context.Background(), uuid.New()) + Expect(err).To(BeNil()) + Expect(*fetched).To(Equal(api.EnhancementData{})) + }) + + It("clears a section on a subsequent save that omits it", func() { + _, err := svc.SaveEnhancementData(context.Background(), assessmentID, api.EnhancementData{ + DeployedEnvironment: &api.DeployedEnvironmentInput{ + Environment: ptr(api.DeployedEnvironmentInputEnvironment("on_premises")), + }, + }) + Expect(err).To(BeNil()) + + saved, err := svc.SaveEnhancementData(context.Background(), assessmentID, api.EnhancementData{ + VsphereCore: &api.VsphereCoreInput{ + SrmEnabled: util.BoolPtr(true), + }, + }) + Expect(err).To(BeNil()) + Expect(saved.DeployedEnvironment).To(BeNil()) + Expect(saved.VsphereCore).NotTo(BeNil()) + Expect(*saved.VsphereCore.SrmEnabled).To(BeTrue()) + }) +}) diff --git a/internal/service/eventwrap/dispatcher_test.go b/internal/service/eventwrap/dispatcher_test.go index 5075781a2..ee31bbbf6 100644 --- a/internal/service/eventwrap/dispatcher_test.go +++ b/internal/service/eventwrap/dispatcher_test.go @@ -74,6 +74,7 @@ func (m *mockStore) PrivateKey() store.PrivateKey func (m *mockStore) Label() store.Label { return nil } func (m *mockStore) Assessment() store.Assessment { return nil } func (m *mockStore) ClusterSizingInput() store.ClusterSizingInput { return nil } +func (m *mockStore) AssessmentEnhancementData() store.AssessmentEnhancementData { return nil } func (m *mockStore) Job() store.Job { return nil } func (m *mockStore) Accounts() store.Accounts { return nil } func (m *mockStore) PartnerCustomer() store.PartnerCustomer { return nil } diff --git a/internal/service/sizer_test.go b/internal/service/sizer_test.go index 626079d7e..be571947e 100644 --- a/internal/service/sizer_test.go +++ b/internal/service/sizer_test.go @@ -23,16 +23,18 @@ import ( // MockStore is a mock implementation of store.Store type MockStore struct { - assessments map[uuid.UUID]*model.Assessment - clusterInputs map[string]*model.AssessmentClusterSizingInput - getError error - outboxEvents []model.OutboxEvent + assessments map[uuid.UUID]*model.Assessment + clusterInputs map[string]*model.AssessmentClusterSizingInput + enhancementData map[uuid.UUID]*model.AssessmentEnhancementData + getError error + outboxEvents []model.OutboxEvent } func NewMockStore() *MockStore { return &MockStore{ - assessments: make(map[uuid.UUID]*model.Assessment), - clusterInputs: make(map[string]*model.AssessmentClusterSizingInput), + assessments: make(map[uuid.UUID]*model.Assessment), + clusterInputs: make(map[string]*model.AssessmentClusterSizingInput), + enhancementData: make(map[uuid.UUID]*model.AssessmentEnhancementData), } } @@ -84,6 +86,10 @@ func (m *MockStore) ClusterSizingInput() store.ClusterSizingInput { return &MockClusterSizingInputStore{store: m} } +func (m *MockStore) AssessmentEnhancementData() store.AssessmentEnhancementData { + return &MockAssessmentEnhancementDataStore{store: m} +} + func (m *MockStore) NewTransactionContext(ctx context.Context) (context.Context, error) { return ctx, nil } @@ -158,6 +164,25 @@ func (m *MockClusterSizingInputStore) Get(ctx context.Context, assessmentID uuid return &copied, nil } +type MockAssessmentEnhancementDataStore struct { + store *MockStore +} + +func (m *MockAssessmentEnhancementDataStore) Upsert(ctx context.Context, input model.AssessmentEnhancementData) (*model.AssessmentEnhancementData, error) { + copied := input + m.store.enhancementData[input.AssessmentID] = &copied + return &copied, nil +} + +func (m *MockAssessmentEnhancementDataStore) Get(ctx context.Context, assessmentID uuid.UUID) (*model.AssessmentEnhancementData, error) { + input, exists := m.store.enhancementData[assessmentID] + if !exists { + return nil, store.ErrRecordNotFound + } + copied := *input + return &copied, nil +} + type MockOutboxStore struct { store *MockStore } diff --git a/internal/service/source_test.go b/internal/service/source_test.go index dc41b886f..f7d947a9f 100644 --- a/internal/service/source_test.go +++ b/internal/service/source_test.go @@ -419,7 +419,7 @@ var _ = Describe("source handler", Ordered, func() { } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) newName := "updated-name" newLabels := []v1alpha1.Label{ {Key: "env", Value: "prod"}, @@ -488,7 +488,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.UpdateSource(ctx, server.UpdateSourceRequestObject{ Id: uuid.New(), Body: &v1alpha1.SourceUpdate{}, @@ -508,7 +508,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) resp, err := srv.UpdateSource(ctx, server.UpdateSourceRequestObject{ Id: uuid.MustParse(sourceID), Body: &v1alpha1.SourceUpdate{}, @@ -535,7 +535,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) // First set initial labels initialLabels := []v1alpha1.Label{ @@ -608,7 +608,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) newSshKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCk83ddeteALlqCbO43E3ardbavFPboYIoFnlQZ3zVi+ls96c1x3P9DDWkNhuOgpQurull2y55Wm7HWLLK5hlk49s6tUuBDftH3XXfGMAmncBH9apGHxl0O+k/X1MrfhoEXHmmEwXTv+X6vC3BsZiazSOkKbIozHgnD7y1z83wuYWbbW0NYvgwhaoOtkWteKSJWwPxNaTwGCpj+RQ6xWygt5EbMSf7U3Ih2P1hcsa615zD5P2GSLxtLwWnHgWCylT/krdyIYlR1pqW9e/Iv2MKlGX6W1DSUxUz5BNxzCA8O53C0NSCeDFAhn9T8VE9U/RkGDtXBFJ8JVcmtM6S9buq5HZ12+0E0VCGFdmnvNT8XxdYrN0ff8f3DQI7ERgHEKQiqjrSPDv2+OMdv3nr3n5+tOBvQEn6aYDbnybILyrUP76UvLvjfgDTnnRxlkpw2Y43EtgtdeIUUo/VBSE9qfzRa21Pz3gBh6ZJE9xF+u6DstgvFigNJ7nMJoSktH5mzuBM= test@test" resp, err := srv.UpdateSource(ctx, server.UpdateSourceRequestObject{ @@ -649,7 +649,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) initialLabels := []v1alpha1.Label{ {Key: "env", Value: "dev"}, @@ -699,7 +699,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) enableProxy := false resp, err := srv.UpdateSource(ctx, server.UpdateSourceRequestObject{ @@ -738,7 +738,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) enableProxy := true httpUrl := "http://proxy.example.com" @@ -784,7 +784,7 @@ TZUUZpsP4or19B48WSqiV/eMdCB/PxnFZYT1SyFLlDBiXolb+30HbGeeaF0bEg+u } ctx := auth.NewTokenContext(context.TODO(), user) - srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil) + srv := handlers.NewServiceHandler(service.NewSourceService(s, nil), service.NewAssessmentService(s, nil, nil), nil, service.NewSizerService(nil, s), nil, nil, nil, nil) networkConfigType := v1alpha1.SourceUpdateNetworkConfigTypeDhcp resp, err := srv.UpdateSource(ctx, server.UpdateSourceRequestObject{ diff --git a/internal/store/assessment_enhancement_data.go b/internal/store/assessment_enhancement_data.go new file mode 100644 index 000000000..1e1123ac9 --- /dev/null +++ b/internal/store/assessment_enhancement_data.go @@ -0,0 +1,82 @@ +package store + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/kubev2v/migration-planner/internal/store/model" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type AssessmentEnhancementData interface { + Upsert(ctx context.Context, input model.AssessmentEnhancementData) (*model.AssessmentEnhancementData, error) + Get(ctx context.Context, assessmentID uuid.UUID) (*model.AssessmentEnhancementData, error) +} + +type AssessmentEnhancementDataStore struct { + db *gorm.DB +} + +var _ AssessmentEnhancementData = (*AssessmentEnhancementDataStore)(nil) + +func NewAssessmentEnhancementDataStore(db *gorm.DB) AssessmentEnhancementData { + return &AssessmentEnhancementDataStore{db: db} +} + +func (s *AssessmentEnhancementDataStore) Upsert(ctx context.Context, input model.AssessmentEnhancementData) (*model.AssessmentEnhancementData, error) { + now := time.Now() + input.UpdatedAt = &now + + result := s.getDB(ctx).Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "assessment_id"}}, + DoUpdates: clause.AssignmentColumns([]string{ + "deployed_env_environment", + "vmware_ver_perpetual_licenses_count", + "vmware_ver_cores_licensing_count", + "vmware_ver_environment_count", + "vmware_ver_other_nodes_count", + "active_env_environments", + "vmware_sub_level", + "vsphere_vm_encryption_enabled", + "vsphere_vm_encryption_policy", + "vsphere_srm_enabled", + "nsx_features", + "aria_ops_features", + "aria_automation_features", + "aria_secure_features", + "customer_physical_locations_count", + "customer_target_hardware", + "updated_at", + }), + }).Create(&input) + if result.Error != nil { + return nil, fmt.Errorf("upserting assessment enhancement data: %w", result.Error) + } + + return &input, nil +} + +func (s *AssessmentEnhancementDataStore) Get(ctx context.Context, assessmentID uuid.UUID) (*model.AssessmentEnhancementData, error) { + var input model.AssessmentEnhancementData + result := s.getDB(ctx).First(&input, "assessment_id = ?", assessmentID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, ErrRecordNotFound + } + return nil, fmt.Errorf("querying assessment enhancement data: %w", result.Error) + } + + return &input, nil +} + +func (s *AssessmentEnhancementDataStore) getDB(ctx context.Context) *gorm.DB { + tx := FromContext(ctx) + if tx != nil { + return tx + } + return s.db +} diff --git a/internal/store/assessment_enhancement_data_test.go b/internal/store/assessment_enhancement_data_test.go new file mode 100644 index 000000000..63e7009b3 --- /dev/null +++ b/internal/store/assessment_enhancement_data_test.go @@ -0,0 +1,105 @@ +package store_test + +import ( + "context" + + "github.com/google/uuid" + "github.com/kubev2v/migration-planner/internal/config" + "github.com/kubev2v/migration-planner/internal/store" + "github.com/kubev2v/migration-planner/internal/store/model" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "gorm.io/gorm" +) + +var _ = Describe("assessment enhancement data store", Ordered, func() { + var ( + s store.Store + gormdb *gorm.DB + ) + + BeforeAll(func() { + cfg, err := config.New() + Expect(err).To(BeNil()) + db, err := store.InitDB(cfg) + Expect(err).To(BeNil()) + + s = store.NewStore(db) + gormdb = db + }) + + AfterAll(func() { + _ = s.Close() + }) + + AfterEach(func() { + gormdb.Exec("DELETE FROM assessment_enhancement_data;") + gormdb.Exec("DELETE FROM assessments;") + }) + + It("upserts and retrieves enhancement data with all field types", func() { + assessmentID := uuid.New() + err := gormdb.Exec( + "INSERT INTO assessments (id, name, org_id, username, source_type, source_id) VALUES (?, ?, ?, ?, ?, NULL);", + assessmentID, "assessment-one", "org1", "user1", "inventory", + ).Error + Expect(err).To(BeNil()) + + env := "on_premises" + count := 5 + locCount := 3 + _, err = s.AssessmentEnhancementData().Upsert(context.Background(), model.AssessmentEnhancementData{ + AssessmentID: assessmentID, + DeployedEnvEnvironment: &env, + VMwareVerPerpetualLicensesCount: &count, + NsxFeatures: model.StringArray{"microsegmentation", "multi_cloud"}, + CustomerPhysicalLocationsCount: &locCount, + }) + Expect(err).To(BeNil()) + + stored, err := s.AssessmentEnhancementData().Get(context.Background(), assessmentID) + Expect(err).To(BeNil()) + Expect(stored.DeployedEnvEnvironment).ToNot(BeNil()) + Expect(*stored.DeployedEnvEnvironment).To(Equal("on_premises")) + Expect(stored.VMwareVerPerpetualLicensesCount).ToNot(BeNil()) + Expect(*stored.VMwareVerPerpetualLicensesCount).To(Equal(5)) + Expect(stored.NsxFeatures).To(Equal(model.StringArray{"microsegmentation", "multi_cloud"})) + Expect(stored.CustomerPhysicalLocationsCount).ToNot(BeNil()) + Expect(*stored.CustomerPhysicalLocationsCount).To(Equal(3)) + }) + + It("upserts and replaces existing data for the same assessment", func() { + assessmentID := uuid.New() + err := gormdb.Exec( + "INSERT INTO assessments (id, name, org_id, username, source_type, source_id) VALUES (?, ?, ?, ?, ?, NULL);", + assessmentID, "assessment-two", "org1", "user1", "inventory", + ).Error + Expect(err).To(BeNil()) + + env1 := "on_premises" + hw := "Dell PowerEdge R750" + _, err = s.AssessmentEnhancementData().Upsert(context.Background(), model.AssessmentEnhancementData{ + AssessmentID: assessmentID, + DeployedEnvEnvironment: &env1, + CustomerTargetHardware: &hw, + }) + Expect(err).To(BeNil()) + + env2 := "on_cloud" + _, err = s.AssessmentEnhancementData().Upsert(context.Background(), model.AssessmentEnhancementData{ + AssessmentID: assessmentID, + DeployedEnvEnvironment: &env2, + }) + Expect(err).To(BeNil()) + + stored, err := s.AssessmentEnhancementData().Get(context.Background(), assessmentID) + Expect(err).To(BeNil()) + Expect(*stored.DeployedEnvEnvironment).To(Equal("on_cloud")) + Expect(stored.CustomerTargetHardware).To(BeNil()) + }) + + It("returns ErrRecordNotFound when record does not exist", func() { + _, err := s.AssessmentEnhancementData().Get(context.Background(), uuid.New()) + Expect(err).To(Equal(store.ErrRecordNotFound)) + }) +}) diff --git a/internal/store/model/assessment_enhancement_data.go b/internal/store/model/assessment_enhancement_data.go new file mode 100644 index 000000000..9e5fd624c --- /dev/null +++ b/internal/store/model/assessment_enhancement_data.go @@ -0,0 +1,47 @@ +package model + +import ( + "time" + + "github.com/google/uuid" +) + +type AssessmentEnhancementData struct { + AssessmentID uuid.UUID `gorm:"primaryKey;column:assessment_id;type:VARCHAR(255);"` + + // Deployed Environment + DeployedEnvEnvironment *string `gorm:"column:deployed_env_environment;type:VARCHAR(50)"` + + // VMware Version Counts + VMwareVerPerpetualLicensesCount *int `gorm:"column:vmware_ver_perpetual_licenses_count"` + VMwareVerCoresLicensingCount *int `gorm:"column:vmware_ver_cores_licensing_count"` + VMwareVerEnvironmentCount *int `gorm:"column:vmware_ver_environment_count"` + VMwareVerOtherNodesCount *int `gorm:"column:vmware_ver_other_nodes_count"` + + // Active Environments + ActiveEnvEnvironments StringArray `gorm:"column:active_env_environments;type:text[]"` + + // VMware Subscription + VMwareSubLevel StringArray `gorm:"column:vmware_sub_level;type:text[]"` + + // vSphere Core + VsphereVmEncryptionEnabled *bool `gorm:"column:vsphere_vm_encryption_enabled"` + VsphereVmEncryptionPolicy *string `gorm:"column:vsphere_vm_encryption_policy"` + VsphereSrmEnabled *bool `gorm:"column:vsphere_srm_enabled"` + + // NSX / Aria + NsxFeatures StringArray `gorm:"column:nsx_features;type:text[]"` + AriaOpsFeatures StringArray `gorm:"column:aria_ops_features;type:text[]"` + AriaAutomationFeatures StringArray `gorm:"column:aria_automation_features;type:text[]"` + AriaSecureFeatures StringArray `gorm:"column:aria_secure_features;type:text[]"` + + // Customer Details + CustomerPhysicalLocationsCount *int `gorm:"column:customer_physical_locations_count"` + CustomerTargetHardware *string `gorm:"column:customer_target_hardware"` + + UpdatedAt *time.Time `gorm:"column:updated_at;type:timestamptz;not null;default:now()"` +} + +func (AssessmentEnhancementData) TableName() string { + return "assessment_enhancement_data" +} diff --git a/internal/store/model/stringarray.go b/internal/store/model/stringarray.go new file mode 100644 index 000000000..b23427731 --- /dev/null +++ b/internal/store/model/stringarray.go @@ -0,0 +1,77 @@ +package model + +import ( + "database/sql/driver" + "fmt" + "strings" +) + +type StringArray []string + +func (a StringArray) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + if len(a) == 0 { + return "{}", nil + } + escaped := make([]string, len(a)) + for i, s := range a { + escaped[i] = `"` + strings.ReplaceAll(strings.ReplaceAll(s, `\`, `\\`), `"`, `\"`) + `"` + } + return "{" + strings.Join(escaped, ",") + "}", nil +} + +func (a *StringArray) Scan(src any) error { + if src == nil { + *a = nil + return nil + } + var raw string + switch v := src.(type) { + case string: + raw = v + case []byte: + raw = string(v) + default: + return fmt.Errorf("unsupported type for StringArray: %T", src) + } + + raw = strings.TrimSpace(raw) + if raw == "{}" || raw == "" { + *a = StringArray{} + return nil + } + + raw = strings.TrimPrefix(raw, "{") + raw = strings.TrimSuffix(raw, "}") + *a = parsePostgresArray(raw) + return nil +} + +func parsePostgresArray(s string) []string { + result := make([]string, 0, 1) + var current strings.Builder + inQuotes := false + escaped := false + + for _, r := range s { + if escaped { + current.WriteRune(r) + escaped = false + continue + } + switch { + case r == '\\': + escaped = true + case r == '"': + inQuotes = !inQuotes + case r == ',' && !inQuotes: + result = append(result, current.String()) + current.Reset() + default: + current.WriteRune(r) + } + } + return append(result, current.String()) +} diff --git a/internal/store/model/stringarray_test.go b/internal/store/model/stringarray_test.go new file mode 100644 index 000000000..50a3966f8 --- /dev/null +++ b/internal/store/model/stringarray_test.go @@ -0,0 +1,224 @@ +package model + +import ( + "testing" +) + +func TestStringArrayValue(t *testing.T) { + tests := []struct { + name string + input StringArray + want string + wantNil bool + }{ + { + name: "nil array", + input: nil, + wantNil: true, + }, + { + name: "empty array", + input: StringArray{}, + want: "{}", + }, + { + name: "single element", + input: StringArray{"hello"}, + want: `{"hello"}`, + }, + { + name: "multiple elements", + input: StringArray{"a", "b", "c"}, + want: `{"a","b","c"}`, + }, + { + name: "element with comma", + input: StringArray{"a,b", "c"}, + want: `{"a,b","c"}`, + }, + { + name: "element with double quotes", + input: StringArray{`say "hello"`, "world"}, + want: `{"say \"hello\"","world"}`, + }, + { + name: "element with backslash", + input: StringArray{`path\to\file`}, + want: `{"path\\to\\file"}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.input.Value() + if err != nil { + t.Fatalf("Value() error = %v", err) + } + if tt.wantNil { + if got != nil { + t.Errorf("Value() = %v, want nil", got) + } + return + } + if got != tt.want { + t.Errorf("Value() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestStringArrayScan(t *testing.T) { + tests := []struct { + name string + input any + want StringArray + wantNil bool + }{ + { + name: "nil input", + input: nil, + wantNil: true, + }, + { + name: "empty braces string", + input: "{}", + want: StringArray{}, + }, + { + name: "empty string", + input: "", + want: StringArray{}, + }, + { + name: "single element", + input: `{"hello"}`, + want: StringArray{"hello"}, + }, + { + name: "multiple elements", + input: `{"a","b","c"}`, + want: StringArray{"a", "b", "c"}, + }, + { + name: "element with comma", + input: `{"a,b","c"}`, + want: StringArray{"a,b", "c"}, + }, + { + name: "element with escaped quotes", + input: `{"say \"hello\"","world"}`, + want: StringArray{`say "hello"`, "world"}, + }, + { + name: "element with escaped backslash", + input: `{"path\\to\\file"}`, + want: StringArray{`path\to\file`}, + }, + { + name: "byte slice input", + input: []byte(`{"x","y"}`), + want: StringArray{"x", "y"}, + }, + { + name: "trailing empty element", + input: `{"a",""}`, + want: StringArray{"a", ""}, + }, + { + name: "single empty element", + input: `{""}`, + want: StringArray{""}, + }, + { + name: "leading empty element", + input: `{"","b"}`, + want: StringArray{"", "b"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got StringArray + err := got.Scan(tt.input) + if err != nil { + t.Fatalf("Scan() error = %v", err) + } + if tt.wantNil { + if got != nil { + t.Errorf("Scan() = %v, want nil", got) + } + return + } + if len(got) != len(tt.want) { + t.Fatalf("Scan() len = %d, want %d", len(got), len(tt.want)) + } + for i := range got { + if got[i] != tt.want[i] { + t.Errorf("Scan()[%d] = %q, want %q", i, got[i], tt.want[i]) + } + } + }) + } +} + +func TestStringArrayScanUnsupportedType(t *testing.T) { + var got StringArray + err := got.Scan(123) + if err == nil { + t.Fatal("Scan(int) should return error") + } +} + +func TestStringArrayRoundTrip(t *testing.T) { + tests := []struct { + name string + input StringArray + }{ + { + name: "simple values", + input: StringArray{"microsegmentation", "multi_cloud"}, + }, + { + name: "values with special characters", + input: StringArray{`say "hello"`, `path\to`, "a,b"}, + }, + { + name: "empty array", + input: StringArray{}, + }, + { + name: "trailing empty element", + input: StringArray{"a", ""}, + }, + { + name: "single empty element", + input: StringArray{""}, + }, + { + name: "leading empty element", + input: StringArray{"", "b"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v, err := tt.input.Value() + if err != nil { + t.Fatalf("Value() error = %v", err) + } + var got StringArray + err = got.Scan(v) + if err != nil { + t.Fatalf("Scan() error = %v", err) + } + if len(got) != len(tt.input) { + t.Fatalf("round-trip len = %d, want %d", len(got), len(tt.input)) + } + for i := range got { + if got[i] != tt.input[i] { + t.Errorf("round-trip[%d] = %q, want %q", i, got[i], tt.input[i]) + } + } + }) + } +} diff --git a/internal/store/store.go b/internal/store/store.go index 0f6028b0d..eb499696f 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -19,6 +19,7 @@ type Store interface { Assessment() Assessment AssessmentSubsetInventory() AssessmentSubsetInventory ClusterSizingInput() ClusterSizingInput + AssessmentEnhancementData() AssessmentEnhancementData Job() Job Accounts() Accounts PartnerCustomer() PartnerCustomer @@ -40,6 +41,7 @@ type DataStore struct { assessment Assessment assessmentSubsetInventory AssessmentSubsetInventory cluster ClusterSizingInput + assessmentEnhancementData AssessmentEnhancementData job Job accounts Accounts partnerCustomer PartnerCustomer @@ -60,6 +62,7 @@ func NewStore(db *gorm.DB) Store { assessment: assessment, assessmentSubsetInventory: NewAssessmentSubsetInventory(db), cluster: NewClusterSizingInputStore(db), + assessmentEnhancementData: NewAssessmentEnhancementDataStore(db), job: NewJobStore(db), authz: NewAuthzStore(db), accounts: NewAccountsStore(db), @@ -114,6 +117,10 @@ func (s *DataStore) ClusterSizingInput() ClusterSizingInput { return s.cluster } +func (s *DataStore) AssessmentEnhancementData() AssessmentEnhancementData { + return s.assessmentEnhancementData +} + func (s *DataStore) Job() Job { return s.job } diff --git a/pkg/migrations/migrations_test.go b/pkg/migrations/migrations_test.go index 7251e9619..f4ac2bfab 100644 --- a/pkg/migrations/migrations_test.go +++ b/pkg/migrations/migrations_test.go @@ -54,7 +54,7 @@ var _ = Describe("migrations", Ordered, func() { return exists } - for _, table := range []string{"relations", "agents", "sources", "keys", "image_infras", "assessments", "assessment_subset_inventories", "assessment_cluster_sizing_inputs", "groups", "members"} { + for _, table := range []string{"relations", "agents", "sources", "keys", "image_infras", "assessments", "assessment_subset_inventories", "assessment_cluster_sizing_inputs", "assessment_enhancement_data", "groups", "members"} { Expect(tableExists(table)).To(BeTrue()) } @@ -95,6 +95,7 @@ var _ = Describe("migrations", Ordered, func() { gormdb.Exec("DROP TABLE IF EXISTS relations;") gormdb.Exec("DROP TABLE IF EXISTS snapshots;") gormdb.Exec("DROP TABLE IF EXISTS assessment_cluster_sizing_inputs;") + gormdb.Exec("DROP TABLE IF EXISTS assessment_enhancement_data;") gormdb.Exec("DROP TABLE IF EXISTS assessments;") gormdb.Exec("DROP TABLE IF EXISTS agents;") gormdb.Exec("DROP TABLE IF EXISTS image_infras;") diff --git a/pkg/migrations/sql/20260730120000_assessment_enhancement_data.sql b/pkg/migrations/sql/20260730120000_assessment_enhancement_data.sql new file mode 100644 index 000000000..e8ca66e29 --- /dev/null +++ b/pkg/migrations/sql/20260730120000_assessment_enhancement_data.sql @@ -0,0 +1,44 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE IF NOT EXISTS assessment_enhancement_data ( + assessment_id VARCHAR(255) NOT NULL PRIMARY KEY + REFERENCES assessments(id) ON DELETE CASCADE, + + -- Deployed Environment + deployed_env_environment VARCHAR(50), + + -- VMware Version Counts + vmware_ver_perpetual_licenses_count INTEGER, + vmware_ver_cores_licensing_count INTEGER, + vmware_ver_environment_count INTEGER, + vmware_ver_other_nodes_count INTEGER, + + -- Active Environments + active_env_environments TEXT[], + + -- VMware Subscription + vmware_sub_level TEXT[], + + -- vSphere Core + vsphere_vm_encryption_enabled BOOLEAN, + vsphere_vm_encryption_policy TEXT, + vsphere_srm_enabled BOOLEAN, + + -- NSX / Aria + nsx_features TEXT[], + aria_ops_features TEXT[], + aria_automation_features TEXT[], + aria_secure_features TEXT[], + + -- Customer Details + customer_physical_locations_count INTEGER, + customer_target_hardware TEXT, + + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS assessment_enhancement_data; +-- +goose StatementEnd