github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/typesystem/resolver.go (about) 1 package typesystem 2 3 import ( 4 "context" 5 "fmt" 6 "slices" 7 8 "github.com/authzed/spicedb/pkg/datastore" 9 core "github.com/authzed/spicedb/pkg/proto/core/v1" 10 "github.com/authzed/spicedb/pkg/schemadsl/compiler" 11 ) 12 13 // Resolver is an interface defined for resolving referenced namespaces and caveats when constructing 14 // and validating a type system. 15 type Resolver interface { 16 // LookupNamespace lookups up a namespace. 17 LookupNamespace(ctx context.Context, name string) (*core.NamespaceDefinition, error) 18 19 // LookupCaveat lookups up a caveat. 20 LookupCaveat(ctx context.Context, name string) (*core.CaveatDefinition, error) 21 22 // WithPredefinedElements adds the given predefined elements to this resolver, returning a new 23 // resolver. 24 WithPredefinedElements(predefined PredefinedElements) Resolver 25 } 26 27 // ResolverForDatastoreReader returns a Resolver for a datastore reader. 28 func ResolverForDatastoreReader(ds datastore.Reader) Resolver { 29 return &resolver{ 30 ds: ds, 31 } 32 } 33 34 // PredefinedElements are predefined namespaces and/or caveats to give to a resolver. 35 type PredefinedElements struct { 36 Namespaces []*core.NamespaceDefinition 37 Caveats []*core.CaveatDefinition 38 } 39 40 func (pe PredefinedElements) combineWith(other PredefinedElements) PredefinedElements { 41 return PredefinedElements{ 42 Namespaces: append(slices.Clone(pe.Namespaces), other.Namespaces...), 43 Caveats: append(slices.Clone(pe.Caveats), other.Caveats...), 44 } 45 } 46 47 // ResolverForPredefinedDefinitions returns a resolver for predefined namespaces and caveats. 48 func ResolverForPredefinedDefinitions(predefined PredefinedElements) Resolver { 49 return &resolver{ 50 predefined: predefined, 51 } 52 } 53 54 // ResolverForSchema returns a resolver for a schema. 55 func ResolverForSchema(schema compiler.CompiledSchema) Resolver { 56 return ResolverForPredefinedDefinitions( 57 PredefinedElements{ 58 Namespaces: schema.ObjectDefinitions, 59 Caveats: schema.CaveatDefinitions, 60 }, 61 ) 62 } 63 64 type resolver struct { 65 ds datastore.Reader 66 predefined PredefinedElements 67 } 68 69 func (r *resolver) LookupNamespace(ctx context.Context, name string) (*core.NamespaceDefinition, error) { 70 if len(r.predefined.Namespaces) > 0 { 71 for _, def := range r.predefined.Namespaces { 72 if def.Name == name { 73 return def, nil 74 } 75 } 76 } 77 78 if r.ds == nil { 79 return nil, asTypeError(NewNamespaceNotFoundErr(name)) 80 } 81 82 ns, _, err := r.ds.ReadNamespaceByName(ctx, name) 83 return ns, err 84 } 85 86 func (r *resolver) WithPredefinedElements(predefined PredefinedElements) Resolver { 87 return &resolver{ 88 ds: r.ds, 89 predefined: predefined.combineWith(r.predefined), 90 } 91 } 92 93 func (r *resolver) LookupCaveat(ctx context.Context, name string) (*core.CaveatDefinition, error) { 94 if len(r.predefined.Caveats) > 0 { 95 for _, caveat := range r.predefined.Caveats { 96 if caveat.Name == name { 97 return caveat, nil 98 } 99 } 100 } 101 102 if r.ds == nil { 103 return nil, asTypeError(NewCaveatNotFoundErr(name)) 104 } 105 106 cr, ok := r.ds.(datastore.CaveatReader) 107 if !ok { 108 return nil, fmt.Errorf("caveats are not supported on this datastore type") 109 } 110 111 caveatDef, _, err := cr.ReadCaveatByName(ctx, name) 112 return caveatDef, err 113 }