diff --git a/.secrets.baseline b/.secrets.baseline index 86ee891d..b1c8ac1a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|package-lock.json|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-11-28T15:02:26Z", + "generated_at": "2025-12-02T13:23:17Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -118,7 +118,7 @@ "hashed_secret": "03e60e3e0d9675b19754e2a81bbb48a26af858e7", "is_secret": false, "is_verified": false, - "line_number": 943, + "line_number": 950, "type": "Secret Keyword", "verified_result": null } diff --git a/cloudinfo/catalog_types.go b/cloudinfo/catalog_types.go index a22f4cb5..2ed34c75 100644 --- a/cloudinfo/catalog_types.go +++ b/cloudinfo/catalog_types.go @@ -51,6 +51,7 @@ type CatalogJson struct { Configuration []struct { Key string `json:"key"` Type string `json:"type"` + TypeMetadata string `json:"type_metadata"` Description string `json:"description"` DefaultValue interface{} `json:"default_value"` Required bool `json:"required"` diff --git a/cloudinfo/projects.go b/cloudinfo/projects.go index e8817d03..f0be40f2 100644 --- a/cloudinfo/projects.go +++ b/cloudinfo/projects.go @@ -510,7 +510,7 @@ func (infoSvc *CloudInfoService) GetConfigAndDependenciesStates(configDetails *C return states, nil } -// IsDeployable checks if a config and all its members are deployable. If any one memeber is deployable, return true. +// IsDeployable checks if a config and all its members are deployable. If any one member is deployable, return true. // trackStackInputs tracks inputs that should not be overridden. // This function initializes a map of inputs that should be preserved in the stack configuration. func trackStackInputs(stackConfig *ConfigDetails) map[string]map[string]interface{} { @@ -873,10 +873,17 @@ func validateCatalogInputsInStackDefinition(stackJson Stack, catalogConfig Catal for _, stackInput := range stackJson.Inputs { if catalogInput.Key == stackInput.Name { found = true - expectedType := convertGoTypeToExpectedType(stackInput.Type) - if !isValidType(catalogInput.Type, expectedType) { + expectedType := stackInput.Type + if expectedType == "" { + expectedType = stackInput.TypeMetadata + } + expectedType = convertGoTypeToExpectedType(expectedType) + if catalogInput.Type != "" && !isValidType(catalogInput.Type, expectedType) { typeMismatches = append(typeMismatches, fmt.Sprintf("catalog configuration type mismatch in product '%s', flavor '%s': %s expected type: %s, got: %s", productName, flavorName, catalogInput.Key, expectedType, catalogInput.Type)) } + if catalogInput.TypeMetadata != "" && !isValidType(catalogInput.TypeMetadata, expectedType) { + typeMismatches = append(typeMismatches, fmt.Sprintf("catalog configuration type_metadata mismatch in product '%s', flavor '%s': %s expected type: %s, got: %s", productName, flavorName, catalogInput.Key, expectedType, catalogInput.TypeMetadata)) + } // Check if the default value type matches the expected type if catalogInput.DefaultValue != nil { defaultValueType := reflect.TypeOf(catalogInput.DefaultValue).String() diff --git a/cloudinfo/projects_test.go b/cloudinfo/projects_test.go index 3dcc924a..e7eb007c 100644 --- a/cloudinfo/projects_test.go +++ b/cloudinfo/projects_test.go @@ -1015,6 +1015,18 @@ func (suite *ProjectsServiceTestSuite) TestCreateStackFromConfigFile() { "catalog configuration type mismatch in product 'Product Name', flavor 'Flavor Name': input3 expected type: array, got: bool\n" + "catalog configuration type mismatch in product 'Product Name', flavor 'Flavor Name': input4 expected type: bool, got: array"), }, + { + name: "catalog input type_metadata mismatch, should return an error", + stackConfig: &ConfigDetails{ + ProjectID: "mockProjectID", + ConfigID: "54321", + }, + stackConfigPath: "testdata/stack_definition_with_type_metadata_only.json", + catalogJsonPath: "testdata/ibm_catalog_with_type_metadata_only.json", + expectedConfig: nil, + expectedError: fmt.Errorf("catalog configuration type_metadata mismatch in product 'Product Name', flavor 'Flavor Name': input5 expected type: string, got: int\n" + + "catalog configuration type_metadata mismatch in product 'Product Name', flavor 'Flavor Name': input6 expected type: string, got: bool"), + }, { // This is checking the type of the actual default value name: "catalog input default type mismatch, should return an error", diff --git a/cloudinfo/projects_types.go b/cloudinfo/projects_types.go index c78689c6..25f00c27 100644 --- a/cloudinfo/projects_types.go +++ b/cloudinfo/projects_types.go @@ -7,12 +7,13 @@ import ( type Stack struct { Inputs []struct { - Name string `json:"name"` - Description string `json:"description"` - Required bool `json:"required"` - Type string `json:"type"` - Hidden bool `json:"hidden"` - Default interface{} `json:"default"` + Name string `json:"name"` + Description string `json:"description"` + Required bool `json:"required"` + Type string `json:"type"` + TypeMetadata string `json:"type_metadata"` + Hidden bool `json:"hidden"` + Default interface{} `json:"default"` } `json:"inputs"` Outputs []struct { Name string `json:"name"` diff --git a/cloudinfo/testdata/ibm_catalog_with_type_metadata_only.json b/cloudinfo/testdata/ibm_catalog_with_type_metadata_only.json new file mode 100644 index 00000000..2dc82783 --- /dev/null +++ b/cloudinfo/testdata/ibm_catalog_with_type_metadata_only.json @@ -0,0 +1,96 @@ +{ + "products": [ + { + "label": "Product Label", + "name": "Product Name", + "product_kind": "Product Kind", + "tags": ["tag1", "tag2"], + "keywords": ["keyword1", "keyword2"], + "short_description": "Short description", + "long_description": "Long description", + "offering_docs_url": "http://example.com/docs", + "offering_icon_url": "http://example.com/icon", + "provider_name": "Provider Name", + "features": [ + { + "title": "Feature Title", + "description": "Feature Description" + } + ], + "support_details": "Support details", + "flavors": [ + { + "label": "Flavor Label", + "name": "Flavor Name", + "working_directory": "Working Directory", + "compliance": { + "authority": "Authority", + "profiles": [ + { + "profile_name": "Profile Name", + "profile_version": "Profile Version" + } + ] + }, + "iam_permissions": [ + { + "service_name": "Service Name", + "role_crns": ["crn1", "crn2"] + } + ], + "architecture": { + "features": [ + { + "title": "Architecture Feature Title", + "description": "Architecture Feature Description" + } + ], + "diagrams": [ + { + "diagram": { + "url": "http://example.com/diagram", + "caption": "Diagram Caption", + "type": "Diagram Type", + "thumbnail_url": "http://example.com/thumbnail" + }, + "description": "Diagram Description" + } + ] + }, + "configuration": [ + { + "key": "input5", + "type_metadata": "int", + "description": "Description for input5", + "default_value": "default_value_5", + "required": false, + "display_name": "Input 5" + }, + { + "key": "input6", + "type_metadata": "bool", + "description": "Description for input6", + "default_value": "", + "required": false, + "display_name": "Input 6" + }, + { + "key": "input7", + "description": "Description for input7", + "default_value": "some_value", + "required": false, + "display_name": "Input 7" + } + ], + "outputs": [ + { + "key": "output1", + "description": "Description for output1" + } + ], + "install_type": "Install Type" + } + ] + } + ] +} diff --git a/cloudinfo/testdata/stack_definition_with_type_metadata_only.json b/cloudinfo/testdata/stack_definition_with_type_metadata_only.json new file mode 100644 index 00000000..4ece5693 --- /dev/null +++ b/cloudinfo/testdata/stack_definition_with_type_metadata_only.json @@ -0,0 +1,43 @@ +{ + "inputs": [ + { + "name": "input5", + "required": false, + "type_metadata": "string", + "hidden": false, + "default": "stack_value_5" + }, + { + "name": "input6", + "required": false, + "type_metadata": "string", + "hidden": false, + "default": "stack_value_6" + }, + { + "name": "input7", + "required": false, + "type_metadata": "string", + "hidden": false, + "default": "stack_value_7" + } + ], + "outputs": [ + { + "name": "output1", + "value": "ref:../members/member1/outputs/output1" + } + ], + "members": [ + { + "inputs": [ + { + "name": "input5", + "value": "ref:../../inputs/input5" + } + ], + "name": "member1", + "version_locator": "version1" + } + ] +}