From 301689c5cb77d0bf2d065abe3fae6f87cbe84aa9 Mon Sep 17 00:00:00 2001 From: Bernardo Pastorelli <13519917+randomswdev@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:51:44 +0100 Subject: [PATCH] Address review changes --- .../ENHANCEMENTS-20240116-102758.yaml | 6 + DESIGN.md | 2 + .../kubernetes/provider_code_spec.json | 21 + internal/explorer/config_explorer.go | 28 +- internal/explorer/eplorer_utils_test.go | 361 ---------- internal/explorer/explorer.go | 18 +- internal/explorer/explorer_utils.go | 10 +- internal/explorer/explorer_utils_test.go | 672 ++++++++++++++++++ 8 files changed, 731 insertions(+), 387 deletions(-) create mode 100644 .changes/unreleased/ENHANCEMENTS-20240116-102758.yaml delete mode 100644 internal/explorer/eplorer_utils_test.go create mode 100644 internal/explorer/explorer_utils_test.go diff --git a/.changes/unreleased/ENHANCEMENTS-20240116-102758.yaml b/.changes/unreleased/ENHANCEMENTS-20240116-102758.yaml new file mode 100644 index 0000000..901eafd --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20240116-102758.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: Added data source and resource support for query and path parameters specified + in the [OAS Path Item](https://spec.openapis.org/oas/v3.1.0#path-item-object) +time: 2024-01-16T10:27:58.255575839+01:00 +custom: + Issue: "114" diff --git a/DESIGN.md b/DESIGN.md index a39c870..ca44e24 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -50,6 +50,7 @@ In these OAS operations, the generator will search the `create` and `read` for s - Will attempt to use `application/json` content-type first. If not found, will grab the first available content-type with a schema (alphabetical order) 4. `read` operation: [parameters](https://spec.openapis.org/oas/v3.1.0#parameterObject) - The generator will merge all `query` and `path` parameters to the root of the schema. + - The generator will consider as parameters the ones in the [OAS Path Item](https://spec.openapis.org/oas/v3.1.0#path-item-object) and the ones in the [OAS Operation](https://spec.openapis.org/oas/v3.1.0#operation-object), merged based on the rules in the specification All schemas found will be deep merged together, with the `requestBody` schema from the `create` operation being the **main schema** that the others will be merged on top. The deep merge has the following characteristics: @@ -72,6 +73,7 @@ data_sources: The generator uses the `read` operation to map to the provider code specification. Multiple schemas will have the [OAS types mapped to Provider Attributes](#oas-types-to-provider-attributes) and then be merged together; with the final result being the [Data Source](https://developer.hashicorp.com/terraform/plugin/code-generation/specification#data-source) `schema`. The schemas that will be merged together (in priority order): 1. `read` operation: [parameters](https://spec.openapis.org/oas/v3.1.0#parameterObject) - The generator will merge all `query` and `path` parameters to the root of the schema. + - The generator will consider as parameters the ones in the [Path Item Object](https://spec.openapis.org/oas/v3.1.0#path-item-object) and the ones in the [Operation Object](https://spec.openapis.org/oas/v3.1.0#operation-object), merged based on the rules in the specification 2. `read` operation: response body in [responses](https://spec.openapis.org/oas/v3.1.0#responsesObject) - The response body is the only schema **required** for data sources. If not found, the generator will skip the data source without mapping. - Will attempt to use `200` or `201` response body. If not found, will grab the first available `2xx` response code with a schema (lexicographic order) diff --git a/internal/cmd/testdata/kubernetes/provider_code_spec.json b/internal/cmd/testdata/kubernetes/provider_code_spec.json index e322643..9aa89b4 100644 --- a/internal/cmd/testdata/kubernetes/provider_code_spec.json +++ b/internal/cmd/testdata/kubernetes/provider_code_spec.json @@ -9221,6 +9221,27 @@ ], "description": "Most recently observed status of the Deployment." } + }, + { + "name": "name", + "string": { + "computed_optional_required": "computed_optional", + "description": "name of the Deployment" + } + }, + { + "name": "namespace", + "string": { + "computed_optional_required": "computed_optional", + "description": "object name and auth scope, such as for teams and projects" + } + }, + { + "name": "pretty", + "string": { + "computed_optional_required": "computed_optional", + "description": "If 'true', then the output is pretty printed." + } } ] } diff --git a/internal/explorer/config_explorer.go b/internal/explorer/config_explorer.go index a0ceaa8..cc5fa13 100644 --- a/internal/explorer/config_explorer.go +++ b/internal/explorer/config_explorer.go @@ -81,19 +81,19 @@ func (e configExplorer) FindResources() (map[string]Resource, error) { continue } - parameters, err := extractParameters(e.spec.Paths, resourceConfig.Read.Path) + commonParameters, err := extractCommonParameters(e.spec.Paths, resourceConfig.Read.Path) if err != nil { - errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s.delete': %w", name, err)) + errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s' common parameters: %w", name, err)) continue } resources[name] = Resource{ - CreateOp: createOp, - ReadOp: readOp, - UpdateOp: updateOp, - DeleteOp: deleteOp, - Parameters: parameters, - SchemaOptions: extractSchemaOptions(resourceConfig.SchemaOptions), + CreateOp: createOp, + ReadOp: readOp, + UpdateOp: updateOp, + DeleteOp: deleteOp, + CommonParameters: commonParameters, + SchemaOptions: extractSchemaOptions(resourceConfig.SchemaOptions), } } @@ -111,16 +111,16 @@ func (e configExplorer) FindDataSources() (map[string]DataSource, error) { continue } - parameters, err := extractParameters(e.spec.Paths, dataSourceConfig.Read.Path) + commonParameters, err := extractCommonParameters(e.spec.Paths, dataSourceConfig.Read.Path) if err != nil { - errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s.delete': %w", name, err)) + errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s' common parameters: %w", name, err)) continue } dataSources[name] = DataSource{ - ReadOp: readOp, - Parameters: parameters, - SchemaOptions: extractSchemaOptions(dataSourceConfig.SchemaOptions), + ReadOp: readOp, + CommonParameters: commonParameters, + SchemaOptions: extractSchemaOptions(dataSourceConfig.SchemaOptions), } } return dataSources, errResult @@ -160,7 +160,7 @@ func extractOp(paths *high.Paths, oasLocation *config.OpenApiSpecLocation) (*hig } } -func extractParameters(paths *high.Paths, path string) ([]*high.Parameter, error) { +func extractCommonParameters(paths *high.Paths, path string) ([]*high.Parameter, error) { // No need to search OAS if not defined if paths.PathItems.GetOrZero(path) == nil { return nil, fmt.Errorf("path '%s' not found in OpenAPI spec", path) diff --git a/internal/explorer/eplorer_utils_test.go b/internal/explorer/eplorer_utils_test.go deleted file mode 100644 index 9f8cae1..0000000 --- a/internal/explorer/eplorer_utils_test.go +++ /dev/null @@ -1,361 +0,0 @@ -package explorer - -import ( - "sync" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/mapper/util" - "github.com/pb33f/libopenapi/datamodel/high/base" - high "github.com/pb33f/libopenapi/datamodel/high/v3" -) - -func TestMergeParameters(t *testing.T) { - testCases := map[string]struct { - noReadOp bool - readParams []*high.Parameter - commonParams []*high.Parameter - want []*high.Parameter - }{ - "merge common and operation": { - noReadOp: false, - readParams: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "bool_prop", - Required: pointer(true), - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"boolean"}, - Description: "hey this is a bool, required!", - }), - }, - { - Name: "float64_prop", - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"number"}, - Format: "float", - Description: "hey this is a float64!", - }), - }, - }, - commonParams: []*high.Parameter{ - { - Name: "common_string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - }, - want: []*high.Parameter{ - { - Name: "common_string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "bool_prop", - Required: pointer(true), - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"boolean"}, - Description: "hey this is a bool, required!", - }), - }, - { - Name: "float64_prop", - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"number"}, - Format: "float", - Description: "hey this is a float64!", - }), - }, - }, - }, - "nil common parameters": { - noReadOp: false, - readParams: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "bool_prop", - Required: pointer(true), - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"boolean"}, - Description: "hey this is a bool, required!", - }), - }, - { - Name: "float64_prop", - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"number"}, - Format: "float", - Description: "hey this is a float64!", - }), - }, - }, - commonParams: nil, - want: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "bool_prop", - Required: pointer(true), - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"boolean"}, - Description: "hey this is a bool, required!", - }), - }, - { - Name: "float64_prop", - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"number"}, - Format: "float", - Description: "hey this is a float64!", - }), - }, - }, - }, - "nil read parameters": { - noReadOp: false, - readParams: nil, - commonParams: []*high.Parameter{ - { - Name: "common_string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - }, - want: []*high.Parameter{ - { - Name: "common_string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - }, - }, - "all nil": { - noReadOp: false, - readParams: nil, - commonParams: nil, - want: nil, - }, - "read overrides common": { - noReadOp: false, - readParams: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "bool_prop", - Required: pointer(true), - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"boolean"}, - Description: "hey this is a bool, required!", - }), - }, - { - Name: "float64_prop", - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"number"}, - Format: "float", - Description: "hey this is a float64!", - }), - }, - }, - commonParams: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(false), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - }, - want: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(true), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - { - Name: "bool_prop", - Required: pointer(true), - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"boolean"}, - Description: "hey this is a bool, required!", - }), - }, - { - Name: "float64_prop", - In: "query", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"number"}, - Format: "float", - Description: "hey this is a float64!", - }), - }, - }, - }, - "no read op": { - noReadOp: true, - readParams: nil, - commonParams: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(false), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - }, - want: []*high.Parameter{ - { - Name: "string_prop", - Required: pointer(false), - In: "path", - Description: "hey this is a string, required and overidden!", - Schema: base.CreateSchemaProxy(&base.Schema{ - Type: []string{"string"}, - Format: util.OAS_format_password, - Description: "you shouldn't see this because the description is overridden!", - }), - }, - }, - }, - } - for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - var readOp *high.Operation = nil - if !testCase.noReadOp { - readOp = &high.Operation{ - Parameters: testCase.readParams, - } - } - - resource := Resource{ - ReadOp: readOp, - Parameters: testCase.commonParams, - } - - mergedParameters := resource.ReadOpParameters() - - if diff := cmp.Diff(mergedParameters, testCase.want, cmp.AllowUnexported(base.Schema{}, base.SchemaProxy{}, sync.Mutex{}, high.Parameter{})); diff != "" { - t.Errorf("unexpected difference for resource: %s", diff) - } - - dataSource := DataSource{ - ReadOp: &high.Operation{ - Parameters: testCase.readParams, - }, - Parameters: testCase.commonParams, - } - - mergedParameters = dataSource.ReadOpParameters() - - if diff := cmp.Diff(mergedParameters, testCase.want, cmp.AllowUnexported(base.Schema{}, base.SchemaProxy{}, sync.Mutex{}, high.Parameter{})); diff != "" { - t.Errorf("unexpected difference for data source: %s", diff) - } - }) - } -} - -func pointer[T any](value T) *T { - return &value -} diff --git a/internal/explorer/explorer.go b/internal/explorer/explorer.go index c7c4c5b..908b62c 100644 --- a/internal/explorer/explorer.go +++ b/internal/explorer/explorer.go @@ -17,19 +17,19 @@ type Explorer interface { // Resource contains CRUD operations and schema options for configuration. type Resource struct { - CreateOp *high.Operation - ReadOp *high.Operation - UpdateOp *high.Operation - DeleteOp *high.Operation - Parameters []*high.Parameter - SchemaOptions SchemaOptions + CreateOp *high.Operation + ReadOp *high.Operation + UpdateOp *high.Operation + DeleteOp *high.Operation + CommonParameters []*high.Parameter + SchemaOptions SchemaOptions } // DataSource contains a Read operation and schema options for configuration. type DataSource struct { - ReadOp *high.Operation - Parameters []*high.Parameter - SchemaOptions SchemaOptions + ReadOp *high.Operation + CommonParameters []*high.Parameter + SchemaOptions SchemaOptions } // Provider contains a name and a schema. diff --git a/internal/explorer/explorer_utils.go b/internal/explorer/explorer_utils.go index 33968c8..7110795 100644 --- a/internal/explorer/explorer_utils.go +++ b/internal/explorer/explorer_utils.go @@ -1,9 +1,13 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package explorer import high "github.com/pb33f/libopenapi/datamodel/high/v3" func mergeParameters(commonParameters []*high.Parameter, operation *high.Operation) []*high.Parameter { - mergedParameters := commonParameters + mergedParameters := make([]*high.Parameter, len(commonParameters)) + copy(mergedParameters, commonParameters) if operation != nil { for _, operationParameter := range operation.Parameters { found := false @@ -23,9 +27,9 @@ func mergeParameters(commonParameters []*high.Parameter, operation *high.Operati } func (e *Resource) ReadOpParameters() []*high.Parameter { - return mergeParameters(e.Parameters, e.ReadOp) + return mergeParameters(e.CommonParameters, e.ReadOp) } func (e *DataSource) ReadOpParameters() []*high.Parameter { - return mergeParameters(e.Parameters, e.ReadOp) + return mergeParameters(e.CommonParameters, e.ReadOp) } diff --git a/internal/explorer/explorer_utils_test.go b/internal/explorer/explorer_utils_test.go new file mode 100644 index 0000000..36927b3 --- /dev/null +++ b/internal/explorer/explorer_utils_test.go @@ -0,0 +1,672 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package explorer + +import ( + "sync" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/mapper/util" + "github.com/pb33f/libopenapi/datamodel/high/base" + high "github.com/pb33f/libopenapi/datamodel/high/v3" +) + +func TestReadOpParameters_Resource(t *testing.T) { + testCases := map[string]struct { + readOp *high.Operation + commonParams []*high.Parameter + want []*high.Parameter + }{ + "merge common and operation": { + readOp: &high.Operation{ + Parameters: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + commonParams: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + "nil common parameters": { + readOp: &high.Operation{ + Parameters: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + commonParams: nil, + want: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + "nil read parameters": { + readOp: &high.Operation{ + Parameters: nil, + }, + commonParams: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + }, + "all nil": { + readOp: nil, + commonParams: nil, + want: []*high.Parameter{}, + }, + "read overrides common": { + readOp: &high.Operation{ + Parameters: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + commonParams: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(false), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + "no read op": { + readOp: nil, + commonParams: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(false), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(false), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + }, + } + for name, testCase := range testCases { + name, testCase := name, testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + resource := Resource{ + ReadOp: testCase.readOp, + CommonParameters: testCase.commonParams, + } + + mergedParameters := resource.ReadOpParameters() + + if diff := cmp.Diff(mergedParameters, testCase.want, cmp.AllowUnexported(base.Schema{}, base.SchemaProxy{}, sync.Mutex{}, high.Parameter{})); diff != "" { + t.Errorf("unexpected difference for resource: %s", diff) + } + }) + } +} + +func TestReadOpParameters_DataSource(t *testing.T) { + testCases := map[string]struct { + readOp *high.Operation + commonParams []*high.Parameter + want []*high.Parameter + }{ + "merge common and operation": { + readOp: &high.Operation{ + Parameters: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + commonParams: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + "nil common parameters": { + readOp: &high.Operation{ + Parameters: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + commonParams: nil, + want: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + "nil read parameters": { + readOp: &high.Operation{ + Parameters: nil, + }, + commonParams: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "common_string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + }, + "all nil": { + readOp: nil, + commonParams: nil, + want: []*high.Parameter{}, + }, + "read overrides common": { + readOp: &high.Operation{ + Parameters: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + commonParams: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(false), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(true), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + { + Name: "bool_prop", + Required: pointer(true), + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"boolean"}, + Description: "hey this is a bool, required!", + }), + }, + { + Name: "float64_prop", + In: "query", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"number"}, + Format: "float", + Description: "hey this is a float64!", + }), + }, + }, + }, + "no read op": { + readOp: nil, + commonParams: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(false), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + want: []*high.Parameter{ + { + Name: "string_prop", + Required: pointer(false), + In: "path", + Description: "hey this is a string, required and overidden!", + Schema: base.CreateSchemaProxy(&base.Schema{ + Type: []string{"string"}, + Format: util.OAS_format_password, + Description: "you shouldn't see this because the description is overridden!", + }), + }, + }, + }, + } + for name, testCase := range testCases { + name, testCase := name, testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + dataSource := DataSource{ + ReadOp: testCase.readOp, + CommonParameters: testCase.commonParams, + } + + mergedParameters := dataSource.ReadOpParameters() + + if diff := cmp.Diff(mergedParameters, testCase.want, cmp.AllowUnexported(base.Schema{}, base.SchemaProxy{}, sync.Mutex{}, high.Parameter{})); diff != "" { + t.Errorf("unexpected difference for data source: %s", diff) + } + }) + } +} + +func pointer[T any](value T) *T { + return &value +}