-
Notifications
You must be signed in to change notification settings - Fork 246
/
loader.go
141 lines (118 loc) · 4.67 KB
/
loader.go
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package validationfile
import (
"context"
"fmt"
"os"
"strings"
v0 "github.com/authzed/authzed-go/proto/authzed/api/v0"
"github.com/authzed/spicedb/internal/datastore"
"github.com/rs/zerolog/log"
"github.com/shopspring/decimal"
"google.golang.org/protobuf/encoding/prototext"
"github.com/authzed/spicedb/pkg/schemadsl/compiler"
"github.com/authzed/spicedb/pkg/schemadsl/input"
"github.com/authzed/spicedb/pkg/tuple"
)
// FullyParsedValidationFile contains the fully parsed information from a validation file.
type FullyParsedValidationFile struct {
// NamespaceDefinitions are the namespaces defined in the validation file, in either
// direct or compiled from schema form.
NamespaceDefinitions []*v0.NamespaceDefinition
// Tuples are the relation tuples defined in the validation file, either directly
// or in the relationships block.
Tuples []*v0.RelationTuple
}
// PopulateFromFiles populates the given datastore with the namespaces and tuples found in
// the validation file(s) specified.
func PopulateFromFiles(ds datastore.Datastore, filePaths []string) (*FullyParsedValidationFile, decimal.Decimal, error) {
var revision decimal.Decimal
nsDefs := []*v0.NamespaceDefinition{}
tuples := []*v0.RelationTuple{}
for _, filePath := range filePaths {
fileContents, err := os.ReadFile(filePath)
if err != nil {
return nil, decimal.Zero, err
}
parsed, err := ParseValidationFile(fileContents)
if err != nil {
return nil, decimal.Zero, fmt.Errorf("Error when parsing config file %s: %w", filePath, err)
}
// Parse the schema, if any.
if parsed.Schema != "" {
defs, err := compiler.Compile([]compiler.InputSchema{
{input.InputSource(filePath), parsed.Schema},
}, nil)
if err != nil {
return nil, decimal.Zero, fmt.Errorf("Error when parsing schema in config file %s: %w", filePath, err)
}
log.Info().Str("filePath", filePath).Int("schemaDefinitionCount", len(defs)).Msg("Loading schema definitions")
for index, nsDef := range defs {
nsDefs = append(nsDefs, nsDef)
log.Info().Str("filePath", filePath).Str("namespaceName", nsDef.Name).Msg("Loading namespace")
_, lnerr := ds.WriteNamespace(context.Background(), nsDef)
if lnerr != nil {
return nil, decimal.Zero, fmt.Errorf("Error when loading namespace config #%v from file %s: %w", index, filePath, lnerr)
}
}
}
// Load the namespace configs.
log.Info().Str("filePath", filePath).Int("namespaceCount", len(parsed.NamespaceConfigs)).Msg("Loading namespaces")
for index, namespaceConfig := range parsed.NamespaceConfigs {
nsDef := v0.NamespaceDefinition{}
nerr := prototext.Unmarshal([]byte(namespaceConfig), &nsDef)
if nerr != nil {
return nil, decimal.Zero, fmt.Errorf("Error when parsing namespace config #%v from file %s: %w", index, filePath, nerr)
}
nsDefs = append(nsDefs, &nsDef)
log.Info().Str("filePath", filePath).Str("namespaceName", nsDef.Name).Msg("Loading namespace")
_, lnerr := ds.WriteNamespace(context.Background(), &nsDef)
if lnerr != nil {
return nil, decimal.Zero, fmt.Errorf("Error when loading namespace config #%v from file %s: %w", index, filePath, lnerr)
}
}
// Load the validation tuples/relationships.
var updates []*v0.RelationTupleUpdate
seenTuples := map[string]bool{}
relationships := parsed.Relationships
if relationships != "" {
lines := strings.Split(relationships, "\n")
for index, line := range lines {
trimmed := strings.TrimSpace(line)
if len(trimmed) == 0 {
continue
}
tpl := tuple.Scan(trimmed)
if tpl == nil {
return nil, decimal.Zero, fmt.Errorf("Error parsing relationship #%v: %s", index, trimmed)
}
_, ok := seenTuples[tuple.String(tpl)]
if ok {
continue
}
seenTuples[tuple.String(tpl)] = true
tuples = append(tuples, tpl)
updates = append(updates, tuple.Create(tpl))
}
}
log.Info().Str("filePath", filePath).Int("tupleCount", len(updates)+len(parsed.ValidationTuples)).Msg("Loading test data")
for index, validationTuple := range parsed.ValidationTuples {
tpl := tuple.Scan(validationTuple)
if tpl == nil {
return nil, decimal.Zero, fmt.Errorf("Error parsing validation tuple #%v: %s", index, validationTuple)
}
_, ok := seenTuples[tuple.String(tpl)]
if ok {
continue
}
seenTuples[tuple.String(tpl)] = true
tuples = append(tuples, tpl)
updates = append(updates, tuple.Create(tpl))
}
wrevision, terr := ds.WriteTuples(context.Background(), []*v0.RelationTuple{}, updates)
if terr != nil {
return nil, decimal.Zero, fmt.Errorf("Error when loading validation tuples from file %s: %w", filePath, terr)
}
revision = wrevision
}
return &FullyParsedValidationFile{nsDefs, tuples}, revision, nil
}