Skip to content

Commit

Permalink
Resolve references
Browse files Browse the repository at this point in the history
  • Loading branch information
stefannegele committed Oct 2, 2023
1 parent ff829de commit d14490d
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 4 deletions.
61 changes: 58 additions & 3 deletions dataContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import (
"gopkg.in/yaml.v3"
"io"
"net/http"
"net/url"
"os"
"strings"
)

const referencePrefix = "$ref:"

type DataContract = map[string]interface{}

func ReadLocalDataContract(dataContractFileName string) (dataContractFile []byte, err error) {
Expand All @@ -34,7 +38,7 @@ func GetValue(contract DataContract, path []string) (value interface{}, err erro
}

if len(path) == 1 {
return contract[fieldName], nil
return resolveValue(contract, fieldName)
}

next, ok := contract[fieldName].(map[string]interface{})
Expand All @@ -45,6 +49,57 @@ func GetValue(contract DataContract, path []string) (value interface{}, err erro
return GetValue(next, path[1:])
}

func resolveValue(object map[string]interface{}, fieldName string) (value interface{}, err error) {
value = object[fieldName]

if stringValue, isString := value.(string); isString && strings.HasPrefix(stringValue, referencePrefix) {
reference := strings.Trim(strings.TrimPrefix(stringValue, referencePrefix), " ")

value, err = resolveReference(reference)
if err != nil {
return nil, err
}
}

return value, nil
}

func resolveReference(reference string) (_ string, err error) {
var bytes []byte

if isURI(reference) {
bytes, err = resolveReferenceFromRemote(reference)
} else {
bytes, err = resolveReferenceLocally(reference)
}

if err != nil {
return "", fmt.Errorf("can't resolve reference '%v': %w", reference, err)
}

return string(bytes), nil
}

func resolveReferenceLocally(reference string) ([]byte, error) {
return os.ReadFile(reference)
}

func resolveReferenceFromRemote(reference string) ([]byte, error) {
response, err := http.Get(reference)
defer response.Body.Close()

if err != nil {
return nil, err
}

return io.ReadAll(response.Body)
}

func isURI(reference string) bool {
_, err := url.ParseRequestURI(reference)
return err == nil
}

func FetchDataContract(url string) (result []byte, err error) {
response, err := http.Get(url)
if err != nil {
Expand All @@ -53,9 +108,9 @@ func FetchDataContract(url string) (result []byte, err error) {

defer response.Body.Close()

if otherContractData, err := io.ReadAll(response.Body); err != nil {
if contractData, err := io.ReadAll(response.Body); err != nil {
return nil, fmt.Errorf("failed to read data contract to compare with: %w", err)
} else {
return otherContractData, nil
return contractData, nil
}
}
24 changes: 24 additions & 0 deletions dataContract_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package main

import (
"fmt"
"os"
"reflect"
"testing"
)

func TestGetValue(t *testing.T) {
model, _ := os.ReadFile("./test_resources/model.yaml")

type args struct {
contract DataContract
path []string
Expand Down Expand Up @@ -45,6 +49,26 @@ func TestGetValue(t *testing.T) {
path: []string{"schema", "type"}},
wantErr: true,
},
{
name: "local reference",
args: args{
contract: DataContract{"schema": map[string]interface{}{
"specification": "$ref: test_resources/model.yaml",
}},
path: []string{"schema", "specification"}},
wantValue: string(model),
wantErr: false,
},
{
name: "remote reference",
args: args{
contract: DataContract{"schema": map[string]interface{}{
"specification": fmt.Sprintf("$ref: %v/model.yaml", TestResourcesServer.URL),
}},
path: []string{"schema", "specification"}},
wantValue: string(model),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package main

import (
"net/http"
"net/http/httptest"
"testing"
)

func TestMainMethod(t *testing.T) {
var TestResourcesServer = httptest.NewServer(http.FileServer(http.Dir("./test_resources")))

func TestMain(m *testing.M) {
defer TestResourcesServer.Close()

m.Run()
}
10 changes: 10 additions & 0 deletions test_resources/model.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
models:
- name: my_table
description: "contains data"
config:
materialized: table
columns:
- name: my_column
data_type: text
description: "contains values"

0 comments on commit d14490d

Please sign in to comment.