Skip to content

Commit 8425855

Browse files
authored
Add support for common parameters for resources and data sources (#114)
* Add support for common parameters * Address review changes --------- Co-authored-by: Bernardo Pastorelli <[email protected]>
1 parent 94def28 commit 8425855

File tree

9 files changed

+852
-92
lines changed

9 files changed

+852
-92
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: ENHANCEMENTS
2+
body: Added data source and resource support for query and path parameters specified
3+
in the [OAS Path Item](https://spec.openapis.org/oas/v3.1.0#path-item-object)
4+
time: 2024-01-16T10:27:58.255575839+01:00
5+
custom:
6+
Issue: "114"

DESIGN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ In these OAS operations, the generator will search the `create` and `read` for s
5050
- Will attempt to use `application/json` content-type first. If not found, will grab the first available content-type with a schema (alphabetical order)
5151
4. `read` operation: [parameters](https://spec.openapis.org/oas/v3.1.0#parameterObject)
5252
- The generator will merge all `query` and `path` parameters to the root of the schema.
53+
- 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
5354

5455
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:
5556

@@ -72,6 +73,7 @@ data_sources:
7273
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):
7374
1. `read` operation: [parameters](https://spec.openapis.org/oas/v3.1.0#parameterObject)
7475
- The generator will merge all `query` and `path` parameters to the root of the schema.
76+
- 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
7577
2. `read` operation: response body in [responses](https://spec.openapis.org/oas/v3.1.0#responsesObject)
7678
- The response body is the only schema **required** for data sources. If not found, the generator will skip the data source without mapping.
7779
- 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)

internal/cmd/testdata/kubernetes/provider_code_spec.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9221,6 +9221,27 @@
92219221
],
92229222
"description": "Most recently observed status of the Deployment."
92239223
}
9224+
},
9225+
{
9226+
"name": "name",
9227+
"string": {
9228+
"computed_optional_required": "computed_optional",
9229+
"description": "name of the Deployment"
9230+
}
9231+
},
9232+
{
9233+
"name": "namespace",
9234+
"string": {
9235+
"computed_optional_required": "computed_optional",
9236+
"description": "object name and auth scope, such as for teams and projects"
9237+
}
9238+
},
9239+
{
9240+
"name": "pretty",
9241+
"string": {
9242+
"computed_optional_required": "computed_optional",
9243+
"description": "If 'true', then the output is pretty printed."
9244+
}
92249245
}
92259246
]
92269247
}

internal/explorer/config_explorer.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,19 @@ func (e configExplorer) FindResources() (map[string]Resource, error) {
8181
continue
8282
}
8383

84+
commonParameters, err := extractCommonParameters(e.spec.Paths, resourceConfig.Read.Path)
85+
if err != nil {
86+
errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s' common parameters: %w", name, err))
87+
continue
88+
}
89+
8490
resources[name] = Resource{
85-
CreateOp: createOp,
86-
ReadOp: readOp,
87-
UpdateOp: updateOp,
88-
DeleteOp: deleteOp,
89-
SchemaOptions: extractSchemaOptions(resourceConfig.SchemaOptions),
91+
CreateOp: createOp,
92+
ReadOp: readOp,
93+
UpdateOp: updateOp,
94+
DeleteOp: deleteOp,
95+
CommonParameters: commonParameters,
96+
SchemaOptions: extractSchemaOptions(resourceConfig.SchemaOptions),
9097
}
9198
}
9299

@@ -103,9 +110,17 @@ func (e configExplorer) FindDataSources() (map[string]DataSource, error) {
103110
errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s.read': %w", name, err))
104111
continue
105112
}
113+
114+
commonParameters, err := extractCommonParameters(e.spec.Paths, dataSourceConfig.Read.Path)
115+
if err != nil {
116+
errResult = errors.Join(errResult, fmt.Errorf("failed to extract '%s' common parameters: %w", name, err))
117+
continue
118+
}
119+
106120
dataSources[name] = DataSource{
107-
ReadOp: readOp,
108-
SchemaOptions: extractSchemaOptions(dataSourceConfig.SchemaOptions),
121+
ReadOp: readOp,
122+
CommonParameters: commonParameters,
123+
SchemaOptions: extractSchemaOptions(dataSourceConfig.SchemaOptions),
109124
}
110125
}
111126
return dataSources, errResult
@@ -145,6 +160,17 @@ func extractOp(paths *high.Paths, oasLocation *config.OpenApiSpecLocation) (*hig
145160
}
146161
}
147162

163+
func extractCommonParameters(paths *high.Paths, path string) ([]*high.Parameter, error) {
164+
// No need to search OAS if not defined
165+
if paths.PathItems.GetOrZero(path) == nil {
166+
return nil, fmt.Errorf("path '%s' not found in OpenAPI spec", path)
167+
}
168+
169+
pathItem, _ := paths.PathItems.Get(path)
170+
171+
return pathItem.Parameters, nil
172+
}
173+
148174
func extractSchemaProxy(document high.Document, componentRef string) (*highbase.SchemaProxy, error) {
149175
// find the reference using the root document.Index
150176
indexRef := document.Index.FindComponentInRoot(componentRef)

internal/explorer/explorer.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ type Explorer interface {
1717

1818
// Resource contains CRUD operations and schema options for configuration.
1919
type Resource struct {
20-
CreateOp *high.Operation
21-
ReadOp *high.Operation
22-
UpdateOp *high.Operation
23-
DeleteOp *high.Operation
24-
SchemaOptions SchemaOptions
20+
CreateOp *high.Operation
21+
ReadOp *high.Operation
22+
UpdateOp *high.Operation
23+
DeleteOp *high.Operation
24+
CommonParameters []*high.Parameter
25+
SchemaOptions SchemaOptions
2526
}
2627

2728
// DataSource contains a Read operation and schema options for configuration.
2829
type DataSource struct {
29-
ReadOp *high.Operation
30-
SchemaOptions SchemaOptions
30+
ReadOp *high.Operation
31+
CommonParameters []*high.Parameter
32+
SchemaOptions SchemaOptions
3133
}
3234

3335
// Provider contains a name and a schema.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package explorer
5+
6+
import high "github.com/pb33f/libopenapi/datamodel/high/v3"
7+
8+
func mergeParameters(commonParameters []*high.Parameter, operation *high.Operation) []*high.Parameter {
9+
mergedParameters := make([]*high.Parameter, len(commonParameters))
10+
copy(mergedParameters, commonParameters)
11+
if operation != nil {
12+
for _, operationParameter := range operation.Parameters {
13+
found := false
14+
for i, mergedParameter := range mergedParameters {
15+
if operationParameter.Name == mergedParameter.Name {
16+
found = true
17+
mergedParameters[i] = operationParameter
18+
break
19+
}
20+
}
21+
if !found {
22+
mergedParameters = append(mergedParameters, operationParameter)
23+
}
24+
}
25+
}
26+
return mergedParameters
27+
}
28+
29+
func (e *Resource) ReadOpParameters() []*high.Parameter {
30+
return mergeParameters(e.CommonParameters, e.ReadOp)
31+
}
32+
33+
func (e *DataSource) ReadOpParameters() []*high.Parameter {
34+
return mergeParameters(e.CommonParameters, e.ReadOp)
35+
}

0 commit comments

Comments
 (0)