Skip to content

Commit

Permalink
Merge pull request #86 from josephschorr/validate
Browse files Browse the repository at this point in the history
  • Loading branch information
jzelinskie authored Feb 15, 2022
2 parents 3923a38 + c1ac13d commit 2459e97
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 48 deletions.
7 changes: 5 additions & 2 deletions cmd/zed/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ var importCmd = &cobra.Command{
From a devtools instance:
zed import https://localhost:8443/download
From a local file:
From a local file (with prefix):
zed import file:///Users/zed/Downloads/authzed-x7izWU8_2Gw3.yaml
From a local file (no prefix):
zed import authzed-x7izWU8_2Gw3.yaml
Only schema:
zed import --relationships=false file:///Users/zed/Downloads/authzed-x7izWU8_2Gw3.yaml
Expand Down Expand Up @@ -81,7 +84,7 @@ func importCmdFunc(cmd *cobra.Command, args []string) error {
return err
}
var p decode.SchemaRelationships
if err := decoder(&p); err != nil {
if _, err := decoder(&p); err != nil {
return err
}

Expand Down
1 change: 1 addition & 0 deletions cmd/zed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func main() {
registerRelationshipCmd(rootCmd)
registerExperimentCmd(rootCmd)
registerImportCmd(rootCmd)
registerValidateCmd(rootCmd)

if err := rootCmd.Execute(); err != nil {
os.Exit(1)
Expand Down
209 changes: 209 additions & 0 deletions cmd/zed/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package main

import (
"context"
"errors"
"fmt"
"net/url"
"os"
"strings"

"github.com/jzelinskie/cobrautil"
"github.com/spf13/cobra"

v0 "github.com/authzed/authzed-go/proto/authzed/api/v0"
"github.com/authzed/spicedb/pkg/development"
"github.com/authzed/spicedb/pkg/tuple"
"github.com/authzed/spicedb/pkg/validationfile"
"github.com/authzed/spicedb/pkg/validationfile/blocks"
"github.com/charmbracelet/lipgloss"

"github.com/authzed/zed/internal/decode"
)

var (
success = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("10")).Render("Success!")
errorPrefix = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("9")).Render("error: ")
errorMessageStyle = lipgloss.NewStyle().Bold(true).Width(80)
linePrefixStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("12"))
highlightedSourceStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9"))
highlightedLineStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9"))
codeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
highlightedCodeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("15"))
)

func registerValidateCmd(rootCmd *cobra.Command) {
rootCmd.AddCommand(validateCmd)
}

var validateCmd = &cobra.Command{
Use: "validate <validation_file>",
Short: "validate the given validation file",
Example: `
From a local file (with prefix):
zed validate file:///Users/zed/Downloads/authzed-x7izWU8_2Gw3.yaml
From a local file (no prefix):
zed validate authzed-x7izWU8_2Gw3.yaml
From a gist:
zed validate https://gist.github.com/ecordell/8e3b613a677e3c844742cf24421c08b6
From a playground link:
zed validate https://play.authzed.com/s/iksdFvCtvnkR/schema
From pastebin:
zed validate https://pastebin.com/8qU45rVK
From a devtools instance:
zed validate https://localhost:8443/download`,
Args: cobra.ExactArgs(1),
RunE: cobrautil.CommandStack(LogCmdFunc, validateCmdFunc),
}

func validateCmdFunc(cmd *cobra.Command, args []string) error {
// Parse the URL of the validation document to import.
u, err := url.Parse(args[0])
if err != nil {
return err
}

decoder, err := decode.DecoderForURL(u)
if err != nil {
return err
}

// Decode the validation document.
var parsed validationfile.ValidationFile
validateContents, err := decoder(&parsed)
if err != nil {
var errWithSource blocks.ErrorWithSource
if errors.As(err, &errWithSource) {
ouputErrorWithSource(validateContents, errWithSource)
}

return err
}

// Create the development context.
ctx := context.Background()
tuples := make([]*v0.RelationTuple, 0, len(parsed.Relationships.Relationships))
for _, rel := range parsed.Relationships.Relationships {
tuples = append(tuples, tuple.MustFromRelationship(rel))
}
devCtx, devErrs, err := development.NewDevContext(ctx, &v0.RequestContext{
Schema: parsed.Schema.Schema,
Relationships: tuples,
})
if err != nil {
return err
}
if devErrs != nil {
outputDeveloperErrors(validateContents, devErrs)
}

// Run assertions.
adevErrs, aerr := development.RunAllAssertions(devCtx, &parsed.Assertions)
if aerr != nil {
return aerr
}
if adevErrs != nil {
outputDeveloperErrors(validateContents, adevErrs)
}

// Run expected relations.
_, erDevErrs, rerr := development.RunValidation(devCtx, &parsed.ExpectedRelations)
if rerr != nil {
return rerr
}
if erDevErrs != nil {
outputDeveloperErrors(validateContents, erDevErrs)
}

fmt.Print(success)
fmt.Printf(" - %d relationships loaded, %d assertions run, %d expected relations validated\n",
len(tuples),
len(parsed.Assertions.AssertTrue)+len(parsed.Assertions.AssertFalse),
len(parsed.ExpectedRelations.ValidationMap),
)
return nil
}

func ouputErrorWithSource(validateContents []byte, errWithSource blocks.ErrorWithSource) {
lines := strings.Split(string(validateContents), "\n")

fmt.Printf("%s%s\n", errorPrefix, errorMessageStyle.Render(errWithSource.Error()))
errorLineNumber := int(errWithSource.LineNumber) - 1 // errWithSource.LineNumber is 1-indexed
for i := errorLineNumber - 3; i < errorLineNumber+3; i++ {
if i == errorLineNumber {
renderLine(lines, i, errWithSource.Source, errorLineNumber)
} else {
renderLine(lines, i, "", errorLineNumber)
}
}
os.Exit(1)
}

func outputDeveloperErrors(validateContents []byte, devErrors *development.DeveloperErrors) {
lines := strings.Split(string(validateContents), "\n")

for _, inputErr := range devErrors.InputErrors {
outputDeveloperError(inputErr, lines)
}

for _, validationErr := range devErrors.ValidationErrors {
outputDeveloperError(validationErr, lines)
}

os.Exit(1)
}

func outputDeveloperError(devError *v0.DeveloperError, lines []string) {
fmt.Printf("%s %s\n", errorPrefix, errorMessageStyle.Render(devError.Message))
errorLineNumber := int(devError.Line) - 1 // devError.Line is 1-indexed
for i := errorLineNumber - 3; i < errorLineNumber+3; i++ {
if i == errorLineNumber {
renderLine(lines, i, devError.Context, errorLineNumber)
} else {
renderLine(lines, i, "", errorLineNumber)
}
}

fmt.Printf("\n\n")
}

func renderLine(lines []string, index int, highlight string, highlightLineIndex int) {
if index < 0 || index >= len(lines) {
return
}

lineNumberLength := len(fmt.Sprintf("%d", len(lines)))
lineContents := lines[index]
highlightIndex := strings.Index(lineContents, highlight)
lineNumberStr := fmt.Sprintf("%d", index+1)
spacer := strings.Repeat(" ", lineNumberLength)

lineNumberStyle := linePrefixStyle
lineContentsStyle := codeStyle
if index == highlightLineIndex {
lineNumberStyle = highlightedLineStyle
lineContentsStyle = highlightedCodeStyle
}

if highlightIndex < 0 || len(highlight) == 0 {
fmt.Printf(" %s | %s\n", lineNumberStyle.Render(lineNumberStr), lineContentsStyle.Render(lineContents))
} else {
fmt.Printf(" %s | %s%s%s\n",
lineNumberStyle.Render(lineNumberStr),
lineContentsStyle.Render(lineContents[0:highlightIndex]),
highlightedSourceStyle.Render(highlight),
lineContentsStyle.Render(lineContents[highlightIndex+len(highlight):]),
)
fmt.Printf(" %s | %s%s%s\n",
lineNumberStyle.Render(spacer),
strings.Repeat(" ", highlightIndex),
highlightedSourceStyle.Render("^"),
highlightedSourceStyle.Render(strings.Repeat("~", len(highlight)-1)),
)
}
}
16 changes: 5 additions & 11 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,23 @@ require (
github.com/99designs/keyring v1.1.6
github.com/AlecAivazis/survey/v2 v2.3.2
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
github.com/authzed/authzed-go v0.4.2-0.20220112232750-c1a3c1b2d699
github.com/authzed/authzed-go v0.4.2-0.20220207230446-c781fa7fa969
github.com/authzed/connector-postgresql v0.2.1-0.20211110161636-5a22597732ae
github.com/authzed/grpcutil v0.0.0-20211020204402-aba1876830e6
github.com/authzed/spicedb v1.4.0
github.com/authzed/grpcutil v0.0.0-20220104222419-f813f77722e5
github.com/authzed/spicedb v1.4.1-0.20220214225827-aea171d69d33
github.com/charmbracelet/lipgloss v0.4.0
github.com/cockroachdb/cockroach v20.1.17+incompatible
github.com/go-logr/stdr v1.2.2 // indirect
github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519 // indirect
github.com/jzelinskie/cobrautil v0.0.8-0.20220111193536-0ffd41c0b812
github.com/jzelinskie/stringz v0.0.1
github.com/mitchellh/go-homedir v1.1.0
github.com/olekukonko/tablewriter v0.0.5
github.com/open-policy-agent/opa v0.36.1
github.com/rs/zerolog v1.26.1
github.com/spf13/afero v1.8.0 // indirect
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1 // indirect
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/otel/exporters/jaeger v1.3.0 // indirect
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998 // indirect
google.golang.org/grpc v1.43.0
google.golang.org/grpc v1.44.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
tailscale.com v1.20.2
Expand Down
Loading

0 comments on commit 2459e97

Please sign in to comment.