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 {
|
||||
Provider *Schema `json:"provider,omitempty"`
|
||||
ResourceSchemas map[string]*Schema `json:"resource_schemas,omitempty"`
|
||||
DataSourceSchemas map[string]*Schema `json:"data_source_schemas,omitempty"`
|
||||
Provider *Schema `json:"provider,omitempty"`
|
||||
ResourceSchemas map[string]*Schema `json:"resource_schemas,omitempty"`
|
||||
DataSourceSchemas map[string]*Schema `json:"data_source_schemas,omitempty"`
|
||||
Functions map[string]*Function `json:"functions,omitempty"`
|
||||
}
|
||||
|
||||
func newProviders() *Providers {
|
||||
@ -61,5 +62,6 @@ func marshalProvider(tps providers.ProviderSchema) *Provider {
|
||||
Provider: marshalSchema(tps.Provider),
|
||||
ResourceSchemas: marshalSchemas(tps.ResourceTypes),
|
||||
DataSourceSchemas: marshalSchemas(tps.DataSources),
|
||||
Functions: marshalFunctions(tps.Functions),
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ func TestMarshalProvider(t *testing.T) {
|
||||
Provider: &Schema{},
|
||||
ResourceSchemas: 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"`
|
||||
ResourceSchemas map[string]interface{} `json:"resource_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
|
||||
@ -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,6 +54,28 @@
|
||||
"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,6 +54,28 @@
|
||||
"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