 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			336 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package bindnode provides a datamodel.Node implementation via Go reflection.
 | |
| //
 | |
| // This package is EXPERIMENTAL; its behavior and API might change as it's still
 | |
| // in development.
 | |
| package bindnode
 | |
| 
 | |
| import (
 | |
| 	"reflect"
 | |
| 
 | |
| 	"github.com/ipfs/go-cid"
 | |
| 	"github.com/ipld/go-ipld-prime/datamodel"
 | |
| 	"github.com/ipld/go-ipld-prime/schema"
 | |
| )
 | |
| 
 | |
| // Prototype implements a schema.TypedPrototype given a Go pointer type and an
 | |
| // IPLD schema type. Note that the result is also a datamodel.NodePrototype.
 | |
| //
 | |
| // If both the Go type and schema type are supplied, it is assumed that they are
 | |
| // compatible with one another.
 | |
| //
 | |
| // If either the Go type or schema type are nil, we infer the missing type from
 | |
| // the other provided type. For example, we can infer an unnamed Go struct type
 | |
| // for a schema struct type, and we can infer a schema Int type for a Go int64
 | |
| // type. The inferring logic is still a work in progress and subject to change.
 | |
| // At this time, inferring IPLD Unions and Enums from Go types is not supported.
 | |
| //
 | |
| // When supplying a non-nil ptrType, Prototype only obtains the Go pointer type
 | |
| // from it, so its underlying value will typically be nil. For example:
 | |
| //
 | |
| //	proto := bindnode.Prototype((*goType)(nil), schemaType)
 | |
| func Prototype(ptrType interface{}, schemaType schema.Type, options ...Option) schema.TypedPrototype {
 | |
| 	if ptrType == nil && schemaType == nil {
 | |
| 		panic("bindnode: either ptrType or schemaType must not be nil")
 | |
| 	}
 | |
| 
 | |
| 	cfg := applyOptions(options...)
 | |
| 
 | |
| 	// TODO: if both are supplied, verify that they are compatible
 | |
| 
 | |
| 	var goType reflect.Type
 | |
| 	if ptrType == nil {
 | |
| 		goType = inferGoType(schemaType, make(map[schema.TypeName]inferredStatus), 0)
 | |
| 	} else {
 | |
| 		goPtrType := reflect.TypeOf(ptrType)
 | |
| 		if goPtrType.Kind() != reflect.Ptr {
 | |
| 			panic("bindnode: ptrType must be a pointer")
 | |
| 		}
 | |
| 		goType = goPtrType.Elem()
 | |
| 		if goType.Kind() == reflect.Ptr {
 | |
| 			panic("bindnode: ptrType must not be a pointer to a pointer")
 | |
| 		}
 | |
| 
 | |
| 		if schemaType == nil {
 | |
| 			schemaType = inferSchema(goType, 0)
 | |
| 		} else {
 | |
| 			verifyCompatibility(cfg, make(map[seenEntry]bool), goType, schemaType)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &_prototype{cfg: cfg, schemaType: schemaType, goType: goType}
 | |
| }
 | |
| 
 | |
| type converter struct {
 | |
| 	kind schema.TypeKind
 | |
| 
 | |
| 	customFromBool func(bool) (interface{}, error)
 | |
| 	customToBool   func(interface{}) (bool, error)
 | |
| 
 | |
| 	customFromInt func(int64) (interface{}, error)
 | |
| 	customToInt   func(interface{}) (int64, error)
 | |
| 
 | |
| 	customFromFloat func(float64) (interface{}, error)
 | |
| 	customToFloat   func(interface{}) (float64, error)
 | |
| 
 | |
| 	customFromString func(string) (interface{}, error)
 | |
| 	customToString   func(interface{}) (string, error)
 | |
| 
 | |
| 	customFromBytes func([]byte) (interface{}, error)
 | |
| 	customToBytes   func(interface{}) ([]byte, error)
 | |
| 
 | |
| 	customFromLink func(cid.Cid) (interface{}, error)
 | |
| 	customToLink   func(interface{}) (cid.Cid, error)
 | |
| 
 | |
| 	customFromAny func(datamodel.Node) (interface{}, error)
 | |
| 	customToAny   func(interface{}) (datamodel.Node, error)
 | |
| }
 | |
| 
 | |
| type config map[reflect.Type]*converter
 | |
| 
 | |
| // this mainly exists to short-circuit the nonPtrType() call; the `Type()` variant
 | |
| // exists for completeness
 | |
| func (c config) converterFor(val reflect.Value) *converter {
 | |
| 	if len(c) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return c[nonPtrType(val)]
 | |
| }
 | |
| 
 | |
| func (c config) converterForType(typ reflect.Type) *converter {
 | |
| 	if len(c) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return c[typ]
 | |
| }
 | |
| 
 | |
| // Option is able to apply custom options to the bindnode API
 | |
| type Option func(config)
 | |
| 
 | |
| // TypedBoolConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func(bool) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) (bool, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // TypedBoolConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedBoolConverter(ptrVal interface{}, from func(bool) (interface{}, error), to func(interface{}) (bool, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:           schema.TypeKind_Bool,
 | |
| 		customFromBool: from,
 | |
| 		customToBool:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TypedIntConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func(int64) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) (int64, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // TypedIntConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedIntConverter(ptrVal interface{}, from func(int64) (interface{}, error), to func(interface{}) (int64, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:          schema.TypeKind_Int,
 | |
| 		customFromInt: from,
 | |
| 		customToInt:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TypedFloatConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func(float64) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) (float64, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // TypedFloatConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedFloatConverter(ptrVal interface{}, from func(float64) (interface{}, error), to func(interface{}) (float64, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:            schema.TypeKind_Float,
 | |
| 		customFromFloat: from,
 | |
| 		customToFloat:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TypedStringConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func(string) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) (string, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // TypedStringConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedStringConverter(ptrVal interface{}, from func(string) (interface{}, error), to func(interface{}) (string, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:             schema.TypeKind_String,
 | |
| 		customFromString: from,
 | |
| 		customToString:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TypedBytesConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func([]byte) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) ([]byte, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // TypedBytesConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedBytesConverter(ptrVal interface{}, from func([]byte) (interface{}, error), to func(interface{}) ([]byte, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:            schema.TypeKind_Bytes,
 | |
| 		customFromBytes: from,
 | |
| 		customToBytes:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TypedLinkConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func([]byte) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) ([]byte, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // Beware that this API is only compatible with cidlink.Link types in the data
 | |
| // model and may result in errors if attempting to convert from other
 | |
| // datamodel.Link types.
 | |
| //
 | |
| // TypedLinkConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedLinkConverter(ptrVal interface{}, from func(cid.Cid) (interface{}, error), to func(interface{}) (cid.Cid, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:           schema.TypeKind_Link,
 | |
| 		customFromLink: from,
 | |
| 		customToLink:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TypedAnyConverter adds custom converter functions for a particular
 | |
| // type as identified by a pointer in the first argument.
 | |
| // The fromFunc is of the form: func(datamodel.Node) (interface{}, error)
 | |
| // and toFunc is of the form: func(interface{}) (datamodel.Node, error)
 | |
| // where interface{} is a pointer form of the type we are converting.
 | |
| //
 | |
| // This method should be able to deal with all forms of Any and return an error
 | |
| // if the expected data forms don't match the expected.
 | |
| //
 | |
| // TypedAnyConverter is an EXPERIMENTAL API and may be removed or
 | |
| // changed in a future release.
 | |
| func TypedAnyConverter(ptrVal interface{}, from func(datamodel.Node) (interface{}, error), to func(interface{}) (datamodel.Node, error)) Option {
 | |
| 	customType := nonPtrType(reflect.ValueOf(ptrVal))
 | |
| 	converter := &converter{
 | |
| 		kind:          schema.TypeKind_Any,
 | |
| 		customFromAny: from,
 | |
| 		customToAny:   to,
 | |
| 	}
 | |
| 	return func(cfg config) {
 | |
| 		cfg[customType] = converter
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func applyOptions(opt ...Option) config {
 | |
| 	if len(opt) == 0 {
 | |
| 		// no need to allocate, we access it via converterFor and converterForType
 | |
| 		// which are safe for nil maps
 | |
| 		return nil
 | |
| 	}
 | |
| 	cfg := make(map[reflect.Type]*converter)
 | |
| 	for _, o := range opt {
 | |
| 		o(cfg)
 | |
| 	}
 | |
| 	return cfg
 | |
| }
 | |
| 
 | |
| // Wrap implements a schema.TypedNode given a non-nil pointer to a Go value and an
 | |
| // IPLD schema type. Note that the result is also a datamodel.Node.
 | |
| //
 | |
| // Wrap is meant to be used when one already has a Go value with data.
 | |
| // As such, ptrVal must not be nil.
 | |
| //
 | |
| // Similar to Prototype, if schemaType is non-nil it is assumed to be compatible
 | |
| // with the Go type, and otherwise it's inferred from the Go type.
 | |
| func Wrap(ptrVal interface{}, schemaType schema.Type, options ...Option) schema.TypedNode {
 | |
| 	if ptrVal == nil {
 | |
| 		panic("bindnode: ptrVal must not be nil")
 | |
| 	}
 | |
| 	goPtrVal := reflect.ValueOf(ptrVal)
 | |
| 	if goPtrVal.Kind() != reflect.Ptr {
 | |
| 		panic("bindnode: ptrVal must be a pointer")
 | |
| 	}
 | |
| 	if goPtrVal.IsNil() {
 | |
| 		// Note that this can happen if ptrVal was a typed nil.
 | |
| 		panic("bindnode: ptrVal must not be nil")
 | |
| 	}
 | |
| 	cfg := applyOptions(options...)
 | |
| 	goVal := goPtrVal.Elem()
 | |
| 	if goVal.Kind() == reflect.Ptr {
 | |
| 		panic("bindnode: ptrVal must not be a pointer to a pointer")
 | |
| 	}
 | |
| 	if schemaType == nil {
 | |
| 		schemaType = inferSchema(goVal.Type(), 0)
 | |
| 	} else {
 | |
| 		// TODO(rvagg): explore ways to make this skippable by caching in the schema.Type
 | |
| 		// passed in to this function; e.g. if you call Prototype(), then you've gone through
 | |
| 		// this already, then calling .Type() on that could return a bindnode version of
 | |
| 		// schema.Type that has the config cached and can be assumed to have been checked or
 | |
| 		// inferred.
 | |
| 		verifyCompatibility(cfg, make(map[seenEntry]bool), goVal.Type(), schemaType)
 | |
| 	}
 | |
| 	return newNode(cfg, schemaType, goVal)
 | |
| }
 | |
| 
 | |
| // TODO: consider making our own Node interface, like:
 | |
| //
 | |
| // type WrappedNode interface {
 | |
| //     datamodel.Node
 | |
| //     Unwrap() (ptrVal interface)
 | |
| // }
 | |
| //
 | |
| // Pros: API is easier to understand, harder to mix up with other datamodel.Nodes.
 | |
| // Cons: One usually only has a datamodel.Node, and type assertions can be weird.
 | |
| 
 | |
| // Unwrap takes a datamodel.Node implemented by Prototype or Wrap,
 | |
| // and returns a pointer to the inner Go value.
 | |
| //
 | |
| // Unwrap returns nil if the node isn't implemented by this package.
 | |
| func Unwrap(node datamodel.Node) (ptrVal interface{}) {
 | |
| 	var val reflect.Value
 | |
| 	switch node := node.(type) {
 | |
| 	case *_node:
 | |
| 		val = node.val
 | |
| 	case *_nodeRepr:
 | |
| 		val = node.val
 | |
| 	default:
 | |
| 		return nil
 | |
| 	}
 | |
| 	if val.Kind() == reflect.Ptr {
 | |
| 		panic("bindnode: didn't expect val to be a pointer")
 | |
| 	}
 | |
| 	return val.Addr().Interface()
 | |
| }
 |