Skip to content

Commit

Permalink
Merge pull request #8 from aserto-dev/add_diff_validate
Browse files Browse the repository at this point in the history
Add validation for Diff struct
  • Loading branch information
oanatmaria authored Nov 3, 2023
2 parents ba54407 + 316525f commit 943935c
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 36 deletions.
5 changes: 4 additions & 1 deletion Depfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ go:
version: "v1.10.1"
golangci-lint:
importPath: "github.com/golangci/golangci-lint/cmd/golangci-lint"
version: "v1.53.3"
version: "v1.54.1"
mockgen:
importPath: github.com/golang/mock/mockgen
version: "v1.6.0"
8 changes: 8 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"sync"

"github.com/aserto-dev/azm/model"
"github.com/aserto-dev/azm/model/diff"
)

type Cache struct {
Expand All @@ -27,6 +28,13 @@ func (c *Cache) UpdateModel(m *model.Model) error {
return nil
}

// Returns a diff struct resulted between the old and the new model.
func (c *Cache) Diff(other *model.Model) *diff.Diff {
c.mtx.Lock()
defer c.mtx.Unlock()
return c.model.Diff(other)
}

// ObjectExists, checks if given object type name exists in the model cache.
func (c *Cache) ObjectExists(on model.ObjectName) bool {
c.mtx.RLock()
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ require (
github.com/aserto-dev/errors v0.0.6
github.com/aserto-dev/go-aserto v0.20.4
github.com/aserto-dev/go-directory v0.30.0
github.com/golang/mock v1.1.1
github.com/hashicorp/go-multierror v1.1.1
github.com/magefile/mage v1.15.0
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1
github.com/pkg/errors v0.9.1
Expand All @@ -26,6 +28,7 @@ require (
github.com/google/uuid v1.3.1 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand All @@ -46,6 +47,10 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down
5 changes: 5 additions & 0 deletions magefiles/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ func Test() error {
func Deps() {
deps.GetAllDeps()
}

// Generate generates all code.
func Generate() error {
return common.Generate()
}
100 changes: 100 additions & 0 deletions model/diff/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package diff

import (
"github.com/aserto-dev/go-directory/pkg/derr"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/samber/lo"
)

//go:generate go run github.com/golang/mock/mockgen -destination=mock_instances.go -package=diff github.com/aserto-dev/azm/model/diff Instances

type Diff struct {
Added Changes
Removed Changes
}

type Changes struct {
Objects []string
Relations map[string][]string
}

// Only the types of the relation instances are needed.
type RelationKind struct {
Object string
Relation string
Subject string
SubjectRelation string
}

type Instances interface {
ObjectTypes() ([]string, error)
RelationTypes() ([]*RelationKind, error)
}

func (d *Diff) Validate(dv Instances) error {
var errs error
var rels []*RelationKind
if len(d.Removed.Objects) > 0 {
objs, err := dv.ObjectTypes()
if err != nil {
return err
}

rels, err = dv.RelationTypes()
if err != nil {
return err
}

err = d.validateObjectTypes(objs, rels)
errs = multierror.Append(errs, err)
}

if len(d.Removed.Relations) > 0 {
var err error
if len(rels) == 0 {
rels, err = dv.RelationTypes()
if err != nil {
return err
}

}
err = d.validateRelationsTypes(rels)
errs = multierror.Append(errs, err)
}

if merr, ok := errs.(*multierror.Error); ok && len(merr.Errors) > 0 {
return errs
}

return nil
}

func (d *Diff) validateObjectTypes(objs []string, rels []*RelationKind) error {
var errs error
for _, objType := range d.Removed.Objects {
if lo.Contains(objs, objType) {
errs = multierror.Append(errs, errors.Wrapf(derr.ErrObjectTypeInUse, "object type [%s]", objType))
}
rel, found := lo.Find(rels, func(rel *RelationKind) bool { return rel.Object == objType || rel.Subject == objType })
if found {
errs = multierror.Append(errs, errors.Wrapf(derr.ErrRelationTypeInUse, "object type [%s], relation type [%s]", objType, rel.Relation))
}
}
return errs
}

func (d *Diff) validateRelationsTypes(relations []*RelationKind) error {
var errs error
for objType, rels := range d.Removed.Relations {
for _, rel := range rels {
_, found := lo.Find(relations, func(rl *RelationKind) bool {
return (rl.Object == objType && rl.Relation == rel) || (rl.Subject == objType && rl.SubjectRelation == rel)
})
if found {
errs = multierror.Append(errs, errors.Wrapf(derr.ErrRelationTypeInUse, "object type [%s], relation type [%s]", objType, rel))
}
}
}
return errs
}
83 changes: 83 additions & 0 deletions model/diff/diff_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package diff_test

import (
"errors"
"testing"

"github.com/aserto-dev/azm/model/diff"
"github.com/aserto-dev/go-directory/pkg/derr"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
)

var ErrBoom = errors.New("Boom")

func TestValidateDiffNoDeletion(t *testing.T) {
ctrl := gomock.NewController(t)
mockInstances := diff.NewMockInstances(ctrl)

dif := diff.Diff{Removed: diff.Changes{}, Added: diff.Changes{}}
err := dif.Validate(mockInstances)

require.NoError(t, err)
}

func TestValidateDiffWithObjectTypeDeletion(t *testing.T) {
ctrl := gomock.NewController(t)
mockInstances := diff.NewMockInstances(ctrl)
objType := "user"

dif := diff.Diff{Removed: diff.Changes{Objects: []string{objType}}, Added: diff.Changes{}}

mockInstances.EXPECT().ObjectTypes().Return([]string{}, nil)
mockInstances.EXPECT().RelationTypes().Return([]*diff.RelationKind{}, nil)
err := dif.Validate(mockInstances)

require.NoError(t, err)
}

func TestValidateDiffWith2ObjectTypeDeletion(t *testing.T) {
ctrl := gomock.NewController(t)
mockInstances := diff.NewMockInstances(ctrl)
objTypes := []string{"user", "member"}

dif := diff.Diff{Removed: diff.Changes{Objects: objTypes}, Added: diff.Changes{}}

mockInstances.EXPECT().ObjectTypes().Return([]string{"user"}, nil)
mockInstances.EXPECT().RelationTypes().Return([]*diff.RelationKind{}, nil)
err := dif.Validate(mockInstances)

require.Error(t, err)
require.Contains(t, err.Error(), derr.ErrObjectTypeInUse.Message)
}

func TestValidateDiffWithRelationTypeDeletion(t *testing.T) {
ctrl := gomock.NewController(t)
mockInstances := diff.NewMockInstances(ctrl)
objTypes := []string{"user", "member"}
relationTypes := map[string][]string{"folder": {"parent_folder"}}

dif := diff.Diff{Removed: diff.Changes{Objects: objTypes, Relations: relationTypes}, Added: diff.Changes{}}

mockInstances.EXPECT().ObjectTypes().Return([]string{}, nil)
mockInstances.EXPECT().RelationTypes().Return([]*diff.RelationKind{{Object: "folder", Relation: "parent_folder"}}, nil)
err := dif.Validate(mockInstances)

require.Error(t, err)
require.Contains(t, err.Error(), derr.ErrRelationTypeInUse.Message)
}

func TestValidateDiffWithObjectInstances(t *testing.T) {
ctrl := gomock.NewController(t)
mockInstances := diff.NewMockInstances(ctrl)
objTypes := []string{"user", "member"}
relationTypes := map[string][]string{"folder": {"parent_folder"}}

dif := diff.Diff{Removed: diff.Changes{Objects: objTypes, Relations: relationTypes}, Added: diff.Changes{}}

mockInstances.EXPECT().ObjectTypes().Return([]string{}, ErrBoom)
err := dif.Validate(mockInstances)

require.Error(t, err)
require.Contains(t, err.Error(), ErrBoom.Error())
}
63 changes: 63 additions & 0 deletions model/diff/mock_instances.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 943935c

Please sign in to comment.