From 0185e20535dcfb0ae8fed9ea0e3210d3a88555b5 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Thu, 16 Oct 2025 13:37:54 -0700 Subject: [PATCH 01/12] fix url --- go.mod | 2 +- go.sum | 4 ++-- metadata/directory.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index f997b96..c24a6c9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/bmeg/jsonschemagraph v0.0.4-0.20251015150525-9ed100499f63 github.com/bytedance/sonic v1.14.0 github.com/calypr/data-client v0.0.0-20251009221450-92246d753b31 - github.com/calypr/git-drs v0.0.0-20251014201703-c83676369df6 + github.com/calypr/git-drs v0.0.0-20251016202529-49a38c0d4a28 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff diff --git a/go.sum b/go.sum index 0aa9ec4..1fa4043 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZw github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/calypr/data-client v0.0.0-20251009221450-92246d753b31 h1:jF+g0snsghxCnf07YEPo7Id4Bzy0zAxtM9wLbQJnRp0= github.com/calypr/data-client v0.0.0-20251009221450-92246d753b31/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= -github.com/calypr/git-drs v0.0.0-20251014201703-c83676369df6 h1:SNk4kNOqfbpRc63PPV56aPi1/zZ0SHAEO6+QUBLYOU4= -github.com/calypr/git-drs v0.0.0-20251014201703-c83676369df6/go.mod h1:etacj0mtHlt+tYYu5j3yy2In8/XPKBi0CVILUsSJ1PA= +github.com/calypr/git-drs v0.0.0-20251016202529-49a38c0d4a28 h1:PFSUVjVTXbTZ+pFerMJdC7F8aRhqOIgrgb5gs+05RNY= +github.com/calypr/git-drs v0.0.0-20251016202529-49a38c0d4a28/go.mod h1:etacj0mtHlt+tYYu5j3yy2In8/XPKBi0CVILUsSJ1PA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= diff --git a/metadata/directory.go b/metadata/directory.go index b2593ff..dade860 100644 --- a/metadata/directory.go +++ b/metadata/directory.go @@ -136,7 +136,7 @@ func BuildDirectoryTreeFromDocRef(docRef *drpb.DocumentReference) { return } - rawURL := docRef.Content[0].GetAttachment().GetUrl().GetValue() + rawURL := docRef.Content[0].GetAttachment().GetTitle().GetValue() u, err := url.Parse(rawURL) if err != nil { log.Printf("Error parsing URL %s for DocRef %s: %v\n", rawURL, docRef.GetId().GetValue(), err) From b1226e1f89a98e96f1f65842e9290e719f69ef62 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Thu, 16 Oct 2025 15:24:14 -0700 Subject: [PATCH 02/12] adds empty command. Backwards compatible with existing etl job --- client/sower/sower.go | 2 +- cmd/cmd.go | 2 ++ cmd/empty/main.go | 20 ++++++++++++++++++++ publish/publish.go | 28 ++++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 cmd/empty/main.go diff --git a/client/sower/sower.go b/client/sower/sower.go index 1d8e3b1..c5ad155 100644 --- a/client/sower/sower.go +++ b/client/sower/sower.go @@ -66,7 +66,7 @@ func (sc *SowerClient) DispatchJob(name string, args *DispatchArgs) (*DispatchRe if resp.Err != nil { return nil, resp.Err } - fmt.Println("RESP: ", string(resp.Body)) + fmt.Println("Response: ", string(resp.Body)) return nil, nil } diff --git a/cmd/cmd.go b/cmd/cmd.go index 0e55e77..7a30273 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,6 +1,7 @@ package cmd import ( + "github.com/calypr/forge/cmd/empty" "github.com/calypr/forge/cmd/initialize" "github.com/calypr/forge/cmd/meta" "github.com/calypr/forge/cmd/ping" @@ -23,6 +24,7 @@ func init() { RootCmd.AddCommand(validate.ValidateCmd) RootCmd.AddCommand(validate.CheckEdgeCmd) RootCmd.AddCommand(publish.PublishCmd) + RootCmd.AddCommand(empty.EmptyCmd) // Don't show the help menu for that command every time there is an error RootCmd.SilenceUsage = true diff --git a/cmd/empty/main.go b/cmd/empty/main.go new file mode 100644 index 0000000..d64d7fb --- /dev/null +++ b/cmd/empty/main.go @@ -0,0 +1,20 @@ +package empty + +import ( + "github.com/calypr/forge/publish" + "github.com/spf13/cobra" +) + +var EmptyCmd = &cobra.Command{ + Use: "empty ", + Short: "empty metadata for a project", + Long: `The 'empty' command is how metadata is removed in calypr.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + err := publish.RunEmpty(args[0]) + if err != nil { + return err + } + return nil + }, +} diff --git a/publish/publish.go b/publish/publish.go index 651d2c2..24bee75 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -13,6 +13,28 @@ import ( const FHIR_JOB_NAME = "fhir_import_export" const SOURCE_GH_USER_ENDPOINT = "https://source.ohsu.edu/api/v3/user" const POD_PUT_METHOD = "put" +const POD_DELETE_METHOD = "delete" + +func RunEmpty(projectId string) error { + sc, err := sower.NewSowerClient() + if err != nil { + return err + } + dispatchArgs := &sower.DispatchArgs{ + ProjectId: sc.ProjectId, + APIEndpoint: sc.Cred.APIEndpoint, + Profile: sc.Cred.Profile, + Method: POD_DELETE_METHOD, + } + resp, err := sc.DispatchJob( + FHIR_JOB_NAME, + dispatchArgs, + ) + if err != nil || resp != nil { + return fmt.Errorf("failed to dispatch job: %v: %w", resp, err) + } + return nil +} func RunPublish(token string) error { err := checkGHPAccessToken(token) @@ -68,11 +90,9 @@ func RunPublish(token string) error { FHIR_JOB_NAME, dispatchArgs, ) - if err != nil { - return fmt.Errorf("failed to dispatch job: %w", err) + if err != nil || resp != nil { + return fmt.Errorf("failed to dispatch job: %v: %w", resp, err) } - fmt.Println("Sower Dispatch Response: ", resp) - return nil } From eab4a8db338ebd379f0ee1291af81d31acb14e60 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Thu, 30 Oct 2025 14:48:57 -0700 Subject: [PATCH 03/12] fix up research study gen --- metadata/meta.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/metadata/meta.go b/metadata/meta.go index eb155f5..15278f4 100644 --- a/metadata/meta.go +++ b/metadata/meta.go @@ -2,6 +2,7 @@ package metadata import ( "bufio" + "bytes" "encoding/json" "fmt" "log" @@ -145,16 +146,23 @@ func getResearchStudy(fhirDirectory string, projectId string, endpoint string, m return "", fmt.Errorf("failed to read existing ResearchStudy file: %v", err) } + lines := bytes.Split(jsonBytes, []byte{'\n'}) + unmarshalBytes := jsonBytes + // If there is more than 1 line in the file, use the first line research study + if len(lines) > 0 && len(lines[0]) > 0 { + unmarshalBytes = lines[0] + } + // Try to unmarshal existing bytes into FHIR resource to get the ID (use unmarshaller) - cr, err := unmarshaller.UnmarshalR5(jsonBytes) + cr, err := unmarshaller.UnmarshalR5(unmarshalBytes) if err != nil { // If the protobuf unmarshaller fails, attempt to decode the plain JSON to find "id" var tmp map[string]any - if err2 := json.Unmarshal(jsonBytes, &tmp); err2 == nil { + if err2 := json.Unmarshal(unmarshalBytes, &tmp); err2 == nil { if idv, ok := tmp["id"].(string); ok && idv != "" { // we have an ID but couldn't unmarshal via fhir unmarshaller; still inject rootDir rootDir := getOrCreateRootDirectory() - newBytes, injErr := injectRootDir(jsonBytes, "Directory/"+rootDir.Id) + newBytes, injErr := injectRootDir(unmarshalBytes, "Directory/"+rootDir.Id) if injErr != nil { return "", injErr } @@ -172,11 +180,15 @@ func getResearchStudy(fhirDirectory string, projectId string, endpoint string, m // inject or update rootDir in the existing JSON and write back rootDir := getOrCreateRootDirectory() - newBytes, err := injectRootDir(jsonBytes, "Directory/"+rootDir.Id) + newFirstLineBytes, err := injectRootDir(unmarshalBytes, "Directory/"+rootDir.Id) if err != nil { return "", fmt.Errorf("failed to inject rootDir into existing ResearchStudy: %v", err) } - if err := os.WriteFile(rsPath, append(newBytes, '\n'), 0644); err != nil { + + lines[0] = newFirstLineBytes + contentToWrite := bytes.Join(lines, []byte{'\n'}) + + if err := os.WriteFile(rsPath, contentToWrite, 0644); err != nil { return "", fmt.Errorf("error writing updated ResearchStudy file %s: %v", rsPath, err) } fmt.Printf("Updated ResearchStudy at %s with rootDir field\n", rsPath) From 44befa4cedfc0cd42cb4b704bb814aabd40afabf Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Mon, 3 Nov 2025 08:05:07 -0800 Subject: [PATCH 04/12] bump data client version --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c24a6c9..74d38d3 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/bmeg/jsonschema/v6 v6.0.4 github.com/bmeg/jsonschemagraph v0.0.4-0.20251015150525-9ed100499f63 github.com/bytedance/sonic v1.14.0 - github.com/calypr/data-client v0.0.0-20251009221450-92246d753b31 - github.com/calypr/git-drs v0.0.0-20251016202529-49a38c0d4a28 + github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 + github.com/calypr/git-drs v0.0.0-20251103160406-847061641992 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff diff --git a/go.sum b/go.sum index 1fa4043..b9535e2 100644 --- a/go.sum +++ b/go.sum @@ -31,10 +31,10 @@ github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxl github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/calypr/data-client v0.0.0-20251009221450-92246d753b31 h1:jF+g0snsghxCnf07YEPo7Id4Bzy0zAxtM9wLbQJnRp0= -github.com/calypr/data-client v0.0.0-20251009221450-92246d753b31/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= -github.com/calypr/git-drs v0.0.0-20251016202529-49a38c0d4a28 h1:PFSUVjVTXbTZ+pFerMJdC7F8aRhqOIgrgb5gs+05RNY= -github.com/calypr/git-drs v0.0.0-20251016202529-49a38c0d4a28/go.mod h1:etacj0mtHlt+tYYu5j3yy2In8/XPKBi0CVILUsSJ1PA= +github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 h1:NVO5FpugLr+6rhTS/er1B2Ui8RIykbFL+IJcOXDdqBo= +github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= +github.com/calypr/git-drs v0.0.0-20251103160406-847061641992 h1:z/EIrWehDHLWNCcVYLth6w3Hvppi8xWMKkzgtmjqFfs= +github.com/calypr/git-drs v0.0.0-20251103160406-847061641992/go.mod h1:qEwYFbRiLKwblNBPFRWtLkGU4aqTp6r2Dwi2Oe7IN4Y= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= From 5f8d00979315161b139c17076fcc413d37abb618 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Mon, 10 Nov 2025 13:43:54 -0800 Subject: [PATCH 05/12] add explorer config validation --- cmd/cmd.go | 11 ++++++---- cmd/validate/main.go | 51 ++++++++++++++++++++++++++++++++++++++------ go.mod | 9 ++++---- go.sum | 24 ++++++++++----------- 4 files changed, 68 insertions(+), 27 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 7a30273..0fe58ca 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -21,11 +21,14 @@ func init() { RootCmd.AddCommand(initialize.InitCmd) RootCmd.AddCommand(ping.PingCmd) RootCmd.AddCommand(meta.MetaCmd) - RootCmd.AddCommand(validate.ValidateCmd) - RootCmd.AddCommand(validate.CheckEdgeCmd) RootCmd.AddCommand(publish.PublishCmd) RootCmd.AddCommand(empty.EmptyCmd) - // Don't show the help menu for that command every time there is an error - RootCmd.SilenceUsage = true + validate.ValidateParentCmd.AddCommand(validate.ValidateConfigCmd) + validate.ValidateParentCmd.AddCommand(validate.ValidateDataCmd) + validate.ValidateParentCmd.AddCommand(validate.ValidateEdgeCmd) + + RootCmd.AddCommand(validate.ValidateParentCmd) + + RootCmd.SilenceUsage = true // Hide usage on error } diff --git a/cmd/validate/main.go b/cmd/validate/main.go index 84f2cf3..e7c3376 100644 --- a/cmd/validate/main.go +++ b/cmd/validate/main.go @@ -11,6 +11,7 @@ import ( "github.com/bmeg/golib" "github.com/bmeg/grip/gripql" "github.com/bytedance/sonic" + "github.com/calypr/gecko/gecko/config" "github.com/cockroachdb/errors" "github.com/calypr/forge/metadata" @@ -21,16 +22,19 @@ import ( // Holds the value of the --out-dir flag var outputDir string +var ValidateParentCmd = &cobra.Command{ + Use: "validate", + Short: "Contains subcommands for validating config, data, and edges", +} func init() { - // Add the --out-dir flag to the check-edge command - CheckEdgeCmd.Flags().StringVarP(&outputDir, "out-dir", "o", "", "Directory to save vertices and edges files") + ValidateEdgeCmd.Flags().StringVarP(&outputDir, "out-dir", "o", "", "Directory to save vertices and edges files") } // ValidateCmd remains unchanged -var ValidateCmd = &cobra.Command{ - Use: "validate ", - Short: "validate data files given a jsonschema and a ndjson data target file or directory", +var ValidateDataCmd = &cobra.Command{ + Use: "data ", + Short: "data data files given a jsonschema and a ndjson data target file or directory", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { path := args[0] @@ -104,8 +108,8 @@ var ValidateCmd = &cobra.Command{ }, } -var CheckEdgeCmd = &cobra.Command{ - Use: "check-edge ", +var ValidateEdgeCmd = &cobra.Command{ + Use: "edge ", Short: "Check for orphaned edges in graph data from FHIR .ndjson files", Long: "Generates graph elements from FHIR .ndjson files and checks for edges referencing non-existent vertices", Args: cobra.ExactArgs(1), @@ -346,3 +350,36 @@ var CheckEdgeCmd = &cobra.Command{ return allErrors.ErrorOrNil() }, } + +var ValidateConfigCmd = &cobra.Command{ + Use: "config ", + Short: "config explorer config file", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + path := args[0] + + info, err := os.Stat(path) + if err != nil { + return errors.Wrapf(err, "could not get file info for %s", path) + } + if info.IsDir() { + return fmt.Errorf("%s is a directory, expected a file", path) + } + bytes, err := os.ReadFile(path) + if err != nil { + return errors.Wrapf(err, "could not read file for %s", path) + } + var explorerConf config.Config + err = json.Unmarshal(bytes, &explorerConf) + if err != nil { + return errors.Wrapf(err, "could not unmarshal file into explorerConfig for %s", path) + } + + if explorerConf.ExplorerConfig != nil && len(explorerConf.ExplorerConfig) == 0 { + return fmt.Errorf("No explorer tabs are defined for explorer config: %s", path) + } + + fmt.Printf("%s is a valid explorer config\n", path) // Added \n for cleaner output + return nil + }, +} diff --git a/go.mod b/go.mod index 74d38d3..b7ba81d 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,13 @@ go 1.24.2 require ( github.com/bmeg/golib v0.0.0-20200725232156-e799a31439fc - github.com/bmeg/grip v0.0.0-20250915204302-93cb1e8117c8 - github.com/bmeg/grip-graphql v0.0.0-20250924224746-dc7f74b4040f + github.com/bmeg/grip v0.0.0-20251106174949-7f0784126fbb + github.com/bmeg/grip-graphql v0.0.0-20251106183540-8b2f286248b3 github.com/bmeg/jsonschema/v6 v6.0.4 - github.com/bmeg/jsonschemagraph v0.0.4-0.20251015150525-9ed100499f63 + github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c github.com/bytedance/sonic v1.14.0 github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 + github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 github.com/calypr/git-drs v0.0.0-20251103160406-847061641992 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 @@ -33,6 +34,7 @@ require ( github.com/cockroachdb/redact v1.1.5 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/sentry-go v0.28.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect @@ -60,7 +62,6 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect diff --git a/go.sum b/go.sum index b9535e2..1d5b39e 100644 --- a/go.sum +++ b/go.sum @@ -18,14 +18,14 @@ github.com/bazelbuild/rules_go v0.24.5/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bmeg/golib v0.0.0-20200725232156-e799a31439fc h1:/0v/ZcXYjGs44InmjECrls31onIbVKVu1Q/E2cmnCEU= github.com/bmeg/golib v0.0.0-20200725232156-e799a31439fc/go.mod h1:hoSeuZtqe58ANXHuWpeODx4bDHGV36QXlCs1yrUvK6M= -github.com/bmeg/grip v0.0.0-20250915204302-93cb1e8117c8 h1:UhgcpVbQuvqg8KJ0ioB4i/KaQw1Zel34oHKjSENPEIA= -github.com/bmeg/grip v0.0.0-20250915204302-93cb1e8117c8/go.mod h1:mIN98ALRqqjqRHqopIuZ9iDJ97w4gAzXqdKE6Enuu7Y= -github.com/bmeg/grip-graphql v0.0.0-20250924224746-dc7f74b4040f h1:GQxRuotthPMMnds1EpYWlveOShUWuCEKhO3tc1KLYPI= -github.com/bmeg/grip-graphql v0.0.0-20250924224746-dc7f74b4040f/go.mod h1:mbCMMkG3xa2/mhs1pbKWoCb95STPri/bqCJtTn2oIKw= +github.com/bmeg/grip v0.0.0-20251106174949-7f0784126fbb h1:GYQ0Tfj36h8m+6dZolHDQJyVnjjqT3pgBZlFGHT+HOE= +github.com/bmeg/grip v0.0.0-20251106174949-7f0784126fbb/go.mod h1:BxpaUuXbymKkEPvSDslziCzU17akkBo1ubu9nAFsI1A= +github.com/bmeg/grip-graphql v0.0.0-20251106183540-8b2f286248b3 h1:rWKYGUcCdStTQxCjKZU1e3/0PioP12WQPchsRZFSe5M= +github.com/bmeg/grip-graphql v0.0.0-20251106183540-8b2f286248b3/go.mod h1:YcZY4w597zXAzi5iA9A48KIRGpnSSCb66ZFyN88LRKA= github.com/bmeg/jsonschema/v6 v6.0.4 h1:AXFAz7G05VZkKretSSU+uacMKF8+C16ONG6pzFzzA7E= github.com/bmeg/jsonschema/v6 v6.0.4/go.mod h1:gTh32doM+BEZyi/TDPJEp8k3qXTckXY4ohptV2xExQY= -github.com/bmeg/jsonschemagraph v0.0.4-0.20251015150525-9ed100499f63 h1:vXw9uZsI22wSqJsQDnje1fetSv5iQTkXGJKRFJP8+S4= -github.com/bmeg/jsonschemagraph v0.0.4-0.20251015150525-9ed100499f63/go.mod h1:Ve7jAQhYAMkHUiko99+2CwqXI4Ur0ty/ai8Tfa2ONz4= +github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c h1:J1EhcEmL1D/YHxoMIw4HeeVv+hRMUFezNlAAlFX/a8M= +github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c/go.mod h1:Ve7jAQhYAMkHUiko99+2CwqXI4Ur0ty/ai8Tfa2ONz4= github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= @@ -33,6 +33,8 @@ github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZw github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 h1:NVO5FpugLr+6rhTS/er1B2Ui8RIykbFL+IJcOXDdqBo= github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= +github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 h1:ii28j/rzbOaxpUVnONEMmDm7FyZy03qBcHFFSNRsCbQ= +github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2/go.mod h1:SJfHDSOaN5xevCor2PVRZxHHJ/WwfAnBBddgnQcUwJo= github.com/calypr/git-drs v0.0.0-20251103160406-847061641992 h1:z/EIrWehDHLWNCcVYLth6w3Hvppi8xWMKkzgtmjqFfs= github.com/calypr/git-drs v0.0.0-20251103160406-847061641992/go.mod h1:qEwYFbRiLKwblNBPFRWtLkGU4aqTp6r2Dwi2Oe7IN4Y= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -71,8 +73,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k= github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg= github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= @@ -179,8 +181,6 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= @@ -222,8 +222,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e h1:IWllFTiDjjLIf2oeKxpIUmtiDV5sn71VgeQgg6vcE7k= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= From 8d6ca8ab4307815a0e13a628b9fb57d2fe055a2f Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Wed, 12 Nov 2025 07:35:19 -0800 Subject: [PATCH 06/12] add config init, sower API support --- client/client.go | 10 ++++- client/fence/fence.go | 4 +- client/sower/resp.go | 9 +++- client/sower/sower.go | 66 ++++++++++++++++++++++++--- cmd/cmd.go | 5 +++ cmd/config/main.go | 20 +++++++++ cmd/publish/main.go | 65 ++++++++++++++++++++++++++- config/config.go | 101 ++++++++++++++++++++++++++++++++++++++++++ publish/publish.go | 24 +++++----- 9 files changed, 279 insertions(+), 25 deletions(-) create mode 100644 cmd/config/main.go create mode 100644 config/config.go diff --git a/client/client.go b/client/client.go index bea90d9..b6fee5d 100644 --- a/client/client.go +++ b/client/client.go @@ -57,7 +57,6 @@ func NewGen3Client() (*Gen3Client, error) { if bucketName == "" { return nil, fmt.Errorf("No gen3 bucket specified. Please provide a gen3Bucket key in your .drsconfig") } - return &Gen3Client{Base: baseUrl, Cred: cred, ProjectId: projectId, BucketName: bucketName}, err } @@ -66,7 +65,7 @@ type Resp struct { Err error } -func (cl *Gen3Client) MakeReq(method string, path string, body []byte) *Resp { +func (cl *Gen3Client) MakeReq(method string, path string, body []byte, params map[string]string) *Resp { a := *cl.Base a.Path = filepath.Join(a.Path, path) @@ -79,6 +78,13 @@ func (cl *Gen3Client) MakeReq(method string, path string, body []byte) *Resp { if err != nil { return &Resp{nil, err} } + + q := req.URL.Query() + for key, val := range params { + q.Add(key, val) + } + req.URL.RawQuery = q.Encode() + expiration, err := token.GetExpiration(cl.Cred.AccessToken) if err != nil { return &Resp{nil, err} diff --git a/client/fence/fence.go b/client/fence/fence.go index 4ecf46a..951928e 100644 --- a/client/fence/fence.go +++ b/client/fence/fence.go @@ -44,7 +44,7 @@ type FenceClient struct { } func (fc *FenceClient) UserPing() (*PingResp, error) { - reqResp := fc.MakeReq(http.MethodGet, commonUtils.FenceUserEndpoint, nil) + reqResp := fc.MakeReq(http.MethodGet, commonUtils.FenceUserEndpoint, nil, nil) if reqResp.Body == nil || reqResp.Err != nil { return nil, reqResp.Err } @@ -54,7 +54,7 @@ func (fc *FenceClient) UserPing() (*PingResp, error) { return nil, err } - bucketResp := fc.MakeReq(http.MethodGet, FenceBucketEndpoint, nil) + bucketResp := fc.MakeReq(http.MethodGet, FenceBucketEndpoint, nil, nil) if reqResp.Body == nil || reqResp.Err != nil { return nil, bucketResp.Err } diff --git a/client/sower/resp.go b/client/sower/resp.go index bce153c..1d46dde 100644 --- a/client/sower/resp.go +++ b/client/sower/resp.go @@ -1,4 +1,11 @@ package sower -type DispatchResp struct { +type StatusResp struct { + Uid string `json:"uid"` + Name string `json:"name"` + Status string `json:"status"` +} + +type OutputResp struct { + Output string `json:"output"` } diff --git a/client/sower/sower.go b/client/sower/sower.go index c5ad155..356de89 100644 --- a/client/sower/sower.go +++ b/client/sower/sower.go @@ -2,19 +2,24 @@ package sower import ( "encoding/json" - "fmt" "net/http" "github.com/calypr/forge/client" ) const sowerDispatch = "/job/dispatch" +const sowerStatus = "/job/status" +const sowerList = "/job/list" +const sowerJobOutput = "/job/output" type Sower interface { - DispatchJob(name string, args *DispatchArgs) (*DispatchResp, error) + DispatchJob(name string, args *DispatchArgs) (*StatusResp, error) + Status(uid string) (*StatusResp, error) + List() (*[]StatusResp, error) + Output(uid string) (*OutputResp, error) } -func DispatchJob(s Sower, name string, args *DispatchArgs) (*DispatchResp, error) { +func DispatchJob(s Sower, name string, args *DispatchArgs) (*StatusResp, error) { return s.DispatchJob(name, args) } @@ -52,7 +57,7 @@ type JobArgs struct { Action string `json:"action"` } -func (sc *SowerClient) DispatchJob(name string, args *DispatchArgs) (*DispatchResp, error) { +func (sc *SowerClient) DispatchJob(name string, args *DispatchArgs) (*StatusResp, error) { body := JobArgs{ Action: name, Input: *args, @@ -62,11 +67,58 @@ func (sc *SowerClient) DispatchJob(name string, args *DispatchArgs) (*DispatchRe return nil, err } - resp := sc.MakeReq(http.MethodPost, sowerDispatch, bodyBytes) + resp := sc.MakeReq(http.MethodPost, sowerDispatch, bodyBytes, nil) if resp.Err != nil { return nil, resp.Err } - fmt.Println("Response: ", string(resp.Body)) - return nil, nil + StatusResp := &StatusResp{} + err = json.Unmarshal(resp.Body, StatusResp) + if err != nil { + return nil, err + } + return StatusResp, nil +} + +func (sc *SowerClient) Status(uid string) (*StatusResp, error) { + resp := sc.MakeReq(http.MethodGet, sowerStatus, nil, map[string]string{"UID": uid}) + if resp.Err != nil { + return nil, resp.Err + } + + StatusResp := &StatusResp{} + err := json.Unmarshal(resp.Body, StatusResp) + if err != nil { + return nil, err + } + return StatusResp, nil +} + +func (sc *SowerClient) Output(uid string) (*OutputResp, error) { + params := map[string]string{"UID": uid} + resp := sc.MakeReq(http.MethodGet, sowerJobOutput, nil, params) + if resp.Err != nil { + return nil, resp.Err + } + + var outputResp OutputResp + err := json.Unmarshal(resp.Body, &outputResp) + if err != nil { + return nil, err + } + return &outputResp, nil +} + +func (sc *SowerClient) List() ([]StatusResp, error) { + resp := sc.MakeReq(http.MethodGet, sowerList, nil, nil) + if resp.Err != nil { + return nil, resp.Err + } + + ListResp := []StatusResp{{}} + err := json.Unmarshal(resp.Body, &ListResp) + if err != nil { + return nil, err + } + return ListResp, nil } diff --git a/cmd/cmd.go b/cmd/cmd.go index 0fe58ca..06ccf71 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,6 +1,7 @@ package cmd import ( + "github.com/calypr/forge/cmd/config" "github.com/calypr/forge/cmd/empty" "github.com/calypr/forge/cmd/initialize" "github.com/calypr/forge/cmd/meta" @@ -23,6 +24,10 @@ func init() { RootCmd.AddCommand(meta.MetaCmd) RootCmd.AddCommand(publish.PublishCmd) RootCmd.AddCommand(empty.EmptyCmd) + RootCmd.AddCommand(config.ConfigCmd) + RootCmd.AddCommand(publish.ListCmd) + RootCmd.AddCommand(publish.StatusCmd) + RootCmd.AddCommand(publish.OutputCmd) validate.ValidateParentCmd.AddCommand(validate.ValidateConfigCmd) validate.ValidateParentCmd.AddCommand(validate.ValidateDataCmd) diff --git a/cmd/config/main.go b/cmd/config/main.go new file mode 100644 index 0000000..fe15848 --- /dev/null +++ b/cmd/config/main.go @@ -0,0 +1,20 @@ +package config + +import ( + "github.com/calypr/forge/config" + "github.com/spf13/cobra" +) + +var ConfigCmd = &cobra.Command{ + Use: "config", + Short: "Autogenerate metadata based off of files that have been uploaded", + Long: `Not needed for expected user workflow. Useful for debugging server side operations only.`, + Example: "forge meta", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.RunConfigInit() + if err != nil { + return err + } + return nil + }, +} diff --git a/cmd/publish/main.go b/cmd/publish/main.go index bfcdc0b..e0ea7fa 100644 --- a/cmd/publish/main.go +++ b/cmd/publish/main.go @@ -1,6 +1,9 @@ package publish import ( + "fmt" + + "github.com/calypr/forge/client/sower" "github.com/calypr/forge/publish" "github.com/spf13/cobra" ) @@ -11,10 +14,70 @@ var PublishCmd = &cobra.Command{ Long: `The 'publish' command is how metadata is handled in calypr.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - err := publish.RunPublish(args[0]) + resp, err := publish.RunPublish(args[0]) + if err != nil { + return err + } + fmt.Printf("Uid: %s \t Name: %s \t Status: %s\n", resp.Uid, resp.Name, resp.Status) + return nil + }, +} + +var ListCmd = &cobra.Command{ + Use: "list ", + Short: "create metadata upload job for FHIR ndjson files", + Long: `The 'publish' command is how metadata is handled in calypr.`, + RunE: func(cmd *cobra.Command, args []string) error { + sc, err := sower.NewSowerClient() + if err != nil { + return err + } + vals, err := sc.List() + if len(vals) == 0 { + fmt.Printf("There are no jobs to list: %s\n", vals) + } else { + for _, val := range vals { + fmt.Printf("Uid: %s \t Name: %s \t Status: %s\n", val.Uid, val.Name, val.Status) + } + } + return nil + }, +} + +var StatusCmd = &cobra.Command{ + Use: "status ", + Short: "create metadata upload job for FHIR ndjson files", + Long: `The 'publish' command is how metadata is handled in calypr.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + sc, err := sower.NewSowerClient() + if err != nil { + return err + } + status, err := sc.Status(args[0]) + if err != nil { + return err + } + fmt.Printf("Uid: %s \t Name: %s \t Status: %s\n", status.Uid, status.Name, status.Status) + return nil + }, +} + +var OutputCmd = &cobra.Command{ + Use: "output ", + Short: "create metadata upload job for FHIR ndjson files", + Long: `The 'publish' command is how metadata is handled in calypr.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + sc, err := sower.NewSowerClient() + if err != nil { + return err + } + output, err := sc.Output(args[0]) if err != nil { return err } + fmt.Printf("Logs: %s\n", output.Output) return nil }, } diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..79e7299 --- /dev/null +++ b/config/config.go @@ -0,0 +1,101 @@ +package config + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + gconf "github.com/calypr/gecko/gecko/config" + "github.com/calypr/git-drs/config" +) + +func RunConfigInit() error { + conf, err := config.LoadConfig() + if err != nil { + return err + } + if conf.Servers.Gen3 == nil { + return fmt.Errorf("Config generation expects a populated gen3 config but conf.Servers.Gen3 is nil") + } + + projectId := conf.Servers.Gen3.Auth.ProjectID + err = os.MkdirAll("CONFIG", os.ModePerm) + if err != nil { + return err + } + + filePath := filepath.Join("CONFIG", projectId+".json") + f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) + if err != nil { + if os.IsExist(err) { + return fmt.Errorf("configuration file '%s' already exists. Aborting to prevent overwrite", filePath) + } + return err + } + defer f.Close() + + // Create an empty config so that users have the basic structure of a config + // as a template for them so that they can focus more time on filling out hte config and less + // time figuring out the correct structure of the config + var emptyConf gconf.Config = gconf.Config{ + SharedFilters: gconf.SharedFiltersConfig{ + SharedFilter: map[string][]gconf.FilterPair{ + "": { + { + Index: "", + Field: "", + }, + }, + }, + }, + ExplorerConfig: []gconf.ConfigItem{{ + TabTitle: "", + Filters: gconf.FiltersConfig{ + Tabs: []gconf.FilterTab{{ + Title: "", + Fields: []string{}, + FieldsConfig: map[string]gconf.FieldConfig{ + "": { + Field: "", + DataField: "", + Index: "", + Label: "", + Type: "", + }, + }, + }}, + }, + Charts: map[string]gconf.Chart{ + "": { + ChartType: "", + Title: "", + }, + }, + GuppyConfig: gconf.GuppyConfig{ + DataType: "", + }, + Table: gconf.TableConfig{ + Enabled: false, + Fields: []string{}, + Columns: map[string]gconf.TableColumnsConfig{ + "": { + Field: "", + Title: "", + AccessorPath: "", + }, + }, + }, + Dropdowns: map[string]any{}, + LoginForDownload: false, + }}, + } + + mEConf, err := json.Marshal(emptyConf) + if err != nil { + return err + } + + _, err = f.Write(mEConf) + return nil +} diff --git a/publish/publish.go b/publish/publish.go index 24bee75..b249f7f 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -36,42 +36,42 @@ func RunEmpty(projectId string) error { return nil } -func RunPublish(token string) error { +func RunPublish(token string) (*sower.StatusResp, error) { err := checkGHPAccessToken(token) if err != nil { - return err + return nil, err } repo, err := gitutil.OpenRepository(".") if err != nil { - return err + return nil, err } remote, err := repo.Remote("origin") if err != nil { - return fmt.Errorf("failed to get 'origin' remote: %w", err) + return nil, fmt.Errorf("failed to get 'origin' remote: %w", err) } urls := remote.Config().URLs if len(urls) == 0 { - return fmt.Errorf("no URLs found for 'origin' remote") + return nil, fmt.Errorf("no URLs found for 'origin' remote") } if len(urls) > 1 { - return fmt.Errorf("not expecting more than 1 remote url. Got %d: %s", len(urls), urls) + return nil, fmt.Errorf("not expecting more than 1 remote url. Got %d: %s", len(urls), urls) } remoteURL := urls[0] url, err := gitutil.TrimGitURLPrefix(remoteURL) if err != nil { - return err + return nil, err } username, err := gitutil.GetGlobalUserIdentity() if err != nil { - return fmt.Errorf("Unable to read global git config to get username: %s", err) + return nil, fmt.Errorf("Unable to read global git config to get username: %s", err) } hash, err := gitutil.GetLastLocalCommit(repo) if err != nil { - return err + return nil, err } sc, err := sower.NewSowerClient() if err != nil { - return err + return nil, err } dispatchArgs := &sower.DispatchArgs{ @@ -91,9 +91,9 @@ func RunPublish(token string) error { dispatchArgs, ) if err != nil || resp != nil { - return fmt.Errorf("failed to dispatch job: %v: %w", resp, err) + return nil, fmt.Errorf("failed to dispatch job: %v: %w", resp, err) } - return nil + return resp, nil } func checkGHPAccessToken(token string) error { From ff15e7f15c467617d6edb9e6b42462db6dfd2f34 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Wed, 12 Nov 2025 14:32:42 -0800 Subject: [PATCH 07/12] fix up sower resp, instructions --- cmd/publish/main.go | 24 +++++++++++++----------- publish/publish.go | 15 ++++++++------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/cmd/publish/main.go b/cmd/publish/main.go index e0ea7fa..a795281 100644 --- a/cmd/publish/main.go +++ b/cmd/publish/main.go @@ -24,9 +24,9 @@ var PublishCmd = &cobra.Command{ } var ListCmd = &cobra.Command{ - Use: "list ", - Short: "create metadata upload job for FHIR ndjson files", - Long: `The 'publish' command is how metadata is handled in calypr.`, + Use: "list", + Short: "view all of the jobs currently catalogued in sower", + Long: `The 'list' command is how jobs are displayed to the user`, RunE: func(cmd *cobra.Command, args []string) error { sc, err := sower.NewSowerClient() if err != nil { @@ -45,10 +45,11 @@ var ListCmd = &cobra.Command{ } var StatusCmd = &cobra.Command{ - Use: "status ", - Short: "create metadata upload job for FHIR ndjson files", - Long: `The 'publish' command is how metadata is handled in calypr.`, - Args: cobra.ExactArgs(1), + Use: "status ", + Short: "view the status of a specific job on sower", + Long: `The 'status' command is how sower job status is communicated to the user. + A specific job's UID can be found from running the list command`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { sc, err := sower.NewSowerClient() if err != nil { @@ -64,10 +65,11 @@ var StatusCmd = &cobra.Command{ } var OutputCmd = &cobra.Command{ - Use: "output ", - Short: "create metadata upload job for FHIR ndjson files", - Long: `The 'publish' command is how metadata is handled in calypr.`, - Args: cobra.ExactArgs(1), + Use: "output ", + Short: "view output logs of a specific job on sower", + Long: `The 'output' command is how sower job output logs are communicated to the user. + A specific job's UID can be found from running the list command`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { sc, err := sower.NewSowerClient() if err != nil { diff --git a/publish/publish.go b/publish/publish.go index b249f7f..bf12aff 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -15,10 +15,10 @@ const SOURCE_GH_USER_ENDPOINT = "https://source.ohsu.edu/api/v3/user" const POD_PUT_METHOD = "put" const POD_DELETE_METHOD = "delete" -func RunEmpty(projectId string) error { +func RunEmpty(projectId string) (*sower.StatusResp, error) { sc, err := sower.NewSowerClient() if err != nil { - return err + return nil, err } dispatchArgs := &sower.DispatchArgs{ ProjectId: sc.ProjectId, @@ -30,10 +30,11 @@ func RunEmpty(projectId string) error { FHIR_JOB_NAME, dispatchArgs, ) - if err != nil || resp != nil { - return fmt.Errorf("failed to dispatch job: %v: %w", resp, err) + if err != nil { + return nil, fmt.Errorf("failed to dispatch empty job. resp: %v err: %s", resp, err) } - return nil + + return resp, nil } func RunPublish(token string) (*sower.StatusResp, error) { @@ -90,8 +91,8 @@ func RunPublish(token string) (*sower.StatusResp, error) { FHIR_JOB_NAME, dispatchArgs, ) - if err != nil || resp != nil { - return nil, fmt.Errorf("failed to dispatch job: %v: %w", resp, err) + if err != nil { + return nil, fmt.Errorf("failed to dispatch job. resp: %v err: %s", resp, err) } return resp, nil } From 8cc3a186bebbb0a28a3cac3bf3bb8f094587add4 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Mon, 1 Dec 2025 14:27:57 -0800 Subject: [PATCH 08/12] update sower to work with latest git-drs --- client/client.go | 19 +++++---- client/fence/fence.go | 5 ++- client/sower/sower.go | 7 ++-- cmd/cmd.go | 3 +- cmd/config/main.go | 17 +++++--- cmd/empty/main.go | 19 +++++++-- cmd/meta/main.go | 6 ++- cmd/ping/main.go | 12 +++++- cmd/publish/main.go | 45 +++++++++++++++++----- cmd/validate/main.go | 26 +++++++++---- config/config.go | 16 +++++--- go.mod | 22 ++++++++++- go.sum | 44 ++++++++++++++++++++- metadata/meta.go | 90 +++++++++++++++++++++++++++++++++++++------ metadata/skeleton.go | 55 +++++++++++++------------- publish/publish.go | 11 +++--- schema/new.go | 8 ---- 17 files changed, 300 insertions(+), 105 deletions(-) diff --git a/client/client.go b/client/client.go index b6fee5d..0527778 100644 --- a/client/client.go +++ b/client/client.go @@ -10,6 +10,7 @@ import ( "time" token "github.com/bmeg/grip-graphql/middleware" + "github.com/calypr/git-drs/config" drsConfig "github.com/calypr/git-drs/config" "github.com/calypr/data-client/client/commonUtils" @@ -24,36 +25,34 @@ type Gen3Client struct { } // load repo-level config and return a new IndexDClient -func NewGen3Client() (*Gen3Client, error) { +func NewGen3Client(remote config.Remote) (*Gen3Client, error) { var conf jwt.Configure cfg, err := drsConfig.LoadConfig() if err != nil { return nil, err } - - profile := cfg.Servers.Gen3.Auth.Profile - if profile == "" { - return nil, fmt.Errorf("No gen3 profile specified. Please provide a gen3Profile key in your .drsconfig") + gfc, ok := cfg.Remotes[remote] + if !ok { + return nil, fmt.Errorf("remote %s not found in config: %v", remote, cfg.Remotes) } - cred, err := conf.ParseConfig(profile) + cred, err := conf.ParseConfig(string(remote)) if err != nil { return nil, err } baseUrl, err := url.Parse(cred.APIEndpoint) if err != nil { - return nil, fmt.Errorf("error parsing base URL from profile %s: %v", profile, err) + return nil, fmt.Errorf("error parsing base URL from profile %s: %v", remote, err) } // get the gen3Project and gen3Bucket from the config - projectId := cfg.Servers.Gen3.Auth.ProjectID + projectId := gfc.Gen3.ProjectID if projectId == "" { return nil, fmt.Errorf("No gen3 project specified. Please provide a gen3Project key in your .drsconfig") } - - bucketName := cfg.Servers.Gen3.Auth.Bucket + bucketName := gfc.Gen3.Bucket if bucketName == "" { return nil, fmt.Errorf("No gen3 bucket specified. Please provide a gen3Bucket key in your .drsconfig") } diff --git a/client/fence/fence.go b/client/fence/fence.go index 951928e..f9d7cc2 100644 --- a/client/fence/fence.go +++ b/client/fence/fence.go @@ -6,6 +6,7 @@ import ( "github.com/calypr/data-client/client/commonUtils" "github.com/calypr/forge/client" + "github.com/calypr/git-drs/config" ) // FenceBucketEndpoint is is the endpoint postfix for FENCE bucket list @@ -31,8 +32,8 @@ func UserPing(f Fence) (*PingResp, error) { return f.UserPing() } -func NewFenceClient() (*FenceClient, error) { - gen3Client, err := client.NewGen3Client() +func NewFenceClient(remote config.Remote) (*FenceClient, error) { + gen3Client, err := client.NewGen3Client(remote) if err != nil { return nil, err } diff --git a/client/sower/sower.go b/client/sower/sower.go index 356de89..47b0743 100644 --- a/client/sower/sower.go +++ b/client/sower/sower.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/calypr/forge/client" + "github.com/calypr/git-drs/config" ) const sowerDispatch = "/job/dispatch" @@ -23,8 +24,8 @@ func DispatchJob(s Sower, name string, args *DispatchArgs) (*StatusResp, error) return s.DispatchJob(name, args) } -func NewSowerClient() (*SowerClient, error) { - gen3Client, err := client.NewGen3Client() +func NewSowerClient(remote config.Remote) (*SowerClient, error) { + gen3Client, err := client.NewGen3Client(remote) if err != nil { return nil, err } @@ -45,7 +46,7 @@ type DispatchArgs struct { ProjectId string `json:"projectId"` Profile string `json:"profile"` BucketName string `json:"bucketName"` - APIEndpoint string `json:"APIEndpoint` + APIEndpoint string `json:"APIEndpoint"` GHCommitHash string `json:"ghCommitHash"` GHPAccessToken string `json:"ghToken"` GHUserName string `json:"ghUserName"` diff --git a/cmd/cmd.go b/cmd/cmd.go index 06ccf71..227b995 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -3,7 +3,6 @@ package cmd import ( "github.com/calypr/forge/cmd/config" "github.com/calypr/forge/cmd/empty" - "github.com/calypr/forge/cmd/initialize" "github.com/calypr/forge/cmd/meta" "github.com/calypr/forge/cmd/ping" "github.com/calypr/forge/cmd/publish" @@ -19,7 +18,7 @@ development and project management tasks.`, } func init() { - RootCmd.AddCommand(initialize.InitCmd) + // RootCmd.AddCommand(initialize.InitCmd) comment this out for now RootCmd.AddCommand(ping.PingCmd) RootCmd.AddCommand(meta.MetaCmd) RootCmd.AddCommand(publish.PublishCmd) diff --git a/cmd/config/main.go b/cmd/config/main.go index fe15848..90367e0 100644 --- a/cmd/config/main.go +++ b/cmd/config/main.go @@ -2,16 +2,23 @@ package config import ( "github.com/calypr/forge/config" + conf "github.com/calypr/git-drs/config" "github.com/spf13/cobra" ) var ConfigCmd = &cobra.Command{ - Use: "config", - Short: "Autogenerate metadata based off of files that have been uploaded", - Long: `Not needed for expected user workflow. Useful for debugging server side operations only.`, - Example: "forge meta", + Use: "config [remote]", + Short: "Build skeleton template for CALYPR explorer page config.", + Long: `Used for creating a template CALYPR explorer config to build and customize your own config`, + Example: "forge config local", RunE: func(cmd *cobra.Command, args []string) error { - err := config.RunConfigInit() + var remote string + if len(args) > 0 { + remote = args[0] + } else { + remote = "" + } + err := config.RunConfigInit(conf.Remote(remote)) if err != nil { return err } diff --git a/cmd/empty/main.go b/cmd/empty/main.go index d64d7fb..6d18bd0 100644 --- a/cmd/empty/main.go +++ b/cmd/empty/main.go @@ -1,20 +1,33 @@ package empty import ( + "fmt" + "github.com/calypr/forge/publish" + "github.com/calypr/git-drs/config" "github.com/spf13/cobra" ) var EmptyCmd = &cobra.Command{ - Use: "empty ", + Use: "empty [remote]", Short: "empty metadata for a project", Long: `The 'empty' command is how metadata is removed in calypr.`, - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { - err := publish.RunEmpty(args[0]) + projectID := args[0] + var remote config.Remote + if len(args) == 2 { + remote = config.Remote(args[1]) + fmt.Printf("Using remote: %s\n", remote) + } else { + remote = config.Remote("") + fmt.Printf("Using default remote: %s\n", remote) + } + resp, err := publish.RunEmpty(projectID, remote) if err != nil { return err } + fmt.Printf("Uid: %s\t Name: %s\t Status: %s\n", resp.Uid, resp.Name, resp.Status) return nil }, } diff --git a/cmd/meta/main.go b/cmd/meta/main.go index ff08ecd..7e466ef 100644 --- a/cmd/meta/main.go +++ b/cmd/meta/main.go @@ -2,6 +2,7 @@ package meta import ( "github.com/calypr/forge/metadata" + "github.com/calypr/git-drs/config" "github.com/spf13/cobra" ) @@ -11,9 +12,10 @@ var MetaCmd = &cobra.Command{ Use: "meta", Short: "Autogenerate metadata based off of files that have been uploaded", Long: `Not needed for expected user workflow. Useful for debugging server side operations only.`, - Example: "forge meta", + Example: "forge meta [remote]", + Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { - err := metadata.RunMetaInit(outPath) + err := metadata.RunMetaInit(outPath, config.Remote(args[0])) if err != nil { return err } diff --git a/cmd/ping/main.go b/cmd/ping/main.go index 376c65f..d27949b 100644 --- a/cmd/ping/main.go +++ b/cmd/ping/main.go @@ -5,15 +5,23 @@ import ( "log" "github.com/calypr/forge/client/fence" + "github.com/calypr/git-drs/config" "github.com/spf13/cobra" "gopkg.in/yaml.v2" ) var PingCmd = &cobra.Command{ - Use: "ping", + Use: "ping [remote]", Short: "Ping Calypr instance and return user's project and user permissions", RunE: func(cmd *cobra.Command, args []string) error { - FenceClient, err := fence.NewFenceClient() + var remote config.Remote + if len(args) > 0 { + remote = config.Remote(args[0]) + } else { + remote = config.Remote("") + } + + FenceClient, err := fence.NewFenceClient(remote) if err != nil { return err } diff --git a/cmd/publish/main.go b/cmd/publish/main.go index a795281..75ae992 100644 --- a/cmd/publish/main.go +++ b/cmd/publish/main.go @@ -5,16 +5,25 @@ import ( "github.com/calypr/forge/client/sower" "github.com/calypr/forge/publish" + "github.com/calypr/git-drs/config" "github.com/spf13/cobra" ) var PublishCmd = &cobra.Command{ - Use: "publish ", + Use: "publish [remote]", Short: "create metadata upload job for FHIR ndjson files", Long: `The 'publish' command is how metadata is handled in calypr.`, - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { - resp, err := publish.RunPublish(args[0]) + var remote config.Remote + if len(args) == 2 { + remote = config.Remote(args[1]) + fmt.Printf("Using remote: %s\n", remote) + } else { + remote = config.Remote("") + fmt.Printf("Using default remote: %s\n", remote) + } + resp, err := publish.RunPublish(args[0], remote) if err != nil { return err } @@ -24,11 +33,11 @@ var PublishCmd = &cobra.Command{ } var ListCmd = &cobra.Command{ - Use: "list", + Use: "list [remote]", Short: "view all of the jobs currently catalogued in sower", Long: `The 'list' command is how jobs are displayed to the user`, RunE: func(cmd *cobra.Command, args []string) error { - sc, err := sower.NewSowerClient() + sc, err := sower.NewSowerClient(config.Remote(args[0])) if err != nil { return err } @@ -45,13 +54,21 @@ var ListCmd = &cobra.Command{ } var StatusCmd = &cobra.Command{ - Use: "status ", + Use: "status [remote]", Short: "view the status of a specific job on sower", Long: `The 'status' command is how sower job status is communicated to the user. A specific job's UID can be found from running the list command`, - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { - sc, err := sower.NewSowerClient() + var remote config.Remote + if len(args) == 2 { + remote = config.Remote(args[1]) + fmt.Printf("Using remote: %s\n", remote) + } else { + remote = config.Remote("") + fmt.Printf("Using default remote: %s\n", remote) + } + sc, err := sower.NewSowerClient(remote) if err != nil { return err } @@ -65,13 +82,21 @@ var StatusCmd = &cobra.Command{ } var OutputCmd = &cobra.Command{ - Use: "output ", + Use: "output [remote]", Short: "view output logs of a specific job on sower", Long: `The 'output' command is how sower job output logs are communicated to the user. A specific job's UID can be found from running the list command`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - sc, err := sower.NewSowerClient() + var remote config.Remote + if len(args) == 2 { + remote = config.Remote(args[1]) + fmt.Printf("Using remote: %s\n", remote) + } else { + remote = config.Remote("") + fmt.Printf("Using default remote: %s\n", remote) + } + sc, err := sower.NewSowerClient(remote) if err != nil { return err } diff --git a/cmd/validate/main.go b/cmd/validate/main.go index e7c3376..2cbf89e 100644 --- a/cmd/validate/main.go +++ b/cmd/validate/main.go @@ -12,6 +12,7 @@ import ( "github.com/bmeg/grip/gripql" "github.com/bytedance/sonic" "github.com/calypr/gecko/gecko/config" + "github.com/cockroachdb/errors" "github.com/calypr/forge/metadata" @@ -31,14 +32,19 @@ func init() { ValidateEdgeCmd.Flags().StringVarP(&outputDir, "out-dir", "o", "", "Directory to save vertices and edges files") } +const META_PATH = "META" +const CONFIG_PATH = "CONFIG" + // ValidateCmd remains unchanged var ValidateDataCmd = &cobra.Command{ Use: "data ", Short: "data data files given a jsonschema and a ndjson data target file or directory", - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - path := args[0] - + path := META_PATH + if len(args) > 0 { + path = args[0] + } sch, err := schema.NewSchema() if err != nil { return errors.Wrap(err, "failed to create schema") @@ -112,9 +118,12 @@ var ValidateEdgeCmd = &cobra.Command{ Use: "edge ", Short: "Check for orphaned edges in graph data from FHIR .ndjson files", Long: "Generates graph elements from FHIR .ndjson files and checks for edges referencing non-existent vertices", - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - path := args[0] + path := META_PATH + if len(args) > 0 { + path = args[0] + } sch, err := schema.NewSchema() if err != nil { return errors.Wrap(err, "failed to create schema") @@ -354,9 +363,12 @@ var ValidateEdgeCmd = &cobra.Command{ var ValidateConfigCmd = &cobra.Command{ Use: "config ", Short: "config explorer config file", - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - path := args[0] + path := CONFIG_PATH + if len(args) > 0 { + path = args[0] + } info, err := os.Stat(path) if err != nil { diff --git a/config/config.go b/config/config.go index 79e7299..0d85dad 100644 --- a/config/config.go +++ b/config/config.go @@ -10,22 +10,28 @@ import ( "github.com/calypr/git-drs/config" ) -func RunConfigInit() error { +func RunConfigInit(remote config.Remote) error { conf, err := config.LoadConfig() if err != nil { return err } - if conf.Servers.Gen3 == nil { - return fmt.Errorf("Config generation expects a populated gen3 config but conf.Servers.Gen3 is nil") + remoteConfig, ok := conf.Remotes[remote] + if !ok { + return fmt.Errorf("Remote %s not found in config: %v", remote, conf.Remotes) + } + if remoteConfig.Gen3 == nil { + return fmt.Errorf("Config generation expects a populated gen3 config but conf.Remotes.%s.Gen3 is nil", remote) + } + if remoteConfig.Gen3.ProjectID == "" { + return fmt.Errorf("ProjectID is '' when expected a populated gen3 config for conf.Remotes.%s.Gen3.ProjectID", remote) } - projectId := conf.Servers.Gen3.Auth.ProjectID err = os.MkdirAll("CONFIG", os.ModePerm) if err != nil { return err } - filePath := filepath.Join("CONFIG", projectId+".json") + filePath := filepath.Join("CONFIG", remoteConfig.Gen3.ProjectID+".json") f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) if err != nil { if os.IsExist(err) { diff --git a/go.mod b/go.mod index b7ba81d..f82f73c 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/bytedance/sonic v1.14.0 github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 - github.com/calypr/git-drs v0.0.0-20251103160406-847061641992 + github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff @@ -23,9 +23,28 @@ require ( require ( bitbucket.org/creachadair/stringset v0.0.9 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/aws/aws-sdk-go-v2 v1.39.2 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect + github.com/aws/aws-sdk-go-v2/config v1.31.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.9 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.88.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect + github.com/aws/smithy-go v1.23.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect @@ -77,6 +96,7 @@ require ( golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.45.0 // indirect + golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/term v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect diff --git a/go.sum b/go.sum index 1d5b39e..29ebe68 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ bitbucket.org/creachadair/stringset v0.0.9 h1:L4vld9nzPt90UZNrXjNelTshD74ps4P5NGs3Iq6yN3o= bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -13,6 +15,42 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= +github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00= +github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= +github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.9 h1:w9LnHqTq8MEdlnyhV4Bwfizd65lfNCNgdlNC6mM5paE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.9/go.mod h1:LGEP6EK4nj+bwWNdrvX/FnDTFowdBNwcSPuZu/ouFys= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.0 h1:X0FveUndcZ3lKbSpIC6rMYGRiQTcUVRNH6X4yYtIrlU= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.0/go.mod h1:IWjQYlqw4EX9jw2g3qnEPPWvCE6bS8fKzhMed1OK7c8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.9 h1:wuZ5uW2uhJR63zwNlqWH2W4aL4ZjeJP3o92/W+odDY4= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.9/go.mod h1:/G58M2fGszCrOzvJUkDdY8O9kycodunH4VdT5oBAqls= +github.com/aws/aws-sdk-go-v2/service/s3 v1.88.4 h1:mUI3b885qJgfqKDUSj6RgbRqLdX0wGmg8ruM03zNfQA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.88.4/go.mod h1:6v8ukAxc7z4x4oBjGUsLnH7KGLY9Uhcgij19UJNkiMg= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/bazelbuild/rules_go v0.24.5 h1:8S5qilf+Il5/TPMZQIOfzQDAZtkhB4jALiAnwRuisDM= github.com/bazelbuild/rules_go v0.24.5/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -35,8 +73,8 @@ github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 h1:NVO5FpugLr+6 github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 h1:ii28j/rzbOaxpUVnONEMmDm7FyZy03qBcHFFSNRsCbQ= github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2/go.mod h1:SJfHDSOaN5xevCor2PVRZxHHJ/WwfAnBBddgnQcUwJo= -github.com/calypr/git-drs v0.0.0-20251103160406-847061641992 h1:z/EIrWehDHLWNCcVYLth6w3Hvppi8xWMKkzgtmjqFfs= -github.com/calypr/git-drs v0.0.0-20251103160406-847061641992/go.mod h1:qEwYFbRiLKwblNBPFRWtLkGU4aqTp6r2Dwi2Oe7IN4Y= +github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0 h1:N/UtHeCY9neficWtSsNBu0NMaaSx62CwAsfShadEEtM= +github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0/go.mod h1:yosz5xq1jz5+qDoQiUtooz6+RYH8SNQu0WkPs8QQd2I= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= @@ -277,6 +315,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= +golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/metadata/meta.go b/metadata/meta.go index 15278f4..1b0b2b8 100644 --- a/metadata/meta.go +++ b/metadata/meta.go @@ -7,10 +7,13 @@ import ( "fmt" "log" "os" + "os/exec" "path/filepath" "time" - idxClient "github.com/calypr/git-drs/client" + indexd_client "github.com/calypr/git-drs/client/indexd" + "github.com/calypr/git-drs/config" + "github.com/calypr/git-drs/drs" fver "github.com/google/fhir/go/fhirversion" "github.com/google/fhir/go/jsonformat" code "github.com/google/fhir/go/proto/google/fhir/proto/r5/core/codes_go_proto" @@ -52,14 +55,20 @@ type MetaStructure struct { Path string `json:"path"` } -func RunMetaInit(outPath string) error { +func RunMetaInit(outPath string, remote config.Remote) error { var rsID string - cfg, err := idxClient.NewIndexDClient(&idxClient.NoOpLogger{}) + var err error + cfg, err := config.LoadConfig() if err != nil { return err } - idxCl, ok := cfg.(*idxClient.IndexDClient) + val, err := cfg.GetCurrentRemoteClient(nil) + if err != nil { + return err + } + + idxCl, ok := val.(*indexd_client.IndexDClient) if !ok { return fmt.Errorf("Config is not IndexDClient") } @@ -88,8 +97,21 @@ func RunMetaInit(outPath string) error { return fmt.Errorf("error listing indexd records: %v", err) } + collectRecs := []*drs.DRSObject{} + for rec := range recs { + if rec.Error != nil { + return fmt.Errorf("error from record channel: %v", rec.Error) + } + collectRecs = append(collectRecs, rec.Object) + } + + LFSRecords, err := findLFSRecords() + if err != nil { + return err + } + // Now that we have a channel, we can pass it directly to the merging function - if err := processDRSRecordsAndUpdateFHIR(recs, outPath, idxCl.Base.Host, idxCl.ProjectId, rsID); err != nil { + if err := processDRSRecordsAndUpdateFHIR(collectRecs, LFSRecords, outPath, idxCl.Base.Host, idxCl.ProjectId, rsID); err != nil { return fmt.Errorf("failed to process DRS records: %v", err) } @@ -246,8 +268,38 @@ func getResearchStudy(fhirDirectory string, projectId string, endpoint string, m return id, nil } +type LSFIles struct { + Files []LFSRecord `json:"files"` +} + +type LFSRecord struct { + Name string `json:"name"` + Size int64 `json:"size"` + Checkout bool `json:"checkout"` + Downloaded bool `json:"downloaded"` + OIDType string `json:"oid_type"` + OID string `json:"oid"` + Version string `json:"version"` +} + +// findLFSRecords runs ls-files and collects the results into struct +func findLFSRecords() ([]LFSRecord, error) { + output, err := exec.Command("git-lfs", "ls-files", "--json").Output() + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + return nil, fmt.Errorf("command execution failed: %w\nstderr: %s", err, exitErr.Stderr) + } + return nil, fmt.Errorf("failed to run git-lfs command: %w", err) + } + var records LSFIles + if err := json.Unmarshal(output, &records); err != nil { + return nil, fmt.Errorf("failed to unmarshal JSON output: %w", err) + } + return records.Files, nil +} + // processDRSRecordsAndUpdateFHIR processes DRS records and updates FHIR NDJSON files with UPSERT operation. -func processDRSRecordsAndUpdateFHIR(drsRecordsChan chan idxClient.ListRecordsResult, fhirDirectory string, endpoint string, project string, researchStudyID string) error { +func processDRSRecordsAndUpdateFHIR(drsRecords []*drs.DRSObject, LfsRecords []LFSRecord, fhirDirectory string, endpoint string, project string, researchStudyID string) error { docRefFP := filepath.Join(fhirDirectory, DOCUMENT_RESOURCE+NDJSON_EXT) dirRefFP := filepath.Join(fhirDirectory, DIRECTORY_RESOURCE+NDJSON_EXT) // New Directory file path existingFHIRRecords := make(map[string]*cprb.ContainedResource) @@ -296,12 +348,28 @@ func processDRSRecordsAndUpdateFHIR(drsRecordsChan chan idxClient.ListRecordsRes } count := 0 - for drsRecord := range drsRecordsChan { - count++ - if drsRecord.Error != nil { - return fmt.Errorf("error from record channel: %v", drsRecord.Error) + for _, rec := range LfsRecords { + foundMatch := false + containedResource := &cprb.ContainedResource{} + for _, drsRecord := range drsRecords { + found := false + for _, sum := range drsRecord.Checksums { + if rec.OID == sum.Checksum { + drsRecord.Name = rec.Name + foundMatch = true + containedResource = templateDocRef(drsRecord, endpoint, project, researchStudyID) + found = true + } + } + if found == true { + break + } } - containedResource := templateDocRef(drsRecord, endpoint, project, researchStudyID) + if !foundMatch { + continue + } + + count++ fhirRecord := containedResource.GetDocumentReference() recordID := fhirRecord.GetId().GetValue() diff --git a/metadata/skeleton.go b/metadata/skeleton.go index 1103d58..462d9db 100644 --- a/metadata/skeleton.go +++ b/metadata/skeleton.go @@ -4,12 +4,12 @@ import ( "fmt" "strings" - idxClient "github.com/calypr/git-drs/client" - "github.com/google/uuid" + "github.com/calypr/git-drs/drs" code "github.com/google/fhir/go/proto/google/fhir/proto/r5/core/codes_go_proto" dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r5/core/datatypes_go_proto" cprb "github.com/google/fhir/go/proto/google/fhir/proto/r5/core/resources/bundle_and_contained_resource_go_proto" drpb "github.com/google/fhir/go/proto/google/fhir/proto/r5/core/resources/document_reference_go_proto" + "github.com/google/uuid" ) const ( @@ -44,53 +44,54 @@ func CreateResourceReference(resourceId string) *dtpb.Reference { } } -func templateDocRef(metaStruc idxClient.ListRecordsResult, endpoint string, project string, rSID string) *cprb.ContainedResource { - obj := metaStruc.Record - +func templateDocRef(obj *drs.DRSObject, endpoint string, project string, rSID string) *cprb.ContainedResource { // Create the extensions for hashes var extensions []*dtpb.Extension - if obj.Hashes.MD5 != "" { - extensions = append(extensions, &dtpb.Extension{ - Url: &dtpb.Uri{Value: endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-md5"}, - Value: &dtpb.Extension_ValueX{ - Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: obj.Hashes.MD5}}, - }, - }) - } - if obj.Hashes.SHA256 != "" { - extensions = append(extensions, &dtpb.Extension{ - Url: &dtpb.Uri{Value: "http://" + endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-sha256"}, - Value: &dtpb.Extension_ValueX{ - Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: obj.Hashes.SHA256}}, - }, - }) + for _, sum := range obj.Checksums { + if sum.Type == drs.ChecksumTypeMD5 { + extensions = append(extensions, &dtpb.Extension{ + Url: &dtpb.Uri{Value: endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-md5"}, + Value: &dtpb.Extension_ValueX{ + Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: sum.Checksum}}, + }, + }) + } + if sum.Type == drs.ChecksumTypeSHA256 { + extensions = append(extensions, &dtpb.Extension{ + Url: &dtpb.Uri{Value: "http://" + endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-sha256"}, + Value: &dtpb.Extension_ValueX{ + Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: sum.Checksum}}, + }, + }) + } } // Determine the URL for the attachment var url *dtpb.Url - if len(obj.URLs) > 0 { - url = &dtpb.Url{Value: obj.URLs[0]} + if len(obj.AccessMethods) > 0 { + // TODO: Big assumption here assuming that there exists only one url per FHIR attachment + url = &dtpb.Url{Value: obj.AccessMethods[0].AccessURL.URL} } // Create the DocumentReference dr := &drpb.DocumentReference{ - Id: &dtpb.Id{Value: obj.Did}, + Id: &dtpb.Id{Value: obj.Id}, Status: &drpb.DocumentReference_StatusCode{Value: code.DocumentReferenceStatusCode_CURRENT}, DocStatus: &drpb.DocumentReference_DocStatusCode{Value: code.CompositionStatusCode_FINAL}, - Date: parseFHIRInstantString(obj.CreatedDate), + Date: parseFHIRInstantString(obj.CreatedTime), Identifier: []*dtpb.Identifier{ { Use: &dtpb.Identifier_UseCode{Value: code.IdentifierUseCode_OFFICIAL}, System: &dtpb.Uri{Value: "http://" + endpoint + "/" + project}, - Value: &dtpb.String{Value: obj.Did}, + Value: &dtpb.String{Value: obj.Id}, }, }, Content: []*drpb.DocumentReference_Content{ { Attachment: &dtpb.Attachment{ - Creation: parseFHIRDateTimeString(obj.CreatedDate), + Creation: parseFHIRDateTimeString(obj.CreatedTime), Size: &dtpb.Integer64{Value: obj.Size}, - Title: &dtpb.String{Value: obj.FileName}, + Title: &dtpb.String{Value: obj.Name}, Extension: extensions, Url: url, }, diff --git a/publish/publish.go b/publish/publish.go index bf12aff..546c4a8 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -7,6 +7,7 @@ import ( "github.com/calypr/forge/client/sower" "github.com/calypr/forge/utils/gitutil" + "github.com/calypr/git-drs/config" ) // This job name must match the sower config otherwise job won't start @@ -15,8 +16,8 @@ const SOURCE_GH_USER_ENDPOINT = "https://source.ohsu.edu/api/v3/user" const POD_PUT_METHOD = "put" const POD_DELETE_METHOD = "delete" -func RunEmpty(projectId string) (*sower.StatusResp, error) { - sc, err := sower.NewSowerClient() +func RunEmpty(projectId string, remote config.Remote) (*sower.StatusResp, error) { + sc, err := sower.NewSowerClient(remote) if err != nil { return nil, err } @@ -37,7 +38,7 @@ func RunEmpty(projectId string) (*sower.StatusResp, error) { return resp, nil } -func RunPublish(token string) (*sower.StatusResp, error) { +func RunPublish(token string, profile config.Remote) (*sower.StatusResp, error) { err := checkGHPAccessToken(token) if err != nil { return nil, err @@ -46,7 +47,7 @@ func RunPublish(token string) (*sower.StatusResp, error) { if err != nil { return nil, err } - remote, err := repo.Remote("origin") + remote, err := repo.Remote(string(profile)) if err != nil { return nil, fmt.Errorf("failed to get 'origin' remote: %w", err) } @@ -70,7 +71,7 @@ func RunPublish(token string) (*sower.StatusResp, error) { if err != nil { return nil, err } - sc, err := sower.NewSowerClient() + sc, err := sower.NewSowerClient(profile) if err != nil { return nil, err } diff --git a/schema/new.go b/schema/new.go index a21ebb9..7b00af8 100644 --- a/schema/new.go +++ b/schema/new.go @@ -9,21 +9,14 @@ import ( "github.com/bmeg/jsonschemagraph/compile" "github.com/bmeg/jsonschemagraph/graph" "github.com/bytedance/sonic" - "github.com/calypr/forge/client" "github.com/hashicorp/go-multierror" ) type Schema struct { Sch *graph.GraphSchema - Cli *client.Gen3Client } func NewSchema() (*Schema, error) { - cli, err := client.NewGen3Client() - if err != nil { - fmt.Println("THE ERR IS HERE1: ", err) - return nil, err - } compiler := jsonschema.NewCompiler() compiler.AssertVocabs() vc, err := compile.GetHyperMediaVocab() @@ -65,7 +58,6 @@ func NewSchema() (*Schema, error) { } return &Schema{ Sch: out, - Cli: cli, }, nil } From d9f37d160c456b65db8c4e86c0c6fd92a9d1cdd5 Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Thu, 4 Dec 2025 13:19:51 -0800 Subject: [PATCH 09/12] cleanup uuid gen --- cmd/cmd.go | 1 - cmd/initialize/main.go | 44 ------------------------------------------ cmd/meta/main.go | 8 ++++++-- cmd/ping/main.go | 2 +- cmd/publish/main.go | 1 + go.mod | 2 +- go.sum | 6 ++++++ metadata/directory.go | 11 ++++++----- metadata/meta.go | 30 +++++++++++++++------------- metadata/skeleton.go | 18 +++++++++-------- 10 files changed, 48 insertions(+), 75 deletions(-) delete mode 100644 cmd/initialize/main.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 227b995..394fbad 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -18,7 +18,6 @@ development and project management tasks.`, } func init() { - // RootCmd.AddCommand(initialize.InitCmd) comment this out for now RootCmd.AddCommand(ping.PingCmd) RootCmd.AddCommand(meta.MetaCmd) RootCmd.AddCommand(publish.PublishCmd) diff --git a/cmd/initialize/main.go b/cmd/initialize/main.go deleted file mode 100644 index 84002fe..0000000 --- a/cmd/initialize/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package initialize - -import ( - drsInit "github.com/calypr/git-drs/cmd/initialize" - "github.com/spf13/cobra" -) - -var ( - mode string - apiEndpoint string - bucket string - credFile string - fenceToken string - profile string - project string - terraProject string -) - -var InitCmd = &cobra.Command{ - Use: "init", - Short: "Initialize repo and server access for forge", - Long: "Description:" + - "\n Initialize repo and server access for git-drs with a gen3 server" + - "\n ~ Provide a url, bucket, profile, project ID, and either a credentials file or token", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - err := drsInit.Init("gen3", apiEndpoint, bucket, credFile, fenceToken, profile, project, terraProject) - if err != nil { - return err - } - return nil - }, -} - -func init() { - InitCmd.Flags().StringVar(&mode, "mode", "gen3", "Use an AnVIL-hosted DRS server rather than a gen3 one. Defaults to false") - InitCmd.Flags().StringVar(&apiEndpoint, "url", "", "[gen3] Specify the API endpoint of the data commons") - InitCmd.Flags().StringVar(&bucket, "bucket", "", "[gen3] Specify the bucket name") - InitCmd.Flags().StringVar(&credFile, "cred", "", "[gen3] Specify the gen3 credential file that you want to use") - InitCmd.Flags().StringVar(&fenceToken, "token", "", "[gen3] Specify the token to be used as a replacement for a credential file for temporary access") - InitCmd.Flags().StringVar(&profile, "profile", "", "[gen3] Specify the gen3 profile to use") - InitCmd.Flags().StringVar(&project, "project", "", "[gen3] Specify the gen3 project ID in the format -") - InitCmd.Flags().StringVar(&terraProject, "terraProject", "", "[AnVIL] Specify the Terra project ID") -} diff --git a/cmd/meta/main.go b/cmd/meta/main.go index 7e466ef..0b2c8d9 100644 --- a/cmd/meta/main.go +++ b/cmd/meta/main.go @@ -7,7 +7,6 @@ import ( ) var outPath string - var MetaCmd = &cobra.Command{ Use: "meta", Short: "Autogenerate metadata based off of files that have been uploaded", @@ -15,7 +14,12 @@ var MetaCmd = &cobra.Command{ Example: "forge meta [remote]", Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { - err := metadata.RunMetaInit(outPath, config.Remote(args[0])) + var remoteName string = "origin" + if len(args) > 0 { + remoteName = args[0] + } + + err := metadata.RunMetaInit(outPath, config.Remote(remoteName)) if err != nil { return err } diff --git a/cmd/ping/main.go b/cmd/ping/main.go index d27949b..5ce07f3 100644 --- a/cmd/ping/main.go +++ b/cmd/ping/main.go @@ -18,7 +18,7 @@ var PingCmd = &cobra.Command{ if len(args) > 0 { remote = config.Remote(args[0]) } else { - remote = config.Remote("") + remote = config.Remote("origin") } FenceClient, err := fence.NewFenceClient(remote) diff --git a/cmd/publish/main.go b/cmd/publish/main.go index 75ae992..cb48162 100644 --- a/cmd/publish/main.go +++ b/cmd/publish/main.go @@ -36,6 +36,7 @@ var ListCmd = &cobra.Command{ Use: "list [remote]", Short: "view all of the jobs currently catalogued in sower", Long: `The 'list' command is how jobs are displayed to the user`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { sc, err := sower.NewSowerClient(config.Remote(args[0])) if err != nil { diff --git a/go.mod b/go.mod index f82f73c..c6c96e9 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/bytedance/sonic v1.14.0 github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 - github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0 + github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff diff --git a/go.sum b/go.sum index 29ebe68..249f258 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,12 @@ github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 h1:ii28j/rzbOaxpUVnON github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2/go.mod h1:SJfHDSOaN5xevCor2PVRZxHHJ/WwfAnBBddgnQcUwJo= github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0 h1:N/UtHeCY9neficWtSsNBu0NMaaSx62CwAsfShadEEtM= github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0/go.mod h1:yosz5xq1jz5+qDoQiUtooz6+RYH8SNQu0WkPs8QQd2I= +github.com/calypr/git-drs v0.0.0-20251202195314-ff143b46d69b h1:oIpg0qd+DQxBY56Pdphxxn2S+abr4UIW9HHsLIcqbeE= +github.com/calypr/git-drs v0.0.0-20251202195314-ff143b46d69b/go.mod h1:yosz5xq1jz5+qDoQiUtooz6+RYH8SNQu0WkPs8QQd2I= +github.com/calypr/git-drs v0.0.0-20251203183852-4efaabddd09f h1:mViDzHVYpjJc1v9P8E97wMwiCkAJtgFP2X07VLgK2qo= +github.com/calypr/git-drs v0.0.0-20251203183852-4efaabddd09f/go.mod h1:fnhdZZnFjI89vxWA9IzFjwTXWCHEMm0m75wCYUSOjB8= +github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947 h1:/sZ24FW+H53ed1BYmdZpQO/auAgjALCLMsY0N7MFrRw= +github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947/go.mod h1:fnhdZZnFjI89vxWA9IzFjwTXWCHEMm0m75wCYUSOjB8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= diff --git a/metadata/directory.go b/metadata/directory.go index dade860..7ce73a6 100644 --- a/metadata/directory.go +++ b/metadata/directory.go @@ -66,7 +66,7 @@ func extractReferenceString(ref *dtpb.Reference) (string, error) { // EnsureDirectoryPathExists recursively checks and creates all parent directories // for a given POSIX path (e.g., "/a/b/c"). It returns the Directory // object for the target path. -func EnsureDirectoryPathExists(posixPath string) *Directory { +func EnsureDirectoryPathExists(endpoint string, posixPath string) *Directory { // Clean and normalize the path to standard POSIX separators (/) cleanPath := filepath.Clean(posixPath) // Use the clean path as the unique key in our cache. @@ -79,7 +79,7 @@ func EnsureDirectoryPathExists(posixPath string) *Directory { return dir } - dirUUID := uuid.NewSHA1(DirectoryNamespaceUUID, []byte(cleanPath)).String() + dirUUID := uuid.NewSHA1(uuid.NewSHA1(uuid.NameSpaceDNS, []byte(endpoint)), []byte(cleanPath)).String() // Base case: Handle the root path "/" if cleanPath == "/" { DirectoryCache[cleanPath] = &Directory{ @@ -93,7 +93,7 @@ func EnsureDirectoryPathExists(posixPath string) *Directory { // Determine the parent path and recursively ensure it exists dirName := filepath.Base(cleanPath) parentPath := filepath.Dir(cleanPath) - parentDir := EnsureDirectoryPathExists(parentPath) + parentDir := EnsureDirectoryPathExists(endpoint, parentPath) // Create the current Directory object with its deterministic UUID currentDir := &Directory{ @@ -130,7 +130,7 @@ func EnsureDirectoryPathExists(posixPath string) *Directory { // BuildDirectoryTreeFromDocRef extracts the path from a DocumentReference and builds the tree. // It ensures all necessary Directory nodes are created and linked in the global DirectoryCache. -func BuildDirectoryTreeFromDocRef(docRef *drpb.DocumentReference) { +func BuildDirectoryTreeFromDocRef(endpoint string, docRef *drpb.DocumentReference) { if len(docRef.Content) == 0 || docRef.Content[0].GetAttachment().GetUrl().GetValue() == "" { log.Println("DocumentReference missing URL attachment.") return @@ -147,7 +147,7 @@ func BuildDirectoryTreeFromDocRef(docRef *drpb.DocumentReference) { dirPath := filepath.Dir(posixPath) // Recursively create all directories up to the file's parent - parentDir := EnsureDirectoryPathExists(dirPath) + parentDir := EnsureDirectoryPathExists(endpoint, dirPath) if parentDir != nil { docRefID := docRef.GetId().GetValue() @@ -166,6 +166,7 @@ func BuildDirectoryTreeFromDocRef(docRef *drpb.DocumentReference) { } if !isAlreadyLinked { + fmt.Println("HELLO WE HERE") parentDir.Child = append(parentDir.Child, fileRef) } } diff --git a/metadata/meta.go b/metadata/meta.go index 1b0b2b8..cbdcfff 100644 --- a/metadata/meta.go +++ b/metadata/meta.go @@ -14,6 +14,7 @@ import ( indexd_client "github.com/calypr/git-drs/client/indexd" "github.com/calypr/git-drs/config" "github.com/calypr/git-drs/drs" + "github.com/calypr/git-drs/drslog" fver "github.com/google/fhir/go/fhirversion" "github.com/google/fhir/go/jsonformat" code "github.com/google/fhir/go/proto/google/fhir/proto/r5/core/codes_go_proto" @@ -63,7 +64,12 @@ func RunMetaInit(outPath string, remote config.Remote) error { return err } - val, err := cfg.GetCurrentRemoteClient(nil) + logger, err := drslog.NewLogger("", true) + if err != nil { + return err + } + + val, err := cfg.GetRemoteClient(remote, logger) if err != nil { return err } @@ -119,12 +125,12 @@ func RunMetaInit(outPath string, remote config.Remote) error { } // getOrCreateRootDirectory ensures the root directory ("/") exists in DirectoryCache -func getOrCreateRootDirectory() *Directory { +func getOrCreateRootDirectory(endpoint string) *Directory { cleanPath := "/" if dir, ok := DirectoryCache[cleanPath]; ok { return dir } - dirUUID := uuid.NewSHA1(DirectoryNamespaceUUID, []byte(cleanPath)).String() + dirUUID := uuid.NewSHA1(uuid.NewSHA1(uuid.NameSpaceDNS, []byte(endpoint)), []byte(cleanPath)).String() newDir := &Directory{ Name: "/", Id: dirUUID, @@ -183,7 +189,7 @@ func getResearchStudy(fhirDirectory string, projectId string, endpoint string, m if err2 := json.Unmarshal(unmarshalBytes, &tmp); err2 == nil { if idv, ok := tmp["id"].(string); ok && idv != "" { // we have an ID but couldn't unmarshal via fhir unmarshaller; still inject rootDir - rootDir := getOrCreateRootDirectory() + rootDir := getOrCreateRootDirectory(endpoint) newBytes, injErr := injectRootDir(unmarshalBytes, "Directory/"+rootDir.Id) if injErr != nil { return "", injErr @@ -201,7 +207,7 @@ func getResearchStudy(fhirDirectory string, projectId string, endpoint string, m fmt.Printf("Loaded existing ResearchStudy from %s with ID %s\n", rsPath, rsID) // inject or update rootDir in the existing JSON and write back - rootDir := getOrCreateRootDirectory() + rootDir := getOrCreateRootDirectory(endpoint) newFirstLineBytes, err := injectRootDir(unmarshalBytes, "Directory/"+rootDir.Id) if err != nil { return "", fmt.Errorf("failed to inject rootDir into existing ResearchStudy: %v", err) @@ -218,10 +224,10 @@ func getResearchStudy(fhirDirectory string, projectId string, endpoint string, m } // File does not exist: create a new ResearchStudy contained resource and inject rootDir - id := createIDFromStrings(endpoint, RESEARCH_STUDY, projectId, projectId) + id := createIDFromStrings(endpoint, RESEARCH_STUDY, projectId) // Ensure root directory exists - rootDir := getOrCreateRootDirectory() + rootDir := getOrCreateRootDirectory(endpoint) rs := &rspb.ResearchStudy{ Id: &dtpb.Id{Value: id}, @@ -352,16 +358,14 @@ func processDRSRecordsAndUpdateFHIR(drsRecords []*drs.DRSObject, LfsRecords []LF foundMatch := false containedResource := &cprb.ContainedResource{} for _, drsRecord := range drsRecords { - found := false for _, sum := range drsRecord.Checksums { - if rec.OID == sum.Checksum { + if sum.Type == drs.ChecksumTypeSHA256 && rec.OID == sum.Checksum { drsRecord.Name = rec.Name foundMatch = true containedResource = templateDocRef(drsRecord, endpoint, project, researchStudyID) - found = true } } - if found == true { + if foundMatch == true { break } } @@ -373,9 +377,8 @@ func processDRSRecordsAndUpdateFHIR(drsRecords []*drs.DRSObject, LfsRecords []LF fhirRecord := containedResource.GetDocumentReference() recordID := fhirRecord.GetId().GetValue() - BuildDirectoryTreeFromDocRef(fhirRecord) + BuildDirectoryTreeFromDocRef(endpoint, fhirRecord) if existing := existingFHIRRecords[recordID].GetDocumentReference(); existing != nil { - // ... (Existing DocumentReference update/merge logic) ... existing.Status = fhirRecord.Status existing.DocStatus = fhirRecord.DocStatus existing.Date = fhirRecord.Date @@ -396,6 +399,7 @@ func processDRSRecordsAndUpdateFHIR(drsRecords []*drs.DRSObject, LfsRecords []LF existingFHIRRecords[recordID] = containedResource } } + log.Printf("Processed %d records", count) docRefFile, err := os.Create(docRefFP) diff --git a/metadata/skeleton.go b/metadata/skeleton.go index 462d9db..bcf52a0 100644 --- a/metadata/skeleton.go +++ b/metadata/skeleton.go @@ -21,8 +21,6 @@ const ( DIR_ID_PREFIX = DIRECTORY_RESOURCE + "/" ) -var DirectoryNamespaceUUID = uuid.MustParse("e61f8f3c-8f2c-4b5c-8d19-9f7918f8f483") - func CreateDocReferenceReference(resourceId string) *dtpb.Reference { return &dtpb.Reference{ Reference: &dtpb.Reference_DocumentReferenceId{ @@ -45,6 +43,11 @@ func CreateResourceReference(resourceId string) *dtpb.Reference { } func templateDocRef(obj *drs.DRSObject, endpoint string, project string, rSID string) *cprb.ContainedResource { + + id := uuid.NewSHA1( + uuid.NewSHA1(uuid.NameSpaceDNS, []byte(endpoint)), + fmt.Appendf(nil, "%s/%s", project, obj.Name)).String() + // Create the extensions for hashes var extensions []*dtpb.Extension for _, sum := range obj.Checksums { @@ -75,7 +78,7 @@ func templateDocRef(obj *drs.DRSObject, endpoint string, project string, rSID st // Create the DocumentReference dr := &drpb.DocumentReference{ - Id: &dtpb.Id{Value: obj.Id}, + Id: &dtpb.Id{Value: id}, Status: &drpb.DocumentReference_StatusCode{Value: code.DocumentReferenceStatusCode_CURRENT}, DocStatus: &drpb.DocumentReference_DocStatusCode{Value: code.CompositionStatusCode_FINAL}, Date: parseFHIRInstantString(obj.CreatedTime), @@ -113,16 +116,15 @@ func templateDocRef(obj *drs.DRSObject, endpoint string, project string, rSID st } } -func createIDFromStrings(apiEndpoint string, resourceType string, projectID string, identifierString string) string { +func createIDFromStrings(apiEndpoint string, resourceType string, projectID string) string { // Use uuid.Nil as the namespace and the apiEndpoint string to create a consistent namespace UUID. // This is necessary because `uuid.NewSHA1` requires a UUID namespace, not a string. - endpointNamespace := uuid.NewSHA1(uuid.Nil, []byte(apiEndpoint)) + endpointNamespace := uuid.NewSHA1(uuid.NameSpaceDNS, []byte(apiEndpoint)) - system := getSystem(apiEndpoint, identifierString, projectID) - nameString := fmt.Sprintf("%s/%s/%s|%s", projectID, resourceType, system, identifierString) + system := getSystem(apiEndpoint, resourceType, projectID) // Generate the final UUID using the endpoint-specific namespace. - return uuid.NewSHA1(endpointNamespace, []byte(nameString)).String() + return uuid.NewSHA1(endpointNamespace, fmt.Appendf(nil, "%s/%s", projectID, system)).String() } func getSystem(apiEndpoint string, identifier string, projectID string) string { From de1c80ef07587dbce2954eeca4a3b7a673623f8b Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Fri, 5 Dec 2025 09:26:49 -0800 Subject: [PATCH 10/12] upgrade deps --- go.mod | 4 ++-- go.sum | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c6c96e9..23d0e97 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,9 @@ require ( github.com/bmeg/jsonschema/v6 v6.0.4 github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c github.com/bytedance/sonic v1.14.0 - github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 + github.com/calypr/data-client v0.0.0-20251205171713-e01d41f735e9 github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 - github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947 + github.com/calypr/git-drs v0.0.0-20251205172356-279c16aaf4ce github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff diff --git a/go.sum b/go.sum index 249f258..190220e 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZw github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 h1:NVO5FpugLr+6rhTS/er1B2Ui8RIykbFL+IJcOXDdqBo= github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= +github.com/calypr/data-client v0.0.0-20251205171713-e01d41f735e9 h1:yHbDu0w/Ufu5TZ3WaMKL2zyI0mt5lydneg7Ob0ATCh8= +github.com/calypr/data-client v0.0.0-20251205171713-e01d41f735e9/go.mod h1:mFUXQQnA/kwCtiZWcD/X1suecRAdmOaaYHFwD79KQ7w= github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 h1:ii28j/rzbOaxpUVnONEMmDm7FyZy03qBcHFFSNRsCbQ= github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2/go.mod h1:SJfHDSOaN5xevCor2PVRZxHHJ/WwfAnBBddgnQcUwJo= github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0 h1:N/UtHeCY9neficWtSsNBu0NMaaSx62CwAsfShadEEtM= @@ -81,6 +83,8 @@ github.com/calypr/git-drs v0.0.0-20251203183852-4efaabddd09f h1:mViDzHVYpjJc1v9P github.com/calypr/git-drs v0.0.0-20251203183852-4efaabddd09f/go.mod h1:fnhdZZnFjI89vxWA9IzFjwTXWCHEMm0m75wCYUSOjB8= github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947 h1:/sZ24FW+H53ed1BYmdZpQO/auAgjALCLMsY0N7MFrRw= github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947/go.mod h1:fnhdZZnFjI89vxWA9IzFjwTXWCHEMm0m75wCYUSOjB8= +github.com/calypr/git-drs v0.0.0-20251205172356-279c16aaf4ce h1:p1yuPP2gEmOJJa/IrA5z2uoyUZQzyZAWazemipmN+l4= +github.com/calypr/git-drs v0.0.0-20251205172356-279c16aaf4ce/go.mod h1:RLI/HuXG5v1tKYj4R43oqG4sQPNJ3lT0XGu98Q2zV0I= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= From 358d1fb6ec325f913bfd35a643885525fb01342f Mon Sep 17 00:00:00 2001 From: matthewpeterkort Date: Tue, 30 Dec 2025 11:44:58 -0800 Subject: [PATCH 11/12] upgrade deps --- client/client.go | 32 ++++++------- client/fence/fence.go | 12 ++--- client/sower/sower.go | 8 ++-- cmd/ping/main.go | 4 +- cmd/publish/main.go | 11 +++-- go.mod | 35 ++++++++------- go.sum | 102 +++++++++++++++++++----------------------- metadata/meta.go | 11 +++-- metadata/skeleton.go | 39 +++++++--------- publish/publish.go | 7 ++- 10 files changed, 129 insertions(+), 132 deletions(-) diff --git a/client/client.go b/client/client.go index 0527778..3f699a5 100644 --- a/client/client.go +++ b/client/client.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "context" "fmt" "io" "net/http" @@ -13,50 +14,52 @@ import ( "github.com/calypr/git-drs/config" drsConfig "github.com/calypr/git-drs/config" - "github.com/calypr/data-client/client/commonUtils" - "github.com/calypr/data-client/client/jwt" + "github.com/calypr/data-client/client/conf" + "github.com/calypr/data-client/client/logs" + index_client "github.com/calypr/git-drs/client/indexd" ) type Gen3Client struct { Base *url.URL - Cred jwt.Credential + Cred *conf.Credential ProjectId string BucketName string } // load repo-level config and return a new IndexDClient -func NewGen3Client(remote config.Remote) (*Gen3Client, error) { - var conf jwt.Configure +func NewGen3Client(remote config.Remote) (*Gen3Client, func(), error) { cfg, err := drsConfig.LoadConfig() if err != nil { - return nil, err + return nil, nil, err } gfc, ok := cfg.Remotes[remote] if !ok { - return nil, fmt.Errorf("remote %s not found in config: %v", remote, cfg.Remotes) + return nil, nil, fmt.Errorf("remote %s not found in config: %v", remote, cfg.Remotes) } - cred, err := conf.ParseConfig(string(remote)) + logger, closer := logs.New(string(remote)) + + cred, err := conf.NewConfigure(logger).Load(string(remote)) if err != nil { - return nil, err + return nil, closer, err } baseUrl, err := url.Parse(cred.APIEndpoint) if err != nil { - return nil, fmt.Errorf("error parsing base URL from profile %s: %v", remote, err) + return nil, closer, fmt.Errorf("error parsing base URL from profile %s: %v", remote, err) } // get the gen3Project and gen3Bucket from the config projectId := gfc.Gen3.ProjectID if projectId == "" { - return nil, fmt.Errorf("No gen3 project specified. Please provide a gen3Project key in your .drsconfig") + return nil, closer, fmt.Errorf("No gen3 project specified. Please provide a gen3Project key in your .drsconfig") } bucketName := gfc.Gen3.Bucket if bucketName == "" { - return nil, fmt.Errorf("No gen3 bucket specified. Please provide a gen3Bucket key in your .drsconfig") + return nil, closer, fmt.Errorf("No gen3 bucket specified. Please provide a gen3Bucket key in your .drsconfig") } - return &Gen3Client{Base: baseUrl, Cred: cred, ProjectId: projectId, BucketName: bucketName}, err + return &Gen3Client{Base: baseUrl, Cred: cred, ProjectId: projectId, BucketName: bucketName}, closer, err } type Resp struct { @@ -90,8 +93,7 @@ func (cl *Gen3Client) MakeReq(method string, path string, body []byte, params ma } // Update AccessToken if token is old if expiration.Before(time.Now()) { - r := jwt.Request{} - err := r.RequestNewAccessToken(cl.Base.String()+commonUtils.FenceAccessTokenEndpoint, &cl.Cred) + err := index_client.RefreshToken(context.Background(), cl.Cred) if err != nil { return &Resp{nil, err} } diff --git a/client/fence/fence.go b/client/fence/fence.go index f9d7cc2..47b9005 100644 --- a/client/fence/fence.go +++ b/client/fence/fence.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "github.com/calypr/data-client/client/commonUtils" + "github.com/calypr/data-client/client/common" "github.com/calypr/forge/client" "github.com/calypr/git-drs/config" ) @@ -32,12 +32,12 @@ func UserPing(f Fence) (*PingResp, error) { return f.UserPing() } -func NewFenceClient(remote config.Remote) (*FenceClient, error) { - gen3Client, err := client.NewGen3Client(remote) +func NewFenceClient(remote config.Remote) (*FenceClient, func(), error) { + gen3Client, closer, err := client.NewGen3Client(remote) if err != nil { - return nil, err + return nil, closer, err } - return &FenceClient{Gen3Client: gen3Client}, nil + return &FenceClient{Gen3Client: gen3Client}, closer, nil } type FenceClient struct { @@ -45,7 +45,7 @@ type FenceClient struct { } func (fc *FenceClient) UserPing() (*PingResp, error) { - reqResp := fc.MakeReq(http.MethodGet, commonUtils.FenceUserEndpoint, nil, nil) + reqResp := fc.MakeReq(http.MethodGet, common.FenceUserEndpoint, nil, nil) if reqResp.Body == nil || reqResp.Err != nil { return nil, reqResp.Err } diff --git a/client/sower/sower.go b/client/sower/sower.go index 47b0743..122e12e 100644 --- a/client/sower/sower.go +++ b/client/sower/sower.go @@ -24,12 +24,12 @@ func DispatchJob(s Sower, name string, args *DispatchArgs) (*StatusResp, error) return s.DispatchJob(name, args) } -func NewSowerClient(remote config.Remote) (*SowerClient, error) { - gen3Client, err := client.NewGen3Client(remote) +func NewSowerClient(remote config.Remote) (*SowerClient, func(), error) { + gen3Client, closer, err := client.NewGen3Client(remote) if err != nil { - return nil, err + return nil, closer, err } - return &SowerClient{Gen3Client: gen3Client}, nil + return &SowerClient{Gen3Client: gen3Client}, closer, nil } type SowerClient struct { diff --git a/cmd/ping/main.go b/cmd/ping/main.go index 5ce07f3..8a61f39 100644 --- a/cmd/ping/main.go +++ b/cmd/ping/main.go @@ -21,10 +21,12 @@ var PingCmd = &cobra.Command{ remote = config.Remote("origin") } - FenceClient, err := fence.NewFenceClient(remote) + FenceClient, closer, err := fence.NewFenceClient(remote) if err != nil { return err } + defer closer() + resp, err := FenceClient.UserPing() if err != nil { return err diff --git a/cmd/publish/main.go b/cmd/publish/main.go index cb48162..0609539 100644 --- a/cmd/publish/main.go +++ b/cmd/publish/main.go @@ -38,10 +38,11 @@ var ListCmd = &cobra.Command{ Long: `The 'list' command is how jobs are displayed to the user`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - sc, err := sower.NewSowerClient(config.Remote(args[0])) + sc, closer, err := sower.NewSowerClient(config.Remote(args[0])) if err != nil { return err } + defer closer() vals, err := sc.List() if len(vals) == 0 { fmt.Printf("There are no jobs to list: %s\n", vals) @@ -69,10 +70,12 @@ var StatusCmd = &cobra.Command{ remote = config.Remote("") fmt.Printf("Using default remote: %s\n", remote) } - sc, err := sower.NewSowerClient(remote) + sc, closer, err := sower.NewSowerClient(remote) if err != nil { return err } + defer closer() + status, err := sc.Status(args[0]) if err != nil { return err @@ -97,10 +100,12 @@ var OutputCmd = &cobra.Command{ remote = config.Remote("") fmt.Printf("Using default remote: %s\n", remote) } - sc, err := sower.NewSowerClient(remote) + sc, closer, err := sower.NewSowerClient(remote) if err != nil { return err } + defer closer() + output, err := sc.Output(args[0]) if err != nil { return err diff --git a/go.mod b/go.mod index 23d0e97..5aadb6b 100644 --- a/go.mod +++ b/go.mod @@ -8,25 +8,27 @@ require ( github.com/bmeg/grip-graphql v0.0.0-20251106183540-8b2f286248b3 github.com/bmeg/jsonschema/v6 v6.0.4 github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c - github.com/bytedance/sonic v1.14.0 - github.com/calypr/data-client v0.0.0-20251205171713-e01d41f735e9 + github.com/bytedance/sonic v1.14.2 + github.com/calypr/data-client v0.0.0-20251230165452-a95f44240b14 github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 - github.com/calypr/git-drs v0.0.0-20251205172356-279c16aaf4ce + github.com/calypr/git-drs v0.0.0-20251230165948-81a82438caa4 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 gopkg.in/yaml.v2 v2.4.0 ) require ( bitbucket.org/creachadair/stringset v0.0.9 // indirect - cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/VividCortex/ewma v1.2.0 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/aws/aws-sdk-go-v2 v1.39.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.12 // indirect @@ -45,10 +47,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect github.com/aws/smithy-go v1.23.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/clipperhouse/uax29/v2 v2.2.0 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cloudwego/base64x v0.1.5 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect @@ -58,27 +62,25 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/fhir/go/protopath v0.7.4 // indirect - github.com/google/go-github v17.0.0+incompatible // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/mattn/go-runewidth v0.0.19 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect @@ -89,22 +91,21 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/vbauerster/mpb/v8 v8.11.2 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sys v0.37.0 // indirect + golang.org/x/sys v0.39.0 // indirect golang.org/x/term v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect google.golang.org/grpc v1.71.0 // indirect google.golang.org/protobuf v1.36.10 // indirect - gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 190220e..fc5f871 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ bitbucket.org/creachadair/stringset v0.0.9 h1:L4vld9nzPt90UZNrXjNelTshD74ps4P5NGs3Iq6yN3o= bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -11,6 +11,10 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= +github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -64,36 +68,28 @@ github.com/bmeg/jsonschema/v6 v6.0.4 h1:AXFAz7G05VZkKretSSU+uacMKF8+C16ONG6pzFzz github.com/bmeg/jsonschema/v6 v6.0.4/go.mod h1:gTh32doM+BEZyi/TDPJEp8k3qXTckXY4ohptV2xExQY= github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c h1:J1EhcEmL1D/YHxoMIw4HeeVv+hRMUFezNlAAlFX/a8M= github.com/bmeg/jsonschemagraph v0.0.4-0.20251017205345-236d2de9887c/go.mod h1:Ve7jAQhYAMkHUiko99+2CwqXI4Ur0ty/ai8Tfa2ONz4= -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7 h1:NVO5FpugLr+6rhTS/er1B2Ui8RIykbFL+IJcOXDdqBo= -github.com/calypr/data-client v0.0.0-20251103160310-cc0ca9939fe7/go.mod h1:w0FrIP2TNqsBJcHUm3M04/5ZKKX7MHKRrYlYDU/A4sI= -github.com/calypr/data-client v0.0.0-20251205171713-e01d41f735e9 h1:yHbDu0w/Ufu5TZ3WaMKL2zyI0mt5lydneg7Ob0ATCh8= -github.com/calypr/data-client v0.0.0-20251205171713-e01d41f735e9/go.mod h1:mFUXQQnA/kwCtiZWcD/X1suecRAdmOaaYHFwD79KQ7w= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/calypr/data-client v0.0.0-20251230165452-a95f44240b14 h1:8JVrJ3zPT27N5lA2QIBpQeCcll4FFeueuXEfGdmAkKI= +github.com/calypr/data-client v0.0.0-20251230165452-a95f44240b14/go.mod h1:/GPTQWCRSZpNPoojP/dUsNlqQa4bykRlTnrJRebwqQ4= github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 h1:ii28j/rzbOaxpUVnONEMmDm7FyZy03qBcHFFSNRsCbQ= github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2/go.mod h1:SJfHDSOaN5xevCor2PVRZxHHJ/WwfAnBBddgnQcUwJo= -github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0 h1:N/UtHeCY9neficWtSsNBu0NMaaSx62CwAsfShadEEtM= -github.com/calypr/git-drs v0.0.0-20251201212931-377c483d9fc0/go.mod h1:yosz5xq1jz5+qDoQiUtooz6+RYH8SNQu0WkPs8QQd2I= -github.com/calypr/git-drs v0.0.0-20251202195314-ff143b46d69b h1:oIpg0qd+DQxBY56Pdphxxn2S+abr4UIW9HHsLIcqbeE= -github.com/calypr/git-drs v0.0.0-20251202195314-ff143b46d69b/go.mod h1:yosz5xq1jz5+qDoQiUtooz6+RYH8SNQu0WkPs8QQd2I= -github.com/calypr/git-drs v0.0.0-20251203183852-4efaabddd09f h1:mViDzHVYpjJc1v9P8E97wMwiCkAJtgFP2X07VLgK2qo= -github.com/calypr/git-drs v0.0.0-20251203183852-4efaabddd09f/go.mod h1:fnhdZZnFjI89vxWA9IzFjwTXWCHEMm0m75wCYUSOjB8= -github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947 h1:/sZ24FW+H53ed1BYmdZpQO/auAgjALCLMsY0N7MFrRw= -github.com/calypr/git-drs v0.0.0-20251204172911-d6f800cc6947/go.mod h1:fnhdZZnFjI89vxWA9IzFjwTXWCHEMm0m75wCYUSOjB8= -github.com/calypr/git-drs v0.0.0-20251205172356-279c16aaf4ce h1:p1yuPP2gEmOJJa/IrA5z2uoyUZQzyZAWazemipmN+l4= -github.com/calypr/git-drs v0.0.0-20251205172356-279c16aaf4ce/go.mod h1:RLI/HuXG5v1tKYj4R43oqG4sQPNJ3lT0XGu98Q2zV0I= +github.com/calypr/git-drs v0.0.0-20251230165948-81a82438caa4 h1:AWs5hOg6VODWhQ/S9frW+6nz476/fr/9s+nqCJRDKvA= +github.com/calypr/git-drs v0.0.0-20251230165948-81a82438caa4/go.mod h1:irPfPmQ5YCcK+GIRvjTqoU+znTL3xs8d/Ex9oX1Xriw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= -github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= @@ -119,8 +115,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k= @@ -146,8 +142,6 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -164,13 +158,8 @@ github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff/go.mod h1:fAaldB6 github.com/google/fhir/go/protopath v0.7.4 h1:UnSxLhWaj0S2LwDmwEaoEkgtHIrRSwE2LiezXPbVuxI= github.com/google/fhir/go/protopath v0.7.4/go.mod h1:HbcIpajWRTTeeSSi7maEzVBX9Wiq+CpUm1P3/dUXIUg= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -181,10 +170,16 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1ns github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -195,10 +190,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -209,14 +202,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -254,8 +245,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -263,19 +254,21 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e h1:IWllFTiDjjLIf2oeKxpIUmtiDV5sn71VgeQgg6vcE7k= -github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/vbauerster/mpb/v8 v8.11.2 h1:OqLoHznUVU7SKS/WV+1dB5/hm20YLheYupiHhL5+M1Y= +github.com/vbauerster/mpb/v8 v8.11.2/go.mod h1:mEB/M353al1a7wMUNtiymmPsEkGlJgeJmtlbY5adCJ8= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -296,6 +289,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -344,9 +338,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= @@ -391,8 +384,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= -gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -409,4 +400,3 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/metadata/meta.go b/metadata/meta.go index cbdcfff..f5de33a 100644 --- a/metadata/meta.go +++ b/metadata/meta.go @@ -358,13 +358,12 @@ func processDRSRecordsAndUpdateFHIR(drsRecords []*drs.DRSObject, LfsRecords []LF foundMatch := false containedResource := &cprb.ContainedResource{} for _, drsRecord := range drsRecords { - for _, sum := range drsRecord.Checksums { - if sum.Type == drs.ChecksumTypeSHA256 && rec.OID == sum.Checksum { - drsRecord.Name = rec.Name - foundMatch = true - containedResource = templateDocRef(drsRecord, endpoint, project, researchStudyID) - } + if drsRecord.Checksums.SHA256 == rec.OID { + drsRecord.Name = rec.Name + foundMatch = true + containedResource = templateDocRef(drsRecord, endpoint, project, researchStudyID) } + if foundMatch == true { break } diff --git a/metadata/skeleton.go b/metadata/skeleton.go index bcf52a0..e271a72 100644 --- a/metadata/skeleton.go +++ b/metadata/skeleton.go @@ -43,40 +43,35 @@ func CreateResourceReference(resourceId string) *dtpb.Reference { } func templateDocRef(obj *drs.DRSObject, endpoint string, project string, rSID string) *cprb.ContainedResource { - id := uuid.NewSHA1( uuid.NewSHA1(uuid.NameSpaceDNS, []byte(endpoint)), - fmt.Appendf(nil, "%s/%s", project, obj.Name)).String() + fmt.Appendf(nil, "%s/%s", project, obj.Name), + ).String() - // Create the extensions for hashes var extensions []*dtpb.Extension - for _, sum := range obj.Checksums { - if sum.Type == drs.ChecksumTypeMD5 { - extensions = append(extensions, &dtpb.Extension{ - Url: &dtpb.Uri{Value: endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-md5"}, - Value: &dtpb.Extension_ValueX{ - Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: sum.Checksum}}, - }, - }) - } - if sum.Type == drs.ChecksumTypeSHA256 { - extensions = append(extensions, &dtpb.Extension{ - Url: &dtpb.Uri{Value: "http://" + endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-sha256"}, - Value: &dtpb.Extension_ValueX{ - Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: sum.Checksum}}, - }, - }) - } + if obj.Checksums.MD5 != "" { + extensions = append(extensions, &dtpb.Extension{ + Url: &dtpb.Uri{Value: endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-md5"}, + Value: &dtpb.Extension_ValueX{ + Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: obj.Checksums.MD5}}, + }, + }) + } + if obj.Checksums.SHA256 != "" { + extensions = append(extensions, &dtpb.Extension{ + Url: &dtpb.Uri{Value: "http://" + endpoint + FHIR_STRUCTURE_DEFINITION + "/checksum-sha256"}, + Value: &dtpb.Extension_ValueX{ + Choice: &dtpb.Extension_ValueX_StringValue{StringValue: &dtpb.String{Value: obj.Checksums.SHA256}}, + }, + }) } - // Determine the URL for the attachment var url *dtpb.Url if len(obj.AccessMethods) > 0 { // TODO: Big assumption here assuming that there exists only one url per FHIR attachment url = &dtpb.Url{Value: obj.AccessMethods[0].AccessURL.URL} } - // Create the DocumentReference dr := &drpb.DocumentReference{ Id: &dtpb.Id{Value: id}, Status: &drpb.DocumentReference_StatusCode{Value: code.DocumentReferenceStatusCode_CURRENT}, diff --git a/publish/publish.go b/publish/publish.go index 546c4a8..432afdc 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -17,10 +17,12 @@ const POD_PUT_METHOD = "put" const POD_DELETE_METHOD = "delete" func RunEmpty(projectId string, remote config.Remote) (*sower.StatusResp, error) { - sc, err := sower.NewSowerClient(remote) + sc, closer, err := sower.NewSowerClient(remote) if err != nil { return nil, err } + defer closer() + dispatchArgs := &sower.DispatchArgs{ ProjectId: sc.ProjectId, APIEndpoint: sc.Cred.APIEndpoint, @@ -71,10 +73,11 @@ func RunPublish(token string, profile config.Remote) (*sower.StatusResp, error) if err != nil { return nil, err } - sc, err := sower.NewSowerClient(profile) + sc, closer, err := sower.NewSowerClient(profile) if err != nil { return nil, err } + defer closer() dispatchArgs := &sower.DispatchArgs{ BucketName: sc.BucketName, From 84cc9ed6f56372d3e9eb6c729be3f1da15c0ed1d Mon Sep 17 00:00:00 2001 From: Quinn Wai Wong <54592956+quinnwai@users.noreply.github.com> Date: Fri, 9 Jan 2026 01:10:47 +0800 Subject: [PATCH 12/12] Bugfix/git drs updates (#11) * update to recent git-drs * update all cmds to use default_remote * hide unneeded completion cmd * add untracked stuff --- README.md | 4 +- cmd/cmd.go | 3 ++ cmd/config/main.go | 29 +++++++---- cmd/empty/main.go | 29 +++++++---- cmd/meta/main.go | 27 +++++++--- cmd/ping/main.go | 24 ++++++--- cmd/publish/main.go | 93 ++++++++++++++++++++++------------ go.mod | 2 +- go.sum | 2 + metadata/meta.go | 2 +- publish/publish.go | 3 +- utils/remoteutil/remoteutil.go | 22 ++++++++ 12 files changed, 167 insertions(+), 73 deletions(-) create mode 100644 utils/remoteutil/remoteutil.go diff --git a/README.md b/README.md index fdb14ad..9ccf2f6 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ This repo is designed to produce git hook commands that take care of metadata ad ``` git clone repo -forge init -- exactly same as git-dirs init, just a wrapper around it +forge init -- exactly same as git-drs init, just a wrapper around it git add files git commit -m "test" -- same as git-drs -git push origin main -- same as git-dirs +git push origin main -- same as git-drs forge publish [github personal access token] ``` diff --git a/cmd/cmd.go b/cmd/cmd.go index 394fbad..51240db 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -15,6 +15,9 @@ var RootCmd = &cobra.Command{ Short: "A powerful command-line tool for project management.", Long: `Forge is a versatile CLI application designed to streamline various development and project management tasks.`, + CompletionOptions: cobra.CompletionOptions{ + DisableDefaultCmd: true, + }, } func init() { diff --git a/cmd/config/main.go b/cmd/config/main.go index 90367e0..1c90e6c 100644 --- a/cmd/config/main.go +++ b/cmd/config/main.go @@ -1,27 +1,38 @@ package config import ( + "fmt" + "github.com/calypr/forge/config" - conf "github.com/calypr/git-drs/config" + "github.com/calypr/forge/utils/remoteutil" "github.com/spf13/cobra" ) +var ( + configRemote string +) + var ConfigCmd = &cobra.Command{ - Use: "config [remote]", + Use: "config", Short: "Build skeleton template for CALYPR explorer page config.", Long: `Used for creating a template CALYPR explorer config to build and customize your own config`, - Example: "forge config local", + Example: "forge config --remote local", + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - var remote string - if len(args) > 0 { - remote = args[0] - } else { - remote = "" + remote, err := remoteutil.LoadRemoteOrDefault(configRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } - err := config.RunConfigInit(conf.Remote(remote)) + fmt.Printf("Using remote: %s\n", string(*remote)) + + err = config.RunConfigInit(*remote) if err != nil { return err } return nil }, } + +func init() { + ConfigCmd.Flags().StringVarP(&configRemote, "remote", "r", "", "target DRS server (default: default_remote)") +} diff --git a/cmd/empty/main.go b/cmd/empty/main.go index 6d18bd0..eb34266 100644 --- a/cmd/empty/main.go +++ b/cmd/empty/main.go @@ -4,26 +4,29 @@ import ( "fmt" "github.com/calypr/forge/publish" - "github.com/calypr/git-drs/config" + "github.com/calypr/forge/utils/remoteutil" "github.com/spf13/cobra" ) +var ( + emptyRemote string +) + var EmptyCmd = &cobra.Command{ - Use: "empty [remote]", + Use: "empty ", Short: "empty metadata for a project", Long: `The 'empty' command is how metadata is removed in calypr.`, - Args: cobra.RangeArgs(1, 2), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { projectID := args[0] - var remote config.Remote - if len(args) == 2 { - remote = config.Remote(args[1]) - fmt.Printf("Using remote: %s\n", remote) - } else { - remote = config.Remote("") - fmt.Printf("Using default remote: %s\n", remote) + + remote, err := remoteutil.LoadRemoteOrDefault(emptyRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } - resp, err := publish.RunEmpty(projectID, remote) + fmt.Printf("Using remote: %s\n", string(*remote)) + + resp, err := publish.RunEmpty(projectID, *remote) if err != nil { return err } @@ -31,3 +34,7 @@ var EmptyCmd = &cobra.Command{ return nil }, } + +func init() { + EmptyCmd.Flags().StringVarP(&emptyRemote, "remote", "r", "", "target DRS server (default: default_remote)") +} diff --git a/cmd/meta/main.go b/cmd/meta/main.go index 0b2c8d9..ba100bb 100644 --- a/cmd/meta/main.go +++ b/cmd/meta/main.go @@ -1,27 +1,39 @@ package meta import ( + "fmt" + "github.com/calypr/forge/metadata" "github.com/calypr/git-drs/config" "github.com/spf13/cobra" ) -var outPath string +var ( + outPath string + remote string +) + var MetaCmd = &cobra.Command{ Use: "meta", Short: "Autogenerate metadata based off of files that have been uploaded", Long: `Not needed for expected user workflow. Useful for debugging server side operations only.`, - Example: "forge meta [remote]", + Example: "forge meta", Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { - var remoteName string = "origin" - if len(args) > 0 { - remoteName = args[0] + + cfg, err := config.LoadConfig() + if err != nil { + return fmt.Errorf("unable to load config: %w", err) + } + + remoteName, err := cfg.GetRemoteOrDefault(remote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } - err := metadata.RunMetaInit(outPath, config.Remote(remoteName)) + err = metadata.CreateMeta(outPath, remoteName) if err != nil { - return err + return fmt.Errorf("could not create metadata: %w", err) } return nil }, @@ -29,4 +41,5 @@ var MetaCmd = &cobra.Command{ func init() { MetaCmd.PersistentFlags().StringVarP(&outPath, "out", "o", metadata.META_DIR, "Directory path to output FHIR .ndjson files") + MetaCmd.Flags().StringVarP(&remote, "remote", "r", "", "target DRS server (default: default_remote)") } diff --git a/cmd/ping/main.go b/cmd/ping/main.go index 8a61f39..3768453 100644 --- a/cmd/ping/main.go +++ b/cmd/ping/main.go @@ -5,23 +5,27 @@ import ( "log" "github.com/calypr/forge/client/fence" - "github.com/calypr/git-drs/config" + "github.com/calypr/forge/utils/remoteutil" "github.com/spf13/cobra" "gopkg.in/yaml.v2" ) +var ( + pingRemote string +) + var PingCmd = &cobra.Command{ - Use: "ping [remote]", + Use: "ping", Short: "Ping Calypr instance and return user's project and user permissions", + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - var remote config.Remote - if len(args) > 0 { - remote = config.Remote(args[0]) - } else { - remote = config.Remote("origin") + remote, err := remoteutil.LoadRemoteOrDefault(pingRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } + fmt.Printf("Using remote: %s\n", string(*remote)) - FenceClient, closer, err := fence.NewFenceClient(remote) + FenceClient, closer, err := fence.NewFenceClient(*remote) if err != nil { return err } @@ -41,3 +45,7 @@ var PingCmd = &cobra.Command{ return nil }, } + +func init() { + PingCmd.Flags().StringVarP(&pingRemote, "remote", "r", "", "target DRS server (default: default_remote)") +} diff --git a/cmd/publish/main.go b/cmd/publish/main.go index 0609539..68ddfd9 100644 --- a/cmd/publish/main.go +++ b/cmd/publish/main.go @@ -5,25 +5,27 @@ import ( "github.com/calypr/forge/client/sower" "github.com/calypr/forge/publish" - "github.com/calypr/git-drs/config" + "github.com/calypr/forge/utils/remoteutil" "github.com/spf13/cobra" ) +var ( + publishRemote string +) + var PublishCmd = &cobra.Command{ - Use: "publish [remote]", + Use: "publish ", Short: "create metadata upload job for FHIR ndjson files", Long: `The 'publish' command is how metadata is handled in calypr.`, - Args: cobra.RangeArgs(1, 2), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - var remote config.Remote - if len(args) == 2 { - remote = config.Remote(args[1]) - fmt.Printf("Using remote: %s\n", remote) - } else { - remote = config.Remote("") - fmt.Printf("Using default remote: %s\n", remote) + remote, err := remoteutil.LoadRemoteOrDefault(publishRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } - resp, err := publish.RunPublish(args[0], remote) + fmt.Printf("Using remote: %s\n", string(*remote)) + + resp, err := publish.RunPublish(args[0], *remote) if err != nil { return err } @@ -32,18 +34,32 @@ var PublishCmd = &cobra.Command{ }, } +var ( + listRemote string +) + var ListCmd = &cobra.Command{ - Use: "list [remote]", + Use: "list", Short: "view all of the jobs currently catalogued in sower", Long: `The 'list' command is how jobs are displayed to the user`, - Args: cobra.ExactArgs(1), + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - sc, closer, err := sower.NewSowerClient(config.Remote(args[0])) + remote, err := remoteutil.LoadRemoteOrDefault(listRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) + } + fmt.Printf("Using remote: %s\n", string(*remote)) + + sc, closer, err := sower.NewSowerClient(*remote) if err != nil { return err } defer closer() vals, err := sc.List() + if err != nil { + return fmt.Errorf("unable to list jobs: %w", err) + } + if len(vals) == 0 { fmt.Printf("There are no jobs to list: %s\n", vals) } else { @@ -55,22 +71,24 @@ var ListCmd = &cobra.Command{ }, } +var ( + statusRemote string +) + var StatusCmd = &cobra.Command{ - Use: "status [remote]", + Use: "status ", Short: "view the status of a specific job on sower", Long: `The 'status' command is how sower job status is communicated to the user. A specific job's UID can be found from running the list command`, - Args: cobra.RangeArgs(1, 2), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - var remote config.Remote - if len(args) == 2 { - remote = config.Remote(args[1]) - fmt.Printf("Using remote: %s\n", remote) - } else { - remote = config.Remote("") - fmt.Printf("Using default remote: %s\n", remote) + remote, err := remoteutil.LoadRemoteOrDefault(statusRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } - sc, closer, err := sower.NewSowerClient(remote) + fmt.Printf("Using remote: %s\n", string(*remote)) + + sc, closer, err := sower.NewSowerClient(*remote) if err != nil { return err } @@ -85,22 +103,24 @@ var StatusCmd = &cobra.Command{ }, } +var ( + outputRemote string +) + var OutputCmd = &cobra.Command{ - Use: "output [remote]", + Use: "output ", Short: "view output logs of a specific job on sower", Long: `The 'output' command is how sower job output logs are communicated to the user. A specific job's UID can be found from running the list command`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - var remote config.Remote - if len(args) == 2 { - remote = config.Remote(args[1]) - fmt.Printf("Using remote: %s\n", remote) - } else { - remote = config.Remote("") - fmt.Printf("Using default remote: %s\n", remote) + remote, err := remoteutil.LoadRemoteOrDefault(outputRemote) + if err != nil { + return fmt.Errorf("could not locate remote: %w", err) } - sc, closer, err := sower.NewSowerClient(remote) + fmt.Printf("Using remote: %s\n", string(*remote)) + + sc, closer, err := sower.NewSowerClient(*remote) if err != nil { return err } @@ -114,3 +134,10 @@ var OutputCmd = &cobra.Command{ return nil }, } + +func init() { + PublishCmd.Flags().StringVarP(&publishRemote, "remote", "r", "", "target DRS server (default: default_remote)") + ListCmd.Flags().StringVarP(&listRemote, "remote", "r", "", "target DRS server (default: default_remote)") + StatusCmd.Flags().StringVarP(&statusRemote, "remote", "r", "", "target DRS server (default: default_remote)") + OutputCmd.Flags().StringVarP(&outputRemote, "remote", "r", "", "target DRS server (default: default_remote)") +} diff --git a/go.mod b/go.mod index 5aadb6b..b4620dd 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/bytedance/sonic v1.14.2 github.com/calypr/data-client v0.0.0-20251230165452-a95f44240b14 github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 - github.com/calypr/git-drs v0.0.0-20251230165948-81a82438caa4 + github.com/calypr/git-drs v0.0.0-20260105205500-0dbe7e55d1f3 github.com/cockroachdb/errors v1.11.3 github.com/go-git/go-git/v5 v5.12.0 github.com/google/fhir/go v0.7.5-0.20250925033537-1f5b5b9427ff diff --git a/go.sum b/go.sum index fc5f871..61e2833 100644 --- a/go.sum +++ b/go.sum @@ -80,6 +80,8 @@ github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2 h1:ii28j/rzbOaxpUVnON github.com/calypr/gecko v0.0.0-20251110184938-909cb1b668e2/go.mod h1:SJfHDSOaN5xevCor2PVRZxHHJ/WwfAnBBddgnQcUwJo= github.com/calypr/git-drs v0.0.0-20251230165948-81a82438caa4 h1:AWs5hOg6VODWhQ/S9frW+6nz476/fr/9s+nqCJRDKvA= github.com/calypr/git-drs v0.0.0-20251230165948-81a82438caa4/go.mod h1:irPfPmQ5YCcK+GIRvjTqoU+znTL3xs8d/Ex9oX1Xriw= +github.com/calypr/git-drs v0.0.0-20260105205500-0dbe7e55d1f3 h1:xOUDTvAXwUZP/YoCDC2WCEiwjHZbnOsJFhvA5QMytAY= +github.com/calypr/git-drs v0.0.0-20260105205500-0dbe7e55d1f3/go.mod h1:2Z0yl6wUu/IeRPq0ALCakK5EnI1FntRYESEI1YbK4lg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= diff --git a/metadata/meta.go b/metadata/meta.go index f5de33a..47268e6 100644 --- a/metadata/meta.go +++ b/metadata/meta.go @@ -56,7 +56,7 @@ type MetaStructure struct { Path string `json:"path"` } -func RunMetaInit(outPath string, remote config.Remote) error { +func CreateMeta(outPath string, remote config.Remote) error { var rsID string var err error cfg, err := config.LoadConfig() diff --git a/publish/publish.go b/publish/publish.go index 432afdc..5b23b07 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -49,7 +49,8 @@ func RunPublish(token string, profile config.Remote) (*sower.StatusResp, error) if err != nil { return nil, err } - remote, err := repo.Remote(string(profile)) + // NOTE: hardcode to retrieve from git remote "origin" + remote, err := repo.Remote("origin") if err != nil { return nil, fmt.Errorf("failed to get 'origin' remote: %w", err) } diff --git a/utils/remoteutil/remoteutil.go b/utils/remoteutil/remoteutil.go new file mode 100644 index 0000000..fec45c0 --- /dev/null +++ b/utils/remoteutil/remoteutil.go @@ -0,0 +1,22 @@ +package remoteutil + +import ( + "fmt" + + "github.com/calypr/git-drs/config" +) + +// LoadRemoteOrDefault loads the git-drs config and returns the specified remote +// or the default remote if remoteName is empty. +func LoadRemoteOrDefault(remoteName string) (*config.Remote, error) { + cfg, err := config.LoadConfig() + if err != nil { + return nil, fmt.Errorf("unable to load config: %w", err) + } + remote, err := cfg.GetRemoteOrDefault(remoteName) + if err != nil { + return nil, fmt.Errorf("could not locate remote: %w", err) + } + + return &remote, nil +}