mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Adds provider function to tofu scheme/metadata commands (#1753)
Signed-off-by: Andrew Hayes <andrew.hayes@harness.io>
This commit is contained in:
parent
edc654c1de
commit
7e706fa1a7
114
internal/command/jsonprovider/function.go
Normal file
114
internal/command/jsonprovider/function.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) The OpenTofu Authors
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) 2023 HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package jsonprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/opentofu/opentofu/internal/providers"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mapTypeName = "map"
|
||||||
|
listTypeName = "list"
|
||||||
|
setTypeName = "set"
|
||||||
|
tupleTypeName = "tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function is the top-level object returned when exporting function schemas
|
||||||
|
type Function struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
ReturnType any `json:"return_type"`
|
||||||
|
Parameters []*FunctionParam `json:"parameters,omitempty"`
|
||||||
|
VariadicParameter *FunctionParam `json:"variadic_parameter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionParam is the object for wrapping the functions parameters and return types
|
||||||
|
type FunctionParam struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Type any `json:"type"`
|
||||||
|
IsNullable *bool `json:"is_nullable,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalReturnType(returnType cty.Type) any {
|
||||||
|
switch {
|
||||||
|
case returnType.IsObjectType():
|
||||||
|
return []any{
|
||||||
|
returnType.FriendlyName(),
|
||||||
|
returnType.AttributeTypes(),
|
||||||
|
}
|
||||||
|
case returnType.IsListType():
|
||||||
|
return []any{
|
||||||
|
listTypeName,
|
||||||
|
returnType.ListElementType(),
|
||||||
|
}
|
||||||
|
case returnType.IsMapType():
|
||||||
|
return []any{
|
||||||
|
mapTypeName,
|
||||||
|
returnType.MapElementType(),
|
||||||
|
}
|
||||||
|
case returnType.IsSetType():
|
||||||
|
return []any{
|
||||||
|
setTypeName,
|
||||||
|
returnType.SetElementType(),
|
||||||
|
}
|
||||||
|
case returnType.IsTupleType():
|
||||||
|
return []any{
|
||||||
|
tupleTypeName,
|
||||||
|
returnType.TupleElementTypes(),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return returnType.FriendlyName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalParameter(parameter providers.FunctionParameterSpec) *FunctionParam {
|
||||||
|
var output FunctionParam
|
||||||
|
output.Description = parameter.Description
|
||||||
|
output.Name = parameter.Name
|
||||||
|
output.Type = marshalReturnType(parameter.Type)
|
||||||
|
|
||||||
|
if parameter.AllowNullValue {
|
||||||
|
isNullable := true
|
||||||
|
output.IsNullable = &isNullable
|
||||||
|
}
|
||||||
|
|
||||||
|
return &output
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalParameters(parameters []providers.FunctionParameterSpec) []*FunctionParam {
|
||||||
|
output := make([]*FunctionParam, 0, len(parameters))
|
||||||
|
for _, parameter := range parameters {
|
||||||
|
output = append(output, marshalParameter(parameter))
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalFunction(function providers.FunctionSpec) *Function {
|
||||||
|
var output Function
|
||||||
|
output.Description = function.Description
|
||||||
|
output.Summary = function.Summary
|
||||||
|
output.ReturnType = marshalReturnType(function.Return)
|
||||||
|
output.Parameters = marshalParameters(function.Parameters)
|
||||||
|
if function.VariadicParameter != nil {
|
||||||
|
output.VariadicParameter = marshalParameter(*function.VariadicParameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &output
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalFunctions(functions map[string]providers.FunctionSpec) map[string]*Function {
|
||||||
|
if functions == nil {
|
||||||
|
return map[string]*Function{}
|
||||||
|
}
|
||||||
|
output := make(map[string]*Function, len(functions))
|
||||||
|
for k, v := range functions {
|
||||||
|
output[k] = marshalFunction(v)
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
250
internal/command/jsonprovider/function_test.go
Normal file
250
internal/command/jsonprovider/function_test.go
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
// Copyright (c) The OpenTofu Authors
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) 2023 HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package jsonprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/opentofu/opentofu/internal/providers"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalReturnType(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
Arg cty.Type
|
||||||
|
Expected any
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]testcase{
|
||||||
|
"string": {
|
||||||
|
Arg: cty.String,
|
||||||
|
Expected: "string",
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
Arg: cty.Number,
|
||||||
|
Expected: "number",
|
||||||
|
},
|
||||||
|
"bool": {
|
||||||
|
Arg: cty.Bool,
|
||||||
|
Expected: "bool",
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
Arg: cty.Object(map[string]cty.Type{"number_type": cty.Number}),
|
||||||
|
Expected: []any{
|
||||||
|
string("object"),
|
||||||
|
map[string]cty.Type{"number_type": cty.Number},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"map": {
|
||||||
|
Arg: cty.Map(cty.String),
|
||||||
|
Expected: []any{
|
||||||
|
string("map"),
|
||||||
|
cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
Arg: cty.List(cty.Bool),
|
||||||
|
Expected: []any{
|
||||||
|
string("list"),
|
||||||
|
cty.Bool,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
Arg: cty.Set(cty.Number),
|
||||||
|
Expected: []any{
|
||||||
|
string("set"),
|
||||||
|
cty.Number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tuple": {
|
||||||
|
Arg: cty.Tuple([]cty.Type{cty.String}),
|
||||||
|
Expected: []any{
|
||||||
|
string("tuple"),
|
||||||
|
[]any{cty.String},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range tests {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
actual := marshalReturnType(tc.Arg)
|
||||||
|
|
||||||
|
// to avoid the nightmare of comparing cty primitve types we can marshal them to json and compare that
|
||||||
|
actualJSON, _ := json.Marshal(actual)
|
||||||
|
expectedJSON, _ := json.Marshal(tc.Expected)
|
||||||
|
if !cmp.Equal(actualJSON, expectedJSON) {
|
||||||
|
t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalParameter(t *testing.T) {
|
||||||
|
// used so can make a pointer to it
|
||||||
|
trueBoolVal := true
|
||||||
|
|
||||||
|
type testcase struct {
|
||||||
|
Arg providers.FunctionParameterSpec
|
||||||
|
Expected FunctionParam
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]testcase{
|
||||||
|
"basic": {
|
||||||
|
Arg: providers.FunctionParameterSpec{
|
||||||
|
Description: "basic string func",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
Expected: FunctionParam{
|
||||||
|
Description: "basic string func",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nullable": {
|
||||||
|
Arg: providers.FunctionParameterSpec{
|
||||||
|
Description: "nullable number func",
|
||||||
|
Type: cty.Number,
|
||||||
|
AllowNullValue: trueBoolVal,
|
||||||
|
},
|
||||||
|
Expected: FunctionParam{
|
||||||
|
Description: "nullable number func",
|
||||||
|
Type: cty.Number,
|
||||||
|
IsNullable: &trueBoolVal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range tests {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
actual := marshalParameter(tc.Arg)
|
||||||
|
|
||||||
|
// to avoid the nightmare of comparing cty primitve types we can marshal them to json and compare that
|
||||||
|
actualJSON, _ := json.Marshal(actual)
|
||||||
|
expectedJSON, _ := json.Marshal(tc.Expected)
|
||||||
|
if !cmp.Equal(actualJSON, expectedJSON) {
|
||||||
|
t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalParameters(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
Arg []providers.FunctionParameterSpec
|
||||||
|
Expected []FunctionParam
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]testcase{
|
||||||
|
"basic": {
|
||||||
|
Arg: []providers.FunctionParameterSpec{{
|
||||||
|
Description: "basic string func",
|
||||||
|
Type: cty.String,
|
||||||
|
}},
|
||||||
|
Expected: []FunctionParam{{
|
||||||
|
Description: "basic string func",
|
||||||
|
Type: cty.String,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range tests {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
actual := marshalParameters(tc.Arg)
|
||||||
|
|
||||||
|
// to avoid the nightmare of comparing cty primitve types we can marshal them to json and compare that
|
||||||
|
actualJSON, _ := json.Marshal(actual)
|
||||||
|
expectedJSON, _ := json.Marshal(tc.Expected)
|
||||||
|
if !cmp.Equal(actualJSON, expectedJSON) {
|
||||||
|
t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalFunction(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
Arg providers.FunctionSpec
|
||||||
|
Expected Function
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]testcase{
|
||||||
|
"basic": {
|
||||||
|
Arg: providers.FunctionSpec{
|
||||||
|
Description: "basic string func",
|
||||||
|
Return: cty.String,
|
||||||
|
},
|
||||||
|
Expected: Function{
|
||||||
|
Description: "basic string func",
|
||||||
|
ReturnType: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"variadic": {
|
||||||
|
Arg: providers.FunctionSpec{
|
||||||
|
Description: "basic string func",
|
||||||
|
Return: cty.String,
|
||||||
|
VariadicParameter: &providers.FunctionParameterSpec{
|
||||||
|
Description: "basic string func",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expected: Function{
|
||||||
|
Description: "basic string func",
|
||||||
|
ReturnType: cty.String,
|
||||||
|
VariadicParameter: &FunctionParam{
|
||||||
|
Description: "basic string func",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range tests {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
actual := marshalFunction(tc.Arg)
|
||||||
|
|
||||||
|
// to avoid the nightmare of comparing cty primitve types we can marshal them to json and compare that
|
||||||
|
actualJSON, _ := json.Marshal(actual)
|
||||||
|
expectedJSON, _ := json.Marshal(tc.Expected)
|
||||||
|
if !cmp.Equal(actualJSON, expectedJSON) {
|
||||||
|
t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalFunctions(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
Arg map[string]providers.FunctionSpec
|
||||||
|
Expected map[string]Function
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]testcase{
|
||||||
|
"basic": {
|
||||||
|
Arg: map[string]providers.FunctionSpec{"basic_func": {
|
||||||
|
Description: "basic string func",
|
||||||
|
Return: cty.String,
|
||||||
|
}},
|
||||||
|
Expected: map[string]Function{"basic_func": {
|
||||||
|
Description: "basic string func",
|
||||||
|
ReturnType: cty.String,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range tests {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
actual := marshalFunctions(tc.Arg)
|
||||||
|
|
||||||
|
// to avoid the nightmare of comparing cty primitve types we can marshal them to json and compare that
|
||||||
|
actualJSON, _ := json.Marshal(actual)
|
||||||
|
expectedJSON, _ := json.Marshal(tc.Expected)
|
||||||
|
if !cmp.Equal(actualJSON, expectedJSON) {
|
||||||
|
t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -24,9 +24,10 @@ type Providers struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
Provider *Schema `json:"provider,omitempty"`
|
Provider *Schema `json:"provider,omitempty"`
|
||||||
ResourceSchemas map[string]*Schema `json:"resource_schemas,omitempty"`
|
ResourceSchemas map[string]*Schema `json:"resource_schemas,omitempty"`
|
||||||
DataSourceSchemas map[string]*Schema `json:"data_source_schemas,omitempty"`
|
DataSourceSchemas map[string]*Schema `json:"data_source_schemas,omitempty"`
|
||||||
|
Functions map[string]*Function `json:"functions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProviders() *Providers {
|
func newProviders() *Providers {
|
||||||
@ -61,5 +62,6 @@ func marshalProvider(tps providers.ProviderSchema) *Provider {
|
|||||||
Provider: marshalSchema(tps.Provider),
|
Provider: marshalSchema(tps.Provider),
|
||||||
ResourceSchemas: marshalSchemas(tps.ResourceTypes),
|
ResourceSchemas: marshalSchemas(tps.ResourceTypes),
|
||||||
DataSourceSchemas: marshalSchemas(tps.DataSources),
|
DataSourceSchemas: marshalSchemas(tps.DataSources),
|
||||||
|
Functions: marshalFunctions(tps.Functions),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ func TestMarshalProvider(t *testing.T) {
|
|||||||
Provider: &Schema{},
|
Provider: &Schema{},
|
||||||
ResourceSchemas: map[string]*Schema{},
|
ResourceSchemas: map[string]*Schema{},
|
||||||
DataSourceSchemas: map[string]*Schema{},
|
DataSourceSchemas: map[string]*Schema{},
|
||||||
|
Functions: map[string]*Function{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -146,6 +147,7 @@ func TestMarshalProvider(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Functions: map[string]*Function{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -223,5 +225,6 @@ func testProvider() providers.ProviderSchema {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Functions: map[string]providers.FunctionSpec{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@ type providerSchema struct {
|
|||||||
Provider interface{} `json:"provider,omitempty"`
|
Provider interface{} `json:"provider,omitempty"`
|
||||||
ResourceSchemas map[string]interface{} `json:"resource_schemas,omitempty"`
|
ResourceSchemas map[string]interface{} `json:"resource_schemas,omitempty"`
|
||||||
DataSourceSchemas map[string]interface{} `json:"data_source_schemas,omitempty"`
|
DataSourceSchemas map[string]interface{} `json:"data_source_schemas,omitempty"`
|
||||||
|
Functions map[string]interface{} `json:"functions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// testProvider returns a mock provider that is configured for basic
|
// testProvider returns a mock provider that is configured for basic
|
||||||
@ -155,5 +156,20 @@ func providersSchemaFixtureSchema() *providers.GetProviderSchemaResponse {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Functions: map[string]providers.FunctionSpec{
|
||||||
|
"test_func": {
|
||||||
|
Description: "a basic string function",
|
||||||
|
Return: cty.String,
|
||||||
|
Summary: "test",
|
||||||
|
Parameters: []providers.FunctionParameterSpec{{
|
||||||
|
Name: "input",
|
||||||
|
Type: cty.Number,
|
||||||
|
}},
|
||||||
|
VariadicParameter: &providers.FunctionParameterSpec{
|
||||||
|
Name: "variadic_input",
|
||||||
|
Type: cty.List(cty.Bool),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,29 @@
|
|||||||
"description_kind": "plain"
|
"description_kind": "plain"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"functions": {
|
||||||
|
"test_func": {
|
||||||
|
"description": "a basic string function",
|
||||||
|
"summary": "test",
|
||||||
|
"return_type": "string",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"description": "",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic_parameter": {
|
||||||
|
"name": "variadic_input",
|
||||||
|
"description": "",
|
||||||
|
"type": [
|
||||||
|
"list",
|
||||||
|
"bool"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -54,7 +54,29 @@
|
|||||||
"description_kind": "plain"
|
"description_kind": "plain"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"functions": {
|
||||||
|
"test_func": {
|
||||||
|
"description": "a basic string function",
|
||||||
|
"summary": "test",
|
||||||
|
"return_type": "string",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"description": "",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic_parameter": {
|
||||||
|
"name": "variadic_input",
|
||||||
|
"description": "",
|
||||||
|
"type": [
|
||||||
|
"list",
|
||||||
|
"bool"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user