From d4dbb10f474693130c52f7bb9e563deed4ca0776 Mon Sep 17 00:00:00 2001 From: Thomas Rooney Date: Thu, 15 Aug 2024 13:22:45 +0100 Subject: [PATCH] chore: stable schema registry (#51) * chore: stable schema registry * chore: multiple case * chore: refactor * chore: 6 characters at most --- workflow/source.go | 82 +++++++++++++++++++++++++---------------- workflow/source_test.go | 12 ++---- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/workflow/source.go b/workflow/source.go index 303f316..cb56aef 100644 --- a/workflow/source.go +++ b/workflow/source.go @@ -1,11 +1,11 @@ package workflow import ( + "crypto/sha256" "fmt" "math/rand" "os" "path/filepath" - "slices" "strings" "github.com/speakeasy-api/sdk-gen-config/workspace" @@ -129,41 +129,59 @@ func (s Source) Validate() error { } func (s Source) GetOutputLocation() (string, error) { - // If we have an output location, we can just return that - if s.Output != nil { - output := *s.Output + if s.Output != nil { + if len(s.Inputs) > 1 && !isYAMLFile(*s.Output) { + return "", fmt.Errorf("when merging multiple inputs, output must be a yaml file") + } + return *s.Output, nil + } + + if len(s.Inputs) == 1 && len(s.Overlays) == 0 { + return s.handleSingleInput() + } + + return s.generateOutputPath() +} - ext := filepath.Ext(output) - if len(s.Inputs) > 1 && !slices.Contains([]string{".yaml", ".yml"}, ext) { - return "", fmt.Errorf("when merging multiple inputs, output must be a yaml file") - } +func (s Source) handleSingleInput() (string, error) { + input := s.Inputs[0].Location + switch getFileStatus(input) { + case fileStatusLocal: + return input, nil + case fileStatusNotExists: + return "", fmt.Errorf("input file %s does not exist", input) + case fileStatusRemote, fileStatusRegistry: + return s.generateRegistryPath(input) + default: + return "", fmt.Errorf("unknown file status for %s", input) + } +} - return output, nil - } +func (s Source) generateRegistryPath(input string) (string, error) { + ext := filepath.Ext(input) + if ext == "" { + ext = ".yaml" + } + hash := fmt.Sprintf("%x", sha256.Sum256([]byte(input))) + return filepath.Join(GetTempDir(), fmt.Sprintf("registry_%s%s", hash[:6], ext)), nil +} - ext := ".yaml" - - // If we only have a single input, no overlays and its a local path, we can just use that - if len(s.Inputs) == 1 && len(s.Overlays) == 0 { - inputFile := s.Inputs[0].Location - - switch getFileStatus(inputFile) { - case fileStatusRegistry: - return filepath.Join(GetTempDir(), fmt.Sprintf("registry_%s", randStringBytes(10))), nil - case fileStatusLocal: - return inputFile, nil - case fileStatusNotExists: - return "", fmt.Errorf("input file %s does not exist", inputFile) - case fileStatusRemote: - ext = filepath.Ext(inputFile) - if ext == "" { - ext = ".yaml" - } - } - } +func (s Source) generateOutputPath() (string, error) { + hashInputs := func() string { + var combined string + for _, input := range s.Inputs { + combined += input.Location + } + hash := sha256.Sum256([]byte(combined)) + return fmt.Sprintf("%x", hash)[:6] + } + + return filepath.Join(GetTempDir(), fmt.Sprintf("output_%s.yaml", hashInputs())), nil +} - // Otherwise output will go to a temp file - return filepath.Join(GetTempDir(), fmt.Sprintf("output_%s%s", randStringBytes(10), ext)), nil +func isYAMLFile(path string) bool { + ext := filepath.Ext(path) + return ext == ".yaml" || ext == ".yml" } func GetTempDir() string { diff --git a/workflow/source_test.go b/workflow/source_test.go index daa07de..12f4c10 100644 --- a/workflow/source_test.go +++ b/workflow/source_test.go @@ -356,10 +356,6 @@ func TestSource_Validate(t *testing.T) { } func TestSource_GetOutputLocation(t *testing.T) { - workflow.SetRandStringBytesFunc(func(n int) string { - return "testrandomstring" - }) - type args struct { source workflow.Source } @@ -392,7 +388,7 @@ func TestSource_GetOutputLocation(t *testing.T) { }, }, }, - wantOutputLocation: ".speakeasy/temp/output_testrandomstring.json", + wantOutputLocation: ".speakeasy/temp/registry_e8ba45.json", }, { name: "simple remote source without extension returns auto-generated output location assumed to be yaml", @@ -405,7 +401,7 @@ func TestSource_GetOutputLocation(t *testing.T) { }, }, }, - wantOutputLocation: ".speakeasy/temp/output_testrandomstring.yaml", + wantOutputLocation: ".speakeasy/temp/registry_94359d.yaml", }, { name: "source with multiple inputs returns specified output location", @@ -438,7 +434,7 @@ func TestSource_GetOutputLocation(t *testing.T) { }, }, }, - wantOutputLocation: ".speakeasy/temp/output_testrandomstring.yaml", + wantOutputLocation: ".speakeasy/temp/output_6a0196.yaml", }, { name: "source with overlays returns specified output location", @@ -471,7 +467,7 @@ func TestSource_GetOutputLocation(t *testing.T) { }, }, }, - wantOutputLocation: ".speakeasy/temp/output_testrandomstring.yaml", + wantOutputLocation: ".speakeasy/temp/output_d910ba.yaml", }, } for _, tt := range tests {