Compare commits

...

4 Commits

Author SHA1 Message Date
shreyas-goenka fc6ec1b0fd
Merge 701b1786a8 into 0753dfe2f4 2024-10-15 20:03:42 +00:00
Shreyas Goenka 701b1786a8
fmt 2024-10-15 22:03:29 +02:00
Andrew Nester 0753dfe2f4
Fixed unmarshalling json input into `interface{}` type (#1832)
## Changes
Fixed unmarshalling json input into `interface{}` type

Commands like `api post` support free form request input, so it should
be unmarshaled correctly

## Tests
Added regression test + E2E test pass
2024-10-15 12:10:02 +00:00
shreyas-goenka ab20624206
Assert SDK version is consistent in the CLI generation process (#1814)
## Changes
Followup from
https://github.com/databricks/cli/pull/1809#discussion_r1790045086. User
will see the following error if the SDK version does not match during
CLI code generation.

```
--- FAIL: TestConsistentDatabricksSdkVersion (1.34s)
    info_test.go:53: 
                Error Trace:    /Users/shreyas.goenka/cli/internal/build/info_test.go:53
                Error:          Not equal: 
                                expected: "0c86ea6dbd9a730c24ff0d4e509603e476955ac5"
                                actual  : "6f6b1371e640f2dfeba72d365ac566368656f6b6"
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -0c86ea6dbd9a730c24ff0d4e509603e476955ac5
                                +6f6b1371e640f2dfeba72d365ac566368656f6b6
                Test:           TestConsistentDatabricksSdkVersion
                Messages:       please update the SDK version before generating the CLI
```

## Tests
Manually asserted that:
1. Generating the CLI without the SDK bump causes an error.
2. Generating the CLI after the SDK bump works as expected.
3. The test works even when the SDK is pinned to a specific commit like
`v0.47.1-0.20241002195128-6cecc224cbf7`
2024-10-14 16:19:48 +00:00
5 changed files with 99 additions and 15 deletions

View File

@ -11,6 +11,7 @@
"toolchain": {
"required": ["go"],
"post_generate": [
"go test -timeout 240s -run TestConsistentDatabricksSdkVersion github.com/databricks/cli/internal/build",
"go run ./bundle/internal/schema/*.go ./bundle/schema/jsonschema.json",
"echo 'bundle/internal/tf/schema/\\*.go linguist-generated=true' >> ./.gitattributes",
"echo 'go.sum linguist-generated=true' >> ./.gitattributes",

View File

@ -148,7 +148,7 @@ func TestApplyPresetsPrefixForUcVolumes(t *testing.T) {
prefix: "[prefix]",
volume: &resources.Volume{
CreateVolumeRequestContent: &catalog.CreateVolumeRequestContent{
Name: "volume1",
Name: "volume1",
CatalogName: "catalog1",
SchemaName: "schema1",
},

View File

@ -0,0 +1,73 @@
package build
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/mod/modfile"
)
// This test ensures that the OpenAPI SHA the CLI is being generated from matches
// the OpenAPI SHA of the Go SDK version used in the CLI. We should always upgrade
// the Go SDK version before generating the CLI because downstream generated assets
// like the bundle schema depend on the Go SDK itself.
func TestConsistentDatabricksSdkVersion(t *testing.T) {
// Read the go.mod file
b, err := os.ReadFile("../../go.mod")
require.NoError(t, err)
// Parse the go.mod file to get the databricks-sdk version
modFile, err := modfile.Parse("../../go.mod", b, nil)
require.NoError(t, err)
modulePath := "github.com/databricks/databricks-sdk-go"
var version string
for _, r := range modFile.Require {
if r.Mod.Path == modulePath {
version = r.Mod.Version
}
}
require.NotEmpty(t, version)
// Full path of the package. For example: github.com/databricks/databricks-sdk-go@v0.47.1-0.20241002195128-6cecc224cbf7
fullPath := fmt.Sprintf("%s@%s", modulePath, version)
type goListResponse struct {
Origin struct {
Hash string
}
}
// Using the go CLI query for the git hash corresponding to the databricks-sdk-go version
cmd := exec.Command("go", "list", "-m", "-json", "-mod=readonly", fullPath)
out, err := cmd.Output()
require.NoError(t, err)
parsedOutput := new(goListResponse)
err = json.Unmarshal(out, parsedOutput)
require.NoError(t, err)
hash := parsedOutput.Origin.Hash
require.NotEmpty(t, hash)
// Read the OpenAPI SHA from the Go SDK.
url := fmt.Sprintf("https://raw.githubusercontent.com/databricks/databricks-sdk-go/%s/.codegen/_openapi_sha", hash)
resp, err := http.Get(url)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
sdkSha, err := io.ReadAll(resp.Body)
require.NoError(t, err)
cliSha, err := os.ReadFile("../../.codegen/_openapi_sha")
require.NoError(t, err)
assert.Equal(t, strings.TrimSpace(string(cliSha)), strings.TrimSpace(string(sdkSha)), "please update the SDK version before generating the CLI")
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"reflect"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/dyn/convert"
@ -63,11 +64,24 @@ func (j *JsonFlag) Unmarshal(v any) diag.Diagnostics {
return diags.Extend(diag.FromErr(err))
}
// Finally unmarshal the normalized data to the output.
// It will fill in the ForceSendFields field if the struct contains it.
err = marshal.Unmarshal(data, v)
if err != nil {
return diags.Extend(diag.FromErr(err))
kind := reflect.ValueOf(v).Kind()
if kind == reflect.Ptr {
kind = reflect.ValueOf(v).Elem().Kind()
}
if kind == reflect.Struct {
// Finally unmarshal the normalized data to the output.
// It will fill in the ForceSendFields field if the struct contains it.
err = marshal.Unmarshal(data, v)
if err != nil {
return diags.Extend(diag.FromErr(err))
}
} else {
// If the output is not a struct, just unmarshal the data to the output.
err = json.Unmarshal(data, v)
if err != nil {
return diags.Extend(diag.FromErr(err))
}
}
return diags

View File

@ -13,10 +13,6 @@ import (
"github.com/stretchr/testify/require"
)
type requestType struct {
Foo string `json:"foo"`
}
func TestJsonFlagEmpty(t *testing.T) {
var body JsonFlag
@ -35,13 +31,13 @@ func TestJsonFlagInline(t *testing.T) {
err := body.Set(`{"foo": "bar"}`)
assert.NoError(t, err)
var request requestType
var request any
diags := body.Unmarshal(&request)
assert.NoError(t, diags.Error())
assert.Empty(t, diags)
assert.Equal(t, "JSON (14 bytes)", body.String())
assert.Equal(t, requestType{"bar"}, request)
assert.Equal(t, map[string]any{"foo": "bar"}, request)
}
func TestJsonFlagError(t *testing.T) {
@ -50,7 +46,7 @@ func TestJsonFlagError(t *testing.T) {
err := body.Set(`{"foo":`)
assert.NoError(t, err)
var request requestType
var request any
diags := body.Unmarshal(&request)
assert.EqualError(t, diags.Error(), "error decoding JSON at (inline):1:8: unexpected end of JSON input")
assert.Equal(t, "JSON (7 bytes)", body.String())
@ -58,7 +54,7 @@ func TestJsonFlagError(t *testing.T) {
func TestJsonFlagFile(t *testing.T) {
var body JsonFlag
var request requestType
var request any
var fpath string
var payload = []byte(`{"foo": "bar"}`)
@ -78,7 +74,7 @@ func TestJsonFlagFile(t *testing.T) {
assert.NoError(t, diags.Error())
assert.Empty(t, diags)
assert.Equal(t, requestType{"bar"}, request)
assert.Equal(t, map[string]any{"foo": "bar"}, request)
}
const jsonData = `