Zanzana: app platform style write APIs (#112812)
* refactor zanzana client instantiation * refactor client imports * POC write API (Mutate) * fix linter * delete exisitng folder parents * refactor common functions * minor refactor * groupd operations by type * atomic folder operations * use deleteExisting for deletes * Add tests for folders * more tests * resource permissions tests * add more tests * fix mock zanzana client * fix linter * fix linter * re-use types from apps * add some comments to the protobuf
This commit is contained in:
@@ -44,6 +44,36 @@ const (
|
||||
statusSubresource = "status"
|
||||
)
|
||||
|
||||
func setup(t *testing.T, srv *Server) *Server {
|
||||
// seed tuples
|
||||
tuples := []*openfgav1.TupleKey{
|
||||
common.NewResourceTuple("user:1", common.RelationGet, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewResourceTuple("user:1", common.RelationUpdate, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewGroupResourceTuple("user:2", common.RelationGet, dashboardGroup, dashboardResource, ""),
|
||||
common.NewGroupResourceTuple("user:2", common.RelationUpdate, dashboardGroup, dashboardResource, ""),
|
||||
common.NewResourceTuple("user:3", common.RelationSetView, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "", "3"),
|
||||
common.NewFolderResourceTuple("user:5", common.RelationSetEdit, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewFolderTuple("user:6", common.RelationGet, "1"),
|
||||
common.NewGroupResourceTuple("user:7", common.RelationGet, folderGroup, folderResource, ""),
|
||||
common.NewFolderParentTuple("5", "4"),
|
||||
common.NewFolderParentTuple("6", "5"),
|
||||
common.NewFolderResourceTuple("user:8", common.RelationSetEdit, dashboardGroup, dashboardResource, "", "5"),
|
||||
common.NewFolderResourceTuple("user:9", common.RelationCreate, dashboardGroup, dashboardResource, "", "5"),
|
||||
common.NewResourceTuple("user:10", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource, "10"),
|
||||
common.NewResourceTuple("user:10", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource, "11"),
|
||||
common.NewGroupResourceTuple("user:11", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource),
|
||||
common.NewFolderResourceTuple("user:12", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource, "5"),
|
||||
common.NewFolderResourceTuple("user:13", common.RelationGet, folderGroup, folderResource, statusSubresource, "5"),
|
||||
common.NewTypedResourceTuple("user:14", common.RelationGet, common.TypeTeam, teamGroup, teamResource, statusSubresource, "1"),
|
||||
common.NewTypedResourceTuple("user:15", common.RelationGet, common.TypeUser, userGroup, userResource, statusSubresource, "1"),
|
||||
common.NewTypedResourceTuple("user:16", common.RelationGet, common.TypeServiceAccount, serviceAccountGroup, serviceAccountResource, statusSubresource, "1"),
|
||||
}
|
||||
|
||||
return setupOpenFGADatabase(t, srv, tuples)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testsuite.Run(m)
|
||||
}
|
||||
@@ -64,27 +94,43 @@ func TestIntegrationServer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
srv := setup(t, testStore, cfg)
|
||||
srv := setupOpenFGAServer(t, testStore, cfg)
|
||||
t.Run("test check", func(t *testing.T) {
|
||||
setup(t, srv)
|
||||
testCheck(t, srv)
|
||||
})
|
||||
|
||||
t.Run("test list", func(t *testing.T) {
|
||||
setup(t, srv)
|
||||
testList(t, srv)
|
||||
})
|
||||
|
||||
t.Run("test list streaming", func(t *testing.T) {
|
||||
setup(t, srv)
|
||||
srv.cfg.UseStreamedListObjects = true
|
||||
testList(t, srv)
|
||||
srv.cfg.UseStreamedListObjects = false
|
||||
})
|
||||
|
||||
t.Run("test batch check", func(t *testing.T) {
|
||||
setup(t, srv)
|
||||
testBatchCheck(t, srv)
|
||||
})
|
||||
|
||||
t.Run("test mutate", func(t *testing.T) {
|
||||
testMutate(t, srv)
|
||||
})
|
||||
|
||||
t.Run("test mutate folders", func(t *testing.T) {
|
||||
testMutateFolders(t, srv)
|
||||
})
|
||||
|
||||
t.Run("test mutate resource permissions", func(t *testing.T) {
|
||||
testMutateResourcePermissions(t, srv)
|
||||
})
|
||||
}
|
||||
|
||||
func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
||||
func setupOpenFGAServer(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
||||
t.Helper()
|
||||
|
||||
store, err := store.NewEmbeddedStore(cfg, testDB, log.NewNopLogger())
|
||||
@@ -95,38 +141,25 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
||||
srv, err := NewServer(cfg.ZanzanaServer, openfga, log.NewNopLogger(), tracing.NewNoopTracerService(), prometheus.NewRegistry())
|
||||
require.NoError(t, err)
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
func setupOpenFGADatabase(t *testing.T, srv *Server, tuples []*openfgav1.TupleKey) *Server {
|
||||
t.Helper()
|
||||
|
||||
storeInf, err := srv.getStoreInfo(context.Background(), namespace)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Clean up any existing store
|
||||
_, err = srv.openfga.DeleteStore(context.Background(), &openfgav1.DeleteStoreRequest{
|
||||
StoreId: storeInf.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// seed tuples
|
||||
writes := &openfgav1.WriteRequestWrites{
|
||||
TupleKeys: []*openfgav1.TupleKey{
|
||||
common.NewResourceTuple("user:1", common.RelationGet, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewResourceTuple("user:1", common.RelationUpdate, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewGroupResourceTuple("user:2", common.RelationGet, dashboardGroup, dashboardResource, ""),
|
||||
common.NewGroupResourceTuple("user:2", common.RelationUpdate, dashboardGroup, dashboardResource, ""),
|
||||
common.NewResourceTuple("user:3", common.RelationSetView, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "", "3"),
|
||||
common.NewFolderResourceTuple("user:5", common.RelationSetEdit, dashboardGroup, dashboardResource, "", "1"),
|
||||
common.NewFolderTuple("user:6", common.RelationGet, "1"),
|
||||
common.NewGroupResourceTuple("user:7", common.RelationGet, folderGroup, folderResource, ""),
|
||||
common.NewFolderParentTuple("5", "4"),
|
||||
common.NewFolderParentTuple("6", "5"),
|
||||
common.NewFolderResourceTuple("user:8", common.RelationSetEdit, dashboardGroup, dashboardResource, "", "5"),
|
||||
common.NewFolderResourceTuple("user:9", common.RelationCreate, dashboardGroup, dashboardResource, "", "5"),
|
||||
common.NewResourceTuple("user:10", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource, "10"),
|
||||
common.NewResourceTuple("user:10", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource, "11"),
|
||||
common.NewGroupResourceTuple("user:11", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource),
|
||||
common.NewFolderResourceTuple("user:12", common.RelationGet, dashboardGroup, dashboardResource, statusSubresource, "5"),
|
||||
common.NewFolderResourceTuple("user:13", common.RelationGet, folderGroup, folderResource, statusSubresource, "5"),
|
||||
common.NewTypedResourceTuple("user:14", common.RelationGet, common.TypeTeam, teamGroup, teamResource, statusSubresource, "1"),
|
||||
common.NewTypedResourceTuple("user:15", common.RelationGet, common.TypeUser, userGroup, userResource, statusSubresource, "1"),
|
||||
common.NewTypedResourceTuple("user:16", common.RelationGet, common.TypeServiceAccount, serviceAccountGroup, serviceAccountResource, statusSubresource, "1"),
|
||||
},
|
||||
}
|
||||
for _, w := range writes.TupleKeys {
|
||||
t.Log(w.String())
|
||||
TupleKeys: tuples,
|
||||
OnDuplicate: "ignore",
|
||||
}
|
||||
|
||||
// First, try to delete any existing tuples to avoid conflicts
|
||||
@@ -140,16 +173,18 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
||||
}
|
||||
|
||||
// Try to delete existing tuples (ignore errors if they don't exist)
|
||||
_, _ = openfga.Write(context.Background(), &openfgav1.WriteRequest{
|
||||
_, err = srv.openfga.Write(context.Background(), &openfgav1.WriteRequest{
|
||||
StoreId: storeInf.ID,
|
||||
AuthorizationModelId: storeInf.ModelID,
|
||||
Deletes: &openfgav1.WriteRequestDeletes{
|
||||
TupleKeys: deletes,
|
||||
OnMissing: "ignore",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now write the new tuples
|
||||
_, err = openfga.Write(context.Background(), &openfgav1.WriteRequest{
|
||||
_, err = srv.openfga.Write(context.Background(), &openfgav1.WriteRequest{
|
||||
StoreId: storeInf.ID,
|
||||
AuthorizationModelId: storeInf.ModelID,
|
||||
Writes: writes,
|
||||
|
||||
Reference in New Issue
Block a user