summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/zed/internal/commands/util.go
blob: 6d5b4da024e64b4e6cd82207198072b993ecc38a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package commands

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/TylerBrock/colorjson"
	"github.com/jzelinskie/cobrautil/v2"
	"github.com/jzelinskie/stringz"
	"github.com/spf13/cobra"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/structpb"

	"github.com/authzed/authzed-go/pkg/requestmeta"
)

// ParseSubject parses the given subject string into its namespace, object ID
// and relation, if valid.
func ParseSubject(s string) (namespace, id, relation string, err error) {
	err = stringz.SplitExact(s, ":", &namespace, &id)
	if err != nil {
		return
	}
	err = stringz.SplitExact(id, "#", &id, &relation)
	if err != nil {
		relation = ""
		err = nil
	}
	return
}

// ParseType parses a type reference of the form `namespace#relaion`.
func ParseType(s string) (namespace, relation string) {
	namespace, relation, _ = strings.Cut(s, "#")
	return
}

// GetCaveatContext returns the entered caveat caveat, if any.
func GetCaveatContext(cmd *cobra.Command) (*structpb.Struct, error) {
	contextString := cobrautil.MustGetString(cmd, "caveat-context")
	if len(contextString) == 0 {
		return nil, nil
	}

	return ParseCaveatContext(contextString)
}

// ParseCaveatContext parses the given context JSON string into caveat context,
// if valid.
func ParseCaveatContext(contextString string) (*structpb.Struct, error) {
	contextMap := map[string]any{}
	err := json.Unmarshal([]byte(contextString), &contextMap)
	if err != nil {
		return nil, fmt.Errorf("invalid caveat context JSON: %w", err)
	}

	context, err := structpb.NewStruct(contextMap)
	if err != nil {
		return nil, fmt.Errorf("could not construct caveat context: %w", err)
	}
	return context, err
}

// PrettyProto returns the given protocol buffer formatted into pretty text.
func PrettyProto(m proto.Message) ([]byte, error) {
	encoded, err := protojson.Marshal(m)
	if err != nil {
		return nil, err
	}
	var obj interface{}
	err = json.Unmarshal(encoded, &obj)
	if err != nil {
		panic("protojson decode failed: " + err.Error())
	}

	f := colorjson.NewFormatter()
	f.Indent = 2
	pretty, err := f.Marshal(obj)
	if err != nil {
		panic("colorjson encode failed: " + err.Error())
	}

	return pretty, nil
}

// InjectRequestID adds the value of the --request-id flag to the
// context of the given command.
func InjectRequestID(cmd *cobra.Command, _ []string) error {
	ctx := cmd.Context()
	requestID := cobrautil.MustGetString(cmd, "request-id")
	if ctx != nil && requestID != "" {
		cmd.SetContext(requestmeta.WithRequestID(ctx, requestID))
	}

	return nil
}

// ValidationError is used to wrap errors that are cobra validation errors. It should be used to
// wrap the Command.PositionalArgs function in order to be able to determine if the error is a validation error.
// This is used to determine if an error should print the usage string. Unfortunately Cobra parameter parsing
// and parameter validation are handled differently, and the latter does not trigger calling Command.FlagErrorFunc
type ValidationError struct {
	error
}

func (ve ValidationError) Is(err error) bool {
	var validationError ValidationError
	return errors.As(err, &validationError)
}

// ValidationWrapper is used to be able to determine if an error is a validation error.
func ValidationWrapper(f cobra.PositionalArgs) cobra.PositionalArgs {
	return func(cmd *cobra.Command, args []string) error {
		if err := f(cmd, args); err != nil {
			return ValidationError{error: err}
		}

		return nil
	}
}