From b36a7df9295edce995007458b7f16b5a35dd9b09 Mon Sep 17 00:00:00 2001 From: Mike Sawka Date: Fri, 8 Nov 2024 10:19:57 -0800 Subject: [PATCH] setmeta updates to support json files (or reading stdin). easier interop with getmeta (#1240) --- cmd/wsh/cmd/wshcmd-setmeta.go | 87 ++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/cmd/wsh/cmd/wshcmd-setmeta.go b/cmd/wsh/cmd/wshcmd-setmeta.go index 36537bc94..8e70e4033 100644 --- a/cmd/wsh/cmd/wshcmd-setmeta.go +++ b/cmd/wsh/cmd/wshcmd-setmeta.go @@ -6,6 +6,8 @@ package cmd import ( "encoding/json" "fmt" + "io" + "os" "strconv" "strings" @@ -14,15 +16,46 @@ import ( ) var setMetaCmd = &cobra.Command{ - Use: "setmeta {blockid|blocknum|this} key=value ...", + Use: "setmeta [-b {blockid|blocknum|this}] [--json file.json] key=value ...", Short: "set metadata for an entity", - Args: cobra.MinimumNArgs(1), + Args: cobra.MinimumNArgs(0), Run: setMetaRun, PreRunE: preRunSetupRpcClient, } +var setMetaJsonFilePath string + func init() { rootCmd.AddCommand(setMetaCmd) + setMetaCmd.Flags().StringVar(&setMetaJsonFilePath, "json", "", "JSON file containing metadata to apply (use '-' for stdin)") +} + +func loadJSONFile(filepath string) (map[string]interface{}, error) { + var data []byte + var err error + + if filepath == "-" { + data, err = io.ReadAll(os.Stdin) + if err != nil { + return nil, fmt.Errorf("reading from stdin: %v", err) + } + } else { + data, err = os.ReadFile(filepath) + if err != nil { + return nil, fmt.Errorf("reading JSON file: %v", err) + } + } + + var result map[string]interface{} + if err := json.Unmarshal(data, &result); err != nil { + return nil, fmt.Errorf("parsing JSON file: %v", err) + } + + if result == nil { + return nil, fmt.Errorf("JSON file must contain an object, not null") + } + + return result, nil } func parseMetaSets(metaSets []string) (map[string]interface{}, error) { @@ -39,7 +72,7 @@ func parseMetaSets(metaSets []string) (map[string]interface{}, error) { meta[fields[0]] = true } else if setVal == "false" { meta[fields[0]] = false - } else if setVal[0] == '[' || setVal[0] == '{' { + } else if setVal[0] == '[' || setVal[0] == '{' || setVal[0] == '"' { var val interface{} err := json.Unmarshal([]byte(setVal), &val) if err != nil { @@ -63,31 +96,63 @@ func parseMetaSets(metaSets []string) (map[string]interface{}, error) { return meta, nil } +func simpleMergeMeta(meta map[string]interface{}, metaUpdate map[string]interface{}) map[string]interface{} { + for k, v := range metaUpdate { + if v == nil { + delete(meta, k) + } else { + meta[k] = v + } + } + return meta +} + func setMetaRun(cmd *cobra.Command, args []string) { - oref := blockArg - metaSetsStrs := args[:] - if oref == "" { - WriteStderr("[error] oref is required\n") + if blockArg == "" { + WriteStderr("[error] block (oref) is required\n") return } - err := validateEasyORef(oref) + err := validateEasyORef(blockArg) if err != nil { WriteStderr("[error] %v\n", err) return } - meta, err := parseMetaSets(metaSetsStrs) + + var jsonMeta map[string]interface{} + if setMetaJsonFilePath != "" { + jsonMeta, err = loadJSONFile(setMetaJsonFilePath) + if err != nil { + WriteStderr("[error] %v\n", err) + return + } + } + + cmdMeta, err := parseMetaSets(args) if err != nil { WriteStderr("[error] %v\n", err) return } - fullORef, err := resolveSimpleId(oref) + + // Merge JSON metadata with command-line metadata, with command-line taking precedence + var fullMeta map[string]any + if len(jsonMeta) > 0 { + fullMeta = simpleMergeMeta(jsonMeta, cmdMeta) + } else { + fullMeta = cmdMeta + } + if len(fullMeta) == 0 { + WriteStderr("[error] no metadata keys specified\n") + return + } + fullORef, err := resolveSimpleId(blockArg) if err != nil { WriteStderr("[error] resolving oref: %v\n", err) return } + setMetaWshCmd := &wshrpc.CommandSetMetaData{ ORef: *fullORef, - Meta: meta, + Meta: fullMeta, } _, err = RpcClient.SendRpcRequest(wshrpc.Command_SetMeta, setMetaWshCmd, &wshrpc.RpcOpts{Timeout: 2000}) if err != nil {