2023-05-02 11:33:06 -04:00
// Copyright (c) HashiCorp, Inc.
2023-08-10 18:43:27 -04:00
// SPDX-License-Identifier: BUSL-1.1
2023-05-02 11:33:06 -04:00
2023-02-14 09:08:47 -05:00
package jsonfunction
import (
internal/command/jsonprovider: Remove root jsonfunction object from function output (#34536)
Previously, the `providers schema -json` output would include the root object from `metadata functions -json`. This object had its own `format_version` property, which would be confusing with the root `format_version` property already present.
This change still uses the `jsonfunction` package for consistency between cty and provider function JSON handling, but removes that extra object, instead making `functions` directly a mapping of names to signatures/definitions. This also adds a code comment to hint maintainers that jsonprovider format versioning is tied to jsonfunction format versioning.
Example output prior to change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"format_version": "1.0",
"function_signatures": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
}
```
Example output after change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
```
2024-01-18 15:48:07 -05:00
"encoding/json"
2023-02-14 09:08:47 -05:00
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
2023-12-21 16:48:19 -05:00
"github.com/hashicorp/terraform/internal/providers"
2023-02-14 09:08:47 -05:00
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
func TestMarshal ( t * testing . T ) {
tests := [ ] struct {
2023-12-21 16:48:19 -05:00
Name string
Functions map [ string ] function . Function
ProviderFunctions map [ string ] providers . FunctionDecl
Want string
WantErr string
2023-02-14 09:08:47 -05:00
} {
{
2023-12-21 16:48:19 -05:00
Name : "minimal function" ,
Functions : map [ string ] function . Function {
2023-02-14 09:08:47 -05:00
"fun" : function . New ( & function . Spec {
Type : function . StaticReturnType ( cty . Bool ) ,
} ) ,
} ,
2023-12-21 16:48:19 -05:00
ProviderFunctions : map [ string ] providers . FunctionDecl {
"fun" : {
ReturnType : cty . Bool ,
} ,
} ,
Want : ` { "format_version":"1.0","function_signatures": { "fun": { "return_type":"bool"}}} ` ,
2023-02-14 09:08:47 -05:00
} ,
{
2023-12-21 16:48:19 -05:00
Name : "function with description" ,
Functions : map [ string ] function . Function {
2023-02-14 09:08:47 -05:00
"fun" : function . New ( & function . Spec {
Description : "`timestamp` returns a UTC timestamp string." ,
Type : function . StaticReturnType ( cty . String ) ,
} ) ,
} ,
2023-12-21 16:48:19 -05:00
ProviderFunctions : map [ string ] providers . FunctionDecl {
"fun" : {
Description : "`timestamp` returns a UTC timestamp string." ,
ReturnType : cty . String ,
} ,
} ,
Want : "{\"format_version\":\"1.0\",\"function_signatures\":{\"fun\":{\"description\":\"`timestamp` returns a UTC timestamp string.\",\"return_type\":\"string\"}}}" ,
2023-02-14 09:08:47 -05:00
} ,
{
2023-12-21 16:48:19 -05:00
Name : "function with parameters" ,
Functions : map [ string ] function . Function {
2023-02-14 09:08:47 -05:00
"fun" : function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "timestamp" ,
Description : "timestamp text" ,
Type : cty . String ,
} ,
{
Name : "duration" ,
Description : "duration text" ,
Type : cty . String ,
} ,
} ,
Type : function . StaticReturnType ( cty . String ) ,
} ) ,
} ,
2023-12-21 16:48:19 -05:00
ProviderFunctions : map [ string ] providers . FunctionDecl {
"fun" : {
Parameters : [ ] providers . FunctionParam {
{
Name : "timestamp" ,
Description : "timestamp text" ,
Type : cty . String ,
} ,
{
Name : "duration" ,
Description : "duration text" ,
Type : cty . String ,
} ,
} ,
ReturnType : cty . String ,
} ,
} ,
Want : ` { "format_version":"1.0","function_signatures": { "fun": { "return_type":"string","parameters":[ { "name":"timestamp","description":"timestamp text","type":"string"}, { "name":"duration","description":"duration text","type":"string"}]}}} ` ,
2023-02-14 09:08:47 -05:00
} ,
{
2023-12-21 16:48:19 -05:00
Name : "function with variadic parameter" ,
Functions : map [ string ] function . Function {
2023-02-14 09:08:47 -05:00
"fun" : function . New ( & function . Spec {
VarParam : & function . Parameter {
Name : "default" ,
Description : "default description" ,
Type : cty . DynamicPseudoType ,
AllowUnknown : true ,
AllowDynamicType : true ,
AllowNull : true ,
AllowMarked : true ,
} ,
Type : function . StaticReturnType ( cty . DynamicPseudoType ) ,
} ) ,
} ,
2023-12-21 16:48:19 -05:00
ProviderFunctions : map [ string ] providers . FunctionDecl {
internal/command/jsonprovider: Remove root jsonfunction object from function output (#34536)
Previously, the `providers schema -json` output would include the root object from `metadata functions -json`. This object had its own `format_version` property, which would be confusing with the root `format_version` property already present.
This change still uses the `jsonfunction` package for consistency between cty and provider function JSON handling, but removes that extra object, instead making `functions` directly a mapping of names to signatures/definitions. This also adds a code comment to hint maintainers that jsonprovider format versioning is tied to jsonfunction format versioning.
Example output prior to change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"format_version": "1.0",
"function_signatures": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
}
```
Example output after change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
```
2024-01-18 15:48:07 -05:00
"fun" : {
2023-12-21 16:48:19 -05:00
VariadicParameter : & providers . FunctionParam {
Name : "default" ,
Description : "default description" ,
Type : cty . DynamicPseudoType ,
AllowUnknownValues : true ,
AllowNullValue : true ,
} ,
ReturnType : cty . DynamicPseudoType ,
} ,
} ,
Want : ` { "format_version":"1.0","function_signatures": { "fun": { "return_type":"dynamic","variadic_parameter": { "name":"default","description":"default description","is_nullable":true,"type":"dynamic"}}}} ` ,
2023-02-14 09:08:47 -05:00
} ,
{
2023-12-21 16:48:19 -05:00
Name : "function with list types" ,
Functions : map [ string ] function . Function {
2023-02-14 09:08:47 -05:00
"fun" : function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . List ( cty . String ) ,
} ,
} ,
Type : function . StaticReturnType ( cty . List ( cty . String ) ) ,
} ) ,
} ,
2023-12-21 16:48:19 -05:00
ProviderFunctions : map [ string ] providers . FunctionDecl {
"fun" : {
Parameters : [ ] providers . FunctionParam {
{
Name : "list" ,
Type : cty . List ( cty . String ) ,
} ,
} ,
ReturnType : cty . List ( cty . String ) ,
} ,
} ,
Want : ` { "format_version":"1.0","function_signatures": { "fun": { "return_type":["list","string"],"parameters":[ { "name":"list","type":["list","string"]}]}}} ` ,
2023-02-14 09:08:47 -05:00
} ,
{
2023-12-21 16:48:19 -05:00
Name : "returns diagnostics on failure" ,
Functions : map [ string ] function . Function {
2023-02-14 09:08:47 -05:00
"fun" : function . New ( & function . Spec {
Params : [ ] function . Parameter { } ,
Type : func ( args [ ] cty . Value ) ( ret cty . Type , err error ) {
return cty . DynamicPseudoType , fmt . Errorf ( "error" )
} ,
} ) ,
} ,
2023-12-21 16:48:19 -05:00
WantErr : "Failed to serialize function \"fun\": error" ,
2023-02-14 09:08:47 -05:00
} ,
}
for i , test := range tests {
t . Run ( fmt . Sprintf ( "%d-%s" , i , test . Name ) , func ( t * testing . T ) {
2023-12-21 16:48:19 -05:00
got , diags := Marshal ( test . Functions )
2023-02-14 09:08:47 -05:00
if test . WantErr != "" {
if ! diags . HasErrors ( ) {
t . Fatal ( "expected error, got none" )
}
if diags . Err ( ) . Error ( ) != test . WantErr {
t . Fatalf ( "expected error %q, got %q" , test . WantErr , diags . Err ( ) )
}
} else {
if diags . HasErrors ( ) {
t . Fatal ( diags )
}
2023-12-21 16:48:19 -05:00
if diff := cmp . Diff ( test . Want , string ( got ) ) ; diff != "" {
t . Fatalf ( "mismatch of function signature: %s" , diff )
}
}
if test . ProviderFunctions != nil {
internal/command/jsonprovider: Remove root jsonfunction object from function output (#34536)
Previously, the `providers schema -json` output would include the root object from `metadata functions -json`. This object had its own `format_version` property, which would be confusing with the root `format_version` property already present.
This change still uses the `jsonfunction` package for consistency between cty and provider function JSON handling, but removes that extra object, instead making `functions` directly a mapping of names to signatures/definitions. This also adds a code comment to hint maintainers that jsonprovider format versioning is tied to jsonfunction format versioning.
Example output prior to change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"format_version": "1.0",
"function_signatures": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
}
```
Example output after change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
```
2024-01-18 15:48:07 -05:00
// Provider functions should marshal identically to cty
// functions, without the wrapping object.
got := MarshalProviderFunctions ( test . ProviderFunctions )
gotBytes , err := json . Marshal ( got )
2023-12-21 16:48:19 -05:00
if err != nil {
// these should never error
internal/command/jsonprovider: Remove root jsonfunction object from function output (#34536)
Previously, the `providers schema -json` output would include the root object from `metadata functions -json`. This object had its own `format_version` property, which would be confusing with the root `format_version` property already present.
This change still uses the `jsonfunction` package for consistency between cty and provider function JSON handling, but removes that extra object, instead making `functions` directly a mapping of names to signatures/definitions. This also adds a code comment to hint maintainers that jsonprovider format versioning is tied to jsonfunction format versioning.
Example output prior to change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"format_version": "1.0",
"function_signatures": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
}
```
Example output after change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
```
2024-01-18 15:48:07 -05:00
t . Fatal ( "Marshal of ProviderFunctions failed:" , err )
2023-12-21 16:48:19 -05:00
}
internal/command/jsonprovider: Remove root jsonfunction object from function output (#34536)
Previously, the `providers schema -json` output would include the root object from `metadata functions -json`. This object had its own `format_version` property, which would be confusing with the root `format_version` property already present.
This change still uses the `jsonfunction` package for consistency between cty and provider function JSON handling, but removes that extra object, instead making `functions` directly a mapping of names to signatures/definitions. This also adds a code comment to hint maintainers that jsonprovider format versioning is tied to jsonfunction format versioning.
Example output prior to change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"format_version": "1.0",
"function_signatures": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
}
```
Example output after change:
```jsonc
{
"format_version": "1.0",
"provider_schemas": {
"registry.terraform.io/bflad/framework": {
// ...
"functions": {
"example": {
"description": "Echoes given argument as result",
"summary": "Example function",
"return_type": "string",
"parameters": [
{
"name": "input",
"description": "String to echo",
"type": "string"
}
]
}
}
}
}
}
```
2024-01-18 15:48:07 -05:00
var want functions
err = json . Unmarshal ( [ ] byte ( test . Want ) , & want )
if err != nil {
// these should never error
t . Fatal ( "Unmarshal of Want failed:" , err )
}
wantBytes , err := json . Marshal ( want . Signatures )
if err != nil {
// these should never error
t . Fatal ( "Marshal of Want.Signatures failed:" , err )
}
if diff := cmp . Diff ( string ( wantBytes ) , string ( gotBytes ) ) ; diff != "" {
2023-02-14 09:08:47 -05:00
t . Fatalf ( "mismatch of function signature: %s" , diff )
}
}
} )
}
}