summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/pkg/validationfile/loader.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/validationfile/loader.go')
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/validationfile/loader.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/pkg/validationfile/loader.go b/vendor/github.com/authzed/spicedb/pkg/validationfile/loader.go
new file mode 100644
index 0000000..d3322b6
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/validationfile/loader.go
@@ -0,0 +1,172 @@
+package validationfile
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ log "github.com/authzed/spicedb/internal/logging"
+ dsctx "github.com/authzed/spicedb/internal/middleware/datastore"
+ "github.com/authzed/spicedb/internal/namespace"
+ "github.com/authzed/spicedb/internal/relationships"
+ caveattypes "github.com/authzed/spicedb/pkg/caveats/types"
+ "github.com/authzed/spicedb/pkg/datastore"
+ "github.com/authzed/spicedb/pkg/genutil/slicez"
+ core "github.com/authzed/spicedb/pkg/proto/core/v1"
+ "github.com/authzed/spicedb/pkg/schema"
+ "github.com/authzed/spicedb/pkg/tuple"
+)
+
+// PopulatedValidationFile contains the fully parsed information from a validation file.
+type PopulatedValidationFile struct {
+ // Schema is the entered schema text, if any.
+ Schema string
+
+ // NamespaceDefinitions are the namespaces defined in the validation file, in either
+ // direct or compiled from schema form.
+ NamespaceDefinitions []*core.NamespaceDefinition
+
+ // CaveatDefinitions are the caveats defined in the validation file, in either
+ // direct or compiled from schema form.
+ CaveatDefinitions []*core.CaveatDefinition
+
+ // Relationships are the relationships defined in the validation file, either directly
+ // or in the relationships block.
+ Relationships []tuple.Relationship
+
+ // ParsedFiles are the underlying parsed validation files.
+ ParsedFiles []ValidationFile
+}
+
+// PopulateFromFiles populates the given datastore with the namespaces and tuples found in
+// the validation file(s) specified.
+func PopulateFromFiles(ctx context.Context, ds datastore.Datastore, caveatTypeSet *caveattypes.TypeSet, filePaths []string) (*PopulatedValidationFile, datastore.Revision, error) {
+ contents := map[string][]byte{}
+
+ for _, filePath := range filePaths {
+ fileContents, err := os.ReadFile(filePath)
+ if err != nil {
+ return nil, datastore.NoRevision, err
+ }
+
+ contents[filePath] = fileContents
+ }
+
+ return PopulateFromFilesContents(ctx, ds, caveatTypeSet, contents)
+}
+
+// PopulateFromFilesContents populates the given datastore with the namespaces and tuples found in
+// the validation file(s) contents specified.
+func PopulateFromFilesContents(ctx context.Context, ds datastore.Datastore, caveatTypeSet *caveattypes.TypeSet, filesContents map[string][]byte) (*PopulatedValidationFile, datastore.Revision, error) {
+ var schemaStr string
+ var objectDefs []*core.NamespaceDefinition
+ var caveatDefs []*core.CaveatDefinition
+ var rels []tuple.Relationship
+ var updates []tuple.RelationshipUpdate
+
+ var revision datastore.Revision
+
+ files := make([]ValidationFile, 0, len(filesContents))
+
+ // Parse each file into definitions and relationship updates.
+ for filePath, fileContents := range filesContents {
+ // Decode the validation file.
+ parsed, err := DecodeValidationFile(fileContents)
+ if err != nil {
+ return nil, datastore.NoRevision, fmt.Errorf("error when parsing config file %s: %w", filePath, err)
+ }
+
+ files = append(files, *parsed)
+
+ // Disallow legacy sections.
+ if len(parsed.NamespaceConfigs) > 0 {
+ return nil, revision, fmt.Errorf("definitions must be specified in `schema`")
+ }
+
+ if len(parsed.ValidationTuples) > 0 {
+ return nil, revision, fmt.Errorf("relationships must be specified in `relationships`")
+ }
+
+ // Add schema definitions.
+ if parsed.Schema.CompiledSchema != nil {
+ defs := parsed.Schema.CompiledSchema.ObjectDefinitions
+ if len(defs) > 0 {
+ schemaStr += parsed.Schema.Schema + "\n\n"
+ }
+
+ log.Ctx(ctx).Info().Str("filePath", filePath).
+ Int("definitionCount", len(defs)).
+ Int("caveatDefinitionCount", len(parsed.Schema.CompiledSchema.CaveatDefinitions)).
+ Int("schemaDefinitionCount", len(parsed.Schema.CompiledSchema.OrderedDefinitions)).
+ Msg("adding schema definitions")
+
+ objectDefs = append(objectDefs, defs...)
+ caveatDefs = append(caveatDefs, parsed.Schema.CompiledSchema.CaveatDefinitions...)
+ }
+
+ // Parse relationships for updates.
+ for _, rel := range parsed.Relationships.Relationships {
+ updates = append(updates, tuple.Touch(rel))
+ rels = append(rels, rel)
+ }
+ }
+
+ // Load the definitions and relationships into the datastore.
+ revision, err := ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error {
+ // Write the caveat definitions.
+ err := rwt.WriteCaveats(ctx, caveatDefs)
+ if err != nil {
+ return err
+ }
+
+ res := schema.ResolverForDatastoreReader(rwt).WithPredefinedElements(schema.PredefinedElements{
+ Definitions: objectDefs,
+ Caveats: caveatDefs,
+ })
+ ts := schema.NewTypeSystem(res)
+ // Validate and write the object definitions.
+ for _, objectDef := range objectDefs {
+ ctx := dsctx.ContextWithDatastore(ctx, ds)
+ vts, terr := ts.GetValidatedDefinition(ctx, objectDef.GetName())
+ if terr != nil {
+ return terr
+ }
+
+ aerr := namespace.AnnotateNamespace(vts)
+ if aerr != nil {
+ return aerr
+ }
+
+ if err := rwt.WriteNamespaces(ctx, objectDef); err != nil {
+ return fmt.Errorf("error when loading object definition %s: %w", objectDef.Name, err)
+ }
+ }
+
+ return err
+ })
+
+ slicez.ForEachChunk(updates, 500, func(chunked []tuple.RelationshipUpdate) {
+ if err != nil {
+ return
+ }
+
+ chunkedRels := make([]tuple.Relationship, 0, len(chunked))
+ for _, update := range chunked {
+ chunkedRels = append(chunkedRels, update.Relationship)
+ }
+ revision, err = ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error {
+ err = relationships.ValidateRelationshipsForCreateOrTouch(ctx, rwt, caveatTypeSet, chunkedRels...)
+ if err != nil {
+ return err
+ }
+
+ return rwt.WriteRelationships(ctx, chunked)
+ })
+ })
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return &PopulatedValidationFile{schemaStr, objectDefs, caveatDefs, rels, files}, revision, err
+}