Merged in feature/permit-io-demo (pull request #144)
draft pr for the test demo harness for permit.io * harness working for permit.io demo * user is const * aws profile * permit import code for research * cleanup * create user tool poc * Merge branch 'main' of bitbucket.org:aarete/query-orchestration into feature/permit-io-demo * tests for user creation * add audit logs and dry-run * add tests sanity check the requested roles * update docs for delete * disable/enable users and test * more tests and make user name required only for create operations * clean up * add docs * aws tag tests * audit -> slog * attempt build fix devbox * edit * just devbox * remove lock change for merge * Merge branch 'main' of bitbucket.org:aarete/query-orchestration into feature/permit-io-demo
This commit is contained in:
+329
@@ -0,0 +1,329 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/aws/smithy-go/document"
|
||||
"github.com/aws/smithy-go/document/internal/serde"
|
||||
smithyjson "github.com/aws/smithy-go/encoding/json"
|
||||
)
|
||||
|
||||
// EncoderOptions is the set of options that can be configured for an Encoder.
|
||||
type EncoderOptions struct{}
|
||||
|
||||
// Encoder is a Smithy document decoder for JSON based protocols.
|
||||
type Encoder struct {
|
||||
options EncoderOptions
|
||||
}
|
||||
|
||||
// Encode returns the JSON encoding of v.
|
||||
func (e *Encoder) Encode(v interface{}) ([]byte, error) {
|
||||
encoder := smithyjson.NewEncoder()
|
||||
|
||||
if err := e.encode(jsonValueProvider(encoder.Value), reflect.ValueOf(v), serde.Tag{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encodedBytes := encoder.Bytes()
|
||||
|
||||
if len(encodedBytes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return encodedBytes, nil
|
||||
}
|
||||
|
||||
// valueProvider is an interface for retrieving a JSON Value type used for encoding.
|
||||
type valueProvider interface {
|
||||
GetValue() smithyjson.Value
|
||||
}
|
||||
|
||||
// jsonValueProvider is a valueProvider that returns the JSON value encoder as is.
|
||||
type jsonValueProvider smithyjson.Value
|
||||
|
||||
func (p jsonValueProvider) GetValue() smithyjson.Value {
|
||||
return smithyjson.Value(p)
|
||||
}
|
||||
|
||||
// jsonObjectKeyProvider is a valueProvider that returns a JSON value type for encoding a value for the given JSON object
|
||||
// key.
|
||||
type jsonObjectKeyProvider struct {
|
||||
Object *smithyjson.Object
|
||||
Key string
|
||||
}
|
||||
|
||||
func (p jsonObjectKeyProvider) GetValue() smithyjson.Value {
|
||||
return p.Object.Key(p.Key)
|
||||
}
|
||||
|
||||
// jsonArrayProvider is a valueProvider that returns a JSON value type for encoding a value in the given JSON array.
|
||||
type jsonArrayProvider struct {
|
||||
Array *smithyjson.Array
|
||||
}
|
||||
|
||||
func (p jsonArrayProvider) GetValue() smithyjson.Value {
|
||||
return p.Array.Value()
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(vp valueProvider, rv reflect.Value, tag serde.Tag) error {
|
||||
// Zero values are serialized as null, or skipped if omitEmpty.
|
||||
if serde.IsZeroValue(rv) {
|
||||
if tag.OmitEmpty {
|
||||
return nil
|
||||
}
|
||||
return e.encodeZeroValue(vp, rv)
|
||||
}
|
||||
|
||||
// Handle both pointers and interface conversion into types
|
||||
rv = serde.ValueElem(rv)
|
||||
|
||||
switch rv.Kind() {
|
||||
case reflect.Invalid:
|
||||
if tag.OmitEmpty {
|
||||
return nil
|
||||
}
|
||||
vp.GetValue().Null()
|
||||
return nil
|
||||
|
||||
case reflect.Struct:
|
||||
return e.encodeStruct(vp, rv)
|
||||
|
||||
case reflect.Map:
|
||||
return e.encodeMap(vp, rv)
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
return e.encodeSlice(vp, rv)
|
||||
|
||||
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
||||
// skip unsupported types
|
||||
return nil
|
||||
|
||||
default:
|
||||
return e.encodeScalar(vp, rv)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeZeroValue(vp valueProvider, rv reflect.Value) error {
|
||||
switch rv.Kind() {
|
||||
case reflect.Invalid:
|
||||
vp.GetValue().Null()
|
||||
case reflect.Array:
|
||||
vp.GetValue().Array().Close()
|
||||
case reflect.Map, reflect.Slice:
|
||||
vp.GetValue().Null()
|
||||
case reflect.String:
|
||||
vp.GetValue().String("")
|
||||
case reflect.Bool:
|
||||
vp.GetValue().Boolean(false)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
vp.GetValue().Long(0)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
vp.GetValue().ULong(0)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
vp.GetValue().Double(0)
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
vp.GetValue().Null()
|
||||
default:
|
||||
return &document.InvalidMarshalError{Message: fmt.Sprintf("unknown value type: %s", rv.String())}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeStruct(vp valueProvider, rv reflect.Value) error {
|
||||
if rv.CanInterface() && document.IsNoSerde(rv.Interface()) {
|
||||
return &document.UnmarshalTypeError{
|
||||
Value: fmt.Sprintf("unsupported type"),
|
||||
Type: rv.Type(),
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case rv.Type().ConvertibleTo(serde.ReflectTypeOf.Time):
|
||||
return &document.InvalidMarshalError{
|
||||
Message: fmt.Sprintf("unsupported type %s", rv.Type().String()),
|
||||
}
|
||||
case rv.Type().ConvertibleTo(serde.ReflectTypeOf.BigFloat):
|
||||
fallthrough
|
||||
case rv.Type().ConvertibleTo(serde.ReflectTypeOf.BigInt):
|
||||
return e.encodeNumber(vp, rv)
|
||||
}
|
||||
|
||||
object := vp.GetValue().Object()
|
||||
defer object.Close()
|
||||
|
||||
fields := serde.GetStructFields(rv.Type())
|
||||
for _, f := range fields.All() {
|
||||
if f.Name == "" {
|
||||
return &document.InvalidMarshalError{Message: "map key cannot be empty"}
|
||||
}
|
||||
|
||||
fv, found := serde.EncoderFieldByIndex(rv, f.Index)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
err := e.encode(jsonObjectKeyProvider{
|
||||
Object: object,
|
||||
Key: f.Name,
|
||||
}, fv, f.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeMap(vp valueProvider, rv reflect.Value) error {
|
||||
object := vp.GetValue().Object()
|
||||
defer object.Close()
|
||||
|
||||
for _, key := range rv.MapKeys() {
|
||||
keyName := fmt.Sprint(key.Interface())
|
||||
if keyName == "" {
|
||||
return &document.InvalidMarshalError{Message: "map key cannot be empty"}
|
||||
}
|
||||
|
||||
ev := rv.MapIndex(key)
|
||||
err := e.encode(jsonObjectKeyProvider{
|
||||
Object: object,
|
||||
Key: keyName,
|
||||
}, ev, serde.Tag{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeSlice(value valueProvider, rv reflect.Value) error {
|
||||
array := value.GetValue().Array()
|
||||
defer array.Close()
|
||||
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
err := e.encode(jsonArrayProvider{Array: array}, rv.Index(i), serde.Tag{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeScalar(vp valueProvider, rv reflect.Value) error {
|
||||
if rv.Type() == serde.ReflectTypeOf.DocumentNumber {
|
||||
number := rv.Interface().(document.Number)
|
||||
if !isValidJSONNumber(number.String()) {
|
||||
return &document.InvalidMarshalError{Message: fmt.Sprintf("invalid number literal: %s", number)}
|
||||
}
|
||||
vp.GetValue().Write([]byte(number))
|
||||
}
|
||||
|
||||
switch rv.Kind() {
|
||||
case reflect.Bool:
|
||||
vp.GetValue().Boolean(rv.Bool())
|
||||
case reflect.String:
|
||||
vp.GetValue().String(rv.String())
|
||||
default:
|
||||
// Fallback to encoding numbers, will return invalid type if not supported
|
||||
err := e.encodeNumber(vp, rv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeNumber(vp valueProvider, rv reflect.Value) error {
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
vp.GetValue().Long(rv.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
vp.GetValue().ULong(rv.Uint())
|
||||
case reflect.Float32:
|
||||
vp.GetValue().Float(float32(rv.Float()))
|
||||
case reflect.Float64:
|
||||
vp.GetValue().Double(rv.Float())
|
||||
default:
|
||||
rvt := rv.Type()
|
||||
switch {
|
||||
case rvt.ConvertibleTo(serde.ReflectTypeOf.BigInt):
|
||||
bi := rv.Convert(serde.ReflectTypeOf.BigInt).Interface().(big.Int)
|
||||
vp.GetValue().BigInteger(&bi)
|
||||
case rvt.ConvertibleTo(serde.ReflectTypeOf.BigFloat):
|
||||
bf := rv.Convert(serde.ReflectTypeOf.BigFloat).Interface().(big.Float)
|
||||
vp.GetValue().BigDecimal(&bf)
|
||||
default:
|
||||
return &document.InvalidMarshalError{Message: fmt.Sprintf("incompatible type: %s", rvt.String())}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isValidJSONNumber reports whether s is a valid JSON number literal.
|
||||
// From https://golang.org/src/encoding/json/encode.go#L652 isValidNumber
|
||||
// Copyright 2010 The Go Authors.
|
||||
func isValidJSONNumber(s string) bool {
|
||||
// This function implements the JSON numbers grammar.
|
||||
// See https://tools.ietf.org/html/rfc7159#section-6
|
||||
// and https://www.json.org/img/number.png
|
||||
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Optional -
|
||||
if s[0] == '-' {
|
||||
s = s[1:]
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Digits
|
||||
switch {
|
||||
default:
|
||||
return false
|
||||
|
||||
case s[0] == '0':
|
||||
s = s[1:]
|
||||
|
||||
case '1' <= s[0] && s[0] <= '9':
|
||||
s = s[1:]
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// . followed by 1 or more digits.
|
||||
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
|
||||
s = s[2:]
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// e or E followed by an optional - or + and
|
||||
// 1 or more digits.
|
||||
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
||||
s = s[1:]
|
||||
if s[0] == '+' || s[0] == '-' {
|
||||
s = s[1:]
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we are at the end.
|
||||
return s == ""
|
||||
}
|
||||
Reference in New Issue
Block a user