Repository: casbin/casbin
Pull request #697 (2021-01-30 09:20:03)

feat: Add BatchEnforce() API
fix: #617 

hsluoyz: @RobotHuang plz fix:

![image](https://user-images.githubusercontent.com/3787410/106353510-0ba1eb00-6326-11eb-9d35-fc5cfe6e1f17.png)


Patch:
diff --git a/README.md b/README.md
index b2c7bb7..142ed62 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@ Casbin
 ====
 
 [![Go Report Card](https://goreportcard.com/badge/github.com/casbin/casbin)](https://goreportcard.com/report/github.com/casbin/casbin)
-[![Build Status](https://travis-ci.com/casbin/casbin.svg?branch=master)](https://travis-ci.com/casbin/casbin)
+[![Build Status](https://travis-ci.org/casbin/casbin.svg?branch=master)](https://travis-ci.org/casbin/casbin)
 [![Coverage Status](https://coveralls.io/repos/github/casbin/casbin/badge.svg?branch=master)](https://coveralls.io/github/casbin/casbin?branch=master)
 [![Godoc](https://godoc.org/github.com/casbin/casbin?status.svg)](https://pkg.go.dev/github.com/casbin/casbin/v2)
 [![Release](https://img.shields.io/github/release/casbin/casbin.svg)](https://github.com/casbin/casbin/releases/latest)
diff --git a/enforcer.go b/enforcer.go
index 494930a..a1e427f 100644
--- a/enforcer.go
+++ b/enforcer.go
@@ -610,6 +610,32 @@ func (e *Enforcer) EnforceExWithMatcher(matcher string, rvals ...interface{}) (b
 	return result, explain, err
 }
 
+// BatchEnforce enforce in batches
+func (e *Enforcer) BatchEnforce(requests [][]interface{}) ([]bool, error) {
+	var results []bool
+	for _, request := range requests {
+		result, err := e.enforce("", nil, request...)
+		if err != nil {
+			return results, err
+		}
+		results = append(results, result)
+	}
+	return results, nil
+}
+
+// BatchEnforceWithMatcher enforce with matcher in batches
+func (e *Enforcer) BatchEnforceWithMatcher(matcher string, requests [][]interface{}) ([]bool, error) {
+	var results []bool
+	for _, request := range requests {
+		result, err := e.enforce(matcher, nil, request...)
+		if err != nil {
+			return results, err
+		}
+		results = append(results, result)
+	}
+	return results, nil
+}
+
 // AddNamedMatchingFunc add MatchingFunc by ptype RoleManager
 func (e *Enforcer) AddNamedMatchingFunc(ptype, name string, fn defaultrolemanager.MatchingFunc) bool {
 	if rm, ok := e.rmMap[ptype]; ok {
diff --git a/internal_api.go b/internal_api.go
index a02a0ad..303c5e9 100644
--- a/internal_api.go
+++ b/internal_api.go
@@ -186,48 +186,6 @@ func (e *Enforcer) updatePolicy(sec string, ptype string, oldRule []string, newR
 	return ruleUpdated, nil
 }
 
-func (e *Enforcer) updatePolicies(sec string, ptype string , oldRules [][]string, newRules [][]string) (bool, error) {
-	if e.dispatcher != nil && e.autoNotifyDispatcher {
-		return true, e.dispatcher.UpdatePolicies(sec, ptype, oldRules, newRules)
-	}
-	
-	if e.shouldPersist() {
-		if err := e.adapter.(persist.UpdatableAdapter).UpdatePolicies(sec, ptype, oldRules, newRules); err != nil {
-			if err.Error() != notImplemented {
-				return false, err
-			}
-		}
-	}
-
-	ruleUpdated := e.model.UpdatePolicies(sec, ptype, oldRules, newRules)
-	if !ruleUpdated {
-		return ruleUpdated, nil
-	}
-
-	if sec == "g" {
-		err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, oldRules) // remove the old rules
-		if err != nil {
-			return ruleUpdated, err
-		}
-		err = e.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, newRules) // add the new rules
-		if err != nil {
-			return ruleUpdated, err
-		}
-	}
-
-	if e.watcher != nil && e.autoNotifyWatcher {
-		var err error
-		if watcher, ok := e.watcher.(persist.WatcherUpdatable); ok {
-			err = watcher.UpdateForUpdatePolicies(oldRules, newRules)
-		} else {
-			err = e.watcher.Update()
-		}
-		return ruleUpdated, err
-	}
-
-	return ruleUpdated, nil
-}
-
 // removePolicies removes rules from the current policy.
 func (e *Enforcer) removePolicies(sec string, ptype string, rules [][]string) (bool, error) {
 	if !e.model.HasPolicies(sec, ptype, rules) {
diff --git a/management_api.go b/management_api.go
index d1ddf24..5c85d01 100644
--- a/management_api.go
+++ b/management_api.go
@@ -166,15 +166,6 @@ func (e *Enforcer) UpdateNamedPolicy(ptype string, p1 []string, p2 []string) (bo
 	return e.updatePolicy("p", ptype, p1, p2)
 }
 
-// UpdatePolicies updates authorization rules from the current policies.
-func (e *Enforcer) UpdatePolicies(oldPolices [][]string, newPolicies [][]string) (bool, error) {
-	return e.UpdateNamedPolicies("p", oldPolices, newPolicies)
-}
-
-func (e *Enforcer) UpdateNamedPolicies(ptype string, p1 [][]string, p2 [][]string) (bool, error) {
-	return e.updatePolicies("p", ptype, p1, p2)
-}
-
 // RemovePolicies removes authorization rules from the current policy.
 func (e *Enforcer) RemovePolicies(rules [][]string) (bool, error) {
 	return e.RemoveNamedPolicies("p", rules)
diff --git a/model/policy.go b/model/policy.go
index 713b4f6..bfce803 100644
--- a/model/policy.go
+++ b/model/policy.go
@@ -200,43 +200,6 @@ func (model Model) UpdatePolicy(sec string, ptype string, oldRule []string, newR
 	return true
 }
 
-// UpdatePolicies updates a policy rule from the model.
-func (model Model) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) bool {
-	rollbackFlag := false
-	// index -> []{oldIndex, newIndex}
-	modifiedRuleIndex := make(map[int][]int)
-	// rollback
-	defer func() {
-		if rollbackFlag {
-			for index, oldNewIndex := range modifiedRuleIndex {
-				model[sec][ptype].Policy[index] = oldRules[oldNewIndex[0]]
-				oldPolicy := strings.Join(oldRules[oldNewIndex[0]], DefaultSep)
-				newPolicy := strings.Join(newRules[oldNewIndex[1]], DefaultSep)
-				delete(model[sec][ptype].PolicyMap, newPolicy)
-				model[sec][ptype].PolicyMap[oldPolicy] = index
-			}
-		}
-	}()
-
-	newIndex := 0
-	for oldIndex, oldRule := range oldRules {
-		oldPolicy := strings.Join(oldRule, DefaultSep)
-		index, ok := model[sec][ptype].PolicyMap[oldPolicy]
-		if !ok {
-			rollbackFlag = true
-			return false
-		}
-
-		model[sec][ptype].Policy[index] = newRules[newIndex]
-		delete(model[sec][ptype].PolicyMap, oldPolicy)
-		model[sec][ptype].PolicyMap[strings.Join(newRules[newIndex], DefaultSep)] = index
-		modifiedRuleIndex[index] = []int{oldIndex, newIndex}
-		newIndex++
-	}
-
-	return true
-}
-
 // RemovePolicies removes policy rules from the model.
 func (model Model) RemovePolicies(sec string, ptype string, rules [][]string) bool {
 	effected := model.RemovePoliciesWithEffected(sec, ptype, rules)
diff --git a/persist/dispatcher.go b/persist/dispatcher.go
index f0e6d71..671154b 100644
--- a/persist/dispatcher.go
+++ b/persist/dispatcher.go
@@ -26,6 +26,4 @@ type Dispatcher interface {
 	ClearPolicy() error
 	// UpdatePolicy updates policy rule from all instance.
 	UpdatePolicy(sec string, ptype string, oldRule, newRule []string) error
-	// UpdatePolicies updates some policy rules from all instance
-	UpdatePolicies(sec string, ptype string, oldrules, newRules [][]string) error
 }
diff --git a/persist/file-adapter/adapter.go b/persist/file-adapter/adapter.go
index 52aba20..8f243da 100644
--- a/persist/file-adapter/adapter.go
+++ b/persist/file-adapter/adapter.go
@@ -36,10 +36,6 @@ func (a *Adapter) UpdatePolicy(sec string, ptype string, oldRule, newPolicy []st
 	return errors.New("not implemented")
 }
 
-func (a *Adapter) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error {
-	return errors.New("not implemented")
-}
-
 // NewAdapter is the constructor for Adapter.
 func NewAdapter(filePath string) *Adapter {
 	return &Adapter{filePath: filePath}
diff --git a/persist/file-adapter/adapter_mock.go b/persist/file-adapter/adapter_mock.go
index 55dd131..5512e35 100644
--- a/persist/file-adapter/adapter_mock.go
+++ b/persist/file-adapter/adapter_mock.go
@@ -109,10 +109,6 @@ func (a *AdapterMock) UpdatePolicy(sec string, ptype string, oldRule, newPolicy
 	return a.GetMockErr()
 }
 
-func (a *AdapterMock) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error {
-	return a.GetMockErr()
-}
-
 // RemoveFilteredPolicy removes policy rules that match the filter from the storage.
 func (a *AdapterMock) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
 	return a.GetMockErr()
diff --git a/persist/update_adapter.go b/persist/update_adapter.go
index 00c1b51..fb0487b 100644
--- a/persist/update_adapter.go
+++ b/persist/update_adapter.go
@@ -20,6 +20,4 @@ type UpdatableAdapter interface {
 	// UpdatePolicy updates a policy rule from storage.
 	// This is part of the Auto-Save feature.
 	UpdatePolicy(sec string, ptype string, oldRule, newPolicy []string) error
-	// UpdatePolicies updates some policy rules to storage, like db, redis.
-	UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error
 }
diff --git a/persist/watcher_update.go b/persist/watcher_update.go
index 4f2ae63..c7dd216 100644
--- a/persist/watcher_update.go
+++ b/persist/watcher_update.go
@@ -20,7 +20,4 @@ type WatcherUpdatable interface {
 	// UPdateForUpdaticy calls the update callback of other instances to synchronize their policy.
 	// It is called after Enforcer.UpdatePolicy()
 	UpdateForUpdatePolicy(oldRule, newRule []string) error
-	// UPdateForUpdaticies calls the update callback of other instances to synchronize their policy.
-	// It is called after Enforcer.UpdatePolicies()
-	UpdateForUpdatePolicies(oldRules, newRules [][]string) error
 }


Test patch:
diff --git a/enforcer_test.go b/enforcer_test.go
index 9225803..e796294 100644
--- a/enforcer_test.go
+++ b/enforcer_test.go
@@ -479,3 +479,23 @@ func TestEnforceExLog(t *testing.T) {
 	testEnforceEx(t, e, "bob", "data2", "read", []string{})
 	testEnforceEx(t, e, "bob", "data2", "write", []string{"bob", "data2", "write"})
 }
+
+func testBatchEnforce(t *testing.T, e *Enforcer, requests [][]interface{}, results []bool) {
+	t.Helper()
+	myRes, _ := e.BatchEnforce(requests)
+	if len(myRes) != len(results) {
+		t.Errorf("%v supposed to be %v", myRes, results)
+	}
+	for i, v := range myRes {
+		if v != results[i] {
+			t.Errorf("%v supposed to be %v", myRes, results)
+		}
+	}
+}
+
+func TestBatchEnforce(t *testing.T) {
+	e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+	results := []bool{true, true, false}
+	testBatchEnforce(t, e, [][]interface{}{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"jack", "data3", "read"}}, results)
+}
+
diff --git a/management_api_test.go b/management_api_test.go
index bbd0e5b..378fb06 100644
--- a/management_api_test.go
+++ b/management_api_test.go
@@ -196,15 +196,6 @@ func TestModifyPolicyAPI(t *testing.T) {
 	_, _ = e.UpdatePolicy([]string{"eve", "data3", "read"}, []string{"eve", "data3", "write"})
 
 	testGetPolicy(t, e, [][]string{{"eve", "data3", "write"}})
-
-	// This test shows a rollback effect.
-	// _, _ = e.UpdatePolicies([][]string{{"eve", "data3", "write"}, {"jack", "data4", "read"}}, [][]string{{"eve", "data3", "read"}, {"jack", "data4", "write"}})
-	// testGetPolicy(t, e, [][]string{{"eve", "data3", "read"}, {"jack", "data4", "write"}})
-
-	_, _ = e.AddPolicies(rules)
-	_, _ = e.UpdatePolicies([][]string{{"eve", "data3", "write"}, {"leyo", "data4", "read"}, {"katy", "data4", "write"}},
-		[][]string{{"eve", "data3", "read"}, {"leyo", "data4", "write"}, {"katy", "data1", "write"}})
-	testGetPolicy(t, e, [][]string{{"eve", "data3", "read"}, {"jack", "data4", "read"}, {"katy", "data1", "write"}, {"leyo", "data4", "write"}, {"ham", "data4", "write"}})
 }
 
 func TestModifyGroupingPolicyAPI(t *testing.T) {