diff options
author | Shawn Pearce <sop@google.com> | 2011-07-11 11:45:06 -0700 |
---|---|---|
committer | Android Code Review <code-review@android.com> | 2011-07-11 11:45:06 -0700 |
commit | 57aa8edde11f7643acb3b1c23151bcf6df857f1e (patch) | |
tree | 2ab05a99146667efadbc561ae3b586bc6d817cf9 | |
parent | ac54919987975f8e426d503fb8c65f7878d3d430 (diff) | |
parent | 82c088e282e5e433cb4eb7dc9af0dfbac82e2162 (diff) | |
download | gerrit-57aa8edde11f7643acb3b1c23151bcf6df857f1e.tar.gz |
Merge "Add inheritance of prolog rules"
4 files changed, 235 insertions, 2 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java index 26a01356..2c554622 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java @@ -32,8 +32,11 @@ import com.google.inject.Provider; import com.googlecode.prolog_cafe.compiler.CompileException; import com.googlecode.prolog_cafe.lang.IntegerTerm; +import com.googlecode.prolog_cafe.lang.ListTerm; +import com.googlecode.prolog_cafe.lang.Prolog; import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; +import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term; import com.googlecode.prolog_cafe.lang.VariableTerm; @@ -42,7 +45,9 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** Access control management for a user accessing a single change. */ @@ -238,9 +243,10 @@ public class ChangeControl { return ruleError("Patch set " + patchSetId + " is not current"); } + ProjectState projectState = getProjectControl().getProjectState(); PrologEnvironment env; try { - env = getProjectControl().getProjectState().newPrologEnvironment(); + env = projectState.newPrologEnvironment(); } catch (CompileException err) { return logRuleError("Cannot consult rules.pl for " + getProject().getName(), err); @@ -275,6 +281,49 @@ public class ChangeControl { + change.getId() + " of " + getProject().getName(), err); } + ProjectState parentState = projectState.getParentState(); + PrologEnvironment childEnv = env; + Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>(); + projectsSeen.add(getProject().getNameKey()); + + while (parentState != null) { + if (!projectsSeen.add(parentState.getProject().getNameKey())) { + //parent has been seen before, stop walk up inheritance tree + break; + } + PrologEnvironment parentEnv; + try { + parentEnv = parentState.newPrologEnvironment(); + } catch (CompileException err) { + return logRuleError("Cannot consult rules.pl for " + + parentState.getProject().getName(), err); + } + parentEnv.copyStoredValues(childEnv); + Term filterRule = + parentEnv.once("gerrit", "locate_submit_filter", new VariableTerm()); + if (filterRule != null) { + try { + Term resultsTerm = toListTerm(results); + results.clear(); + Term[] template = parentEnv.once( + "gerrit", "filter_submit_results", + filterRule, + resultsTerm, + new VariableTerm()); + results.addAll(((ListTerm) template[2]).toJava()); + } catch (PrologException err) { + return logRuleError("Exception calling " + filterRule + " on change " + + change.getId() + " of " + parentState.getProject().getName(), err); + } catch (RuntimeException err) { + return logRuleError("Exception calling " + filterRule + " on change " + + change.getId() + " of " + parentState.getProject().getName(), err); + } + } + + parentState = parentState.getParentState(); + childEnv = parentEnv; + } + if (results.isEmpty()) { // This should never occur. A well written submit rule will always produce // at least one result informing the caller of the labels that are @@ -398,4 +447,12 @@ public class ChangeControl { && who.name().equals("user") && who.arg(0).isInteger(); } + + private static Term toListTerm(List<Term> terms) { + Term list = Prolog.Nil; + for (int i = terms.size() - 1; i >= 0; i--) { + list = new ListTerm(terms.get(i), list); + } + return list; + } }
\ No newline at end of file diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index c587829c..9f6f6e71 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java @@ -52,6 +52,7 @@ public class ProjectState { } private final boolean isAllProjects; + private final AllProjectsName allProjectsName; private final ProjectCache projectCache; private final ProjectControl.AssistedFactory projectControlFactory; private final PrologEnvironment.Factory envFactory; @@ -84,6 +85,7 @@ public class ProjectState { @Assisted final ProjectConfig config) { this.projectCache = projectCache; this.isAllProjects = config.getProject().getNameKey().equals(allProjectsName); + this.allProjectsName = allProjectsName; this.projectControlFactory = projectControlFactory; this.envFactory = envFactory; this.gitMgr = gitMgr; @@ -257,4 +259,20 @@ public class ProjectState { public ProjectControl controlFor(final CurrentUser user) { return projectControlFactory.create(user, this); } -} + + /** + * @return ProjectState of project's parent. If the project does not have a + * parent, return state of the top level project, All-Projects. If + * this project is All-Projects, return null. + */ + public ProjectState getParentState() { + if (isAllProjects) { + return null; + } + Project.NameKey parentName = getProject().getParent(); + if (parentName == null) { + parentName = allProjectsName; + } + return projectCache.get(parentName); + } +}
\ No newline at end of file diff --git a/gerrit-server/src/main/prolog/gerrit_common.pl b/gerrit-server/src/main/prolog/gerrit_common.pl index 7ff05c14..eb83ffdd 100644 --- a/gerrit-server/src/main/prolog/gerrit_common.pl +++ b/gerrit-server/src/main/prolog/gerrit_common.pl @@ -266,6 +266,98 @@ check_label_range_permission(Label, ExpValue, ok(Who)) :- % . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% filter_submit_results/3: +%% +%% Executes the submit_filter against the given list of results, +%% returns a list of filtered results. +%% +:- public filter_submit_results/3. +%% +filter_submit_results(Filter, In, Out) :- + filter_submit_results(Filter, In, [], Tmp), + reverse(Tmp, Out). +filter_submit_results(Filter, [I | In], Tmp, Out) :- + arg(1, I, R), + call_submit_filter(Filter, R, S), + !, + S =.. [submit | Ls], + ( is_all_ok(Ls) -> T = ok(S) ; T = not_ready(S) ), + filter_submit_results(Filter, In, [T | Tmp], Out). +filter_submit_results(Filter, [_ | In], Tmp, Out) :- + filter_submit_results(Filter, In, Tmp, Out), + ! + . +filter_submit_results(Filter, [], Out, Out). + +call_submit_filter(P:X, R, S) :- !, F =.. [X, R, S], P:F. +call_submit_filter(X, R, S) :- F =.. [X, R, S], F. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% locate_submit_filter/1: +%% +%% Finds a submit_filter if available. +%% +:- public locate_submit_filter/1. +%% +locate_submit_filter(FilterName) :- + '$compiled_predicate'(user, submit_filter, 2), + !, + FilterName = user:submit_filter + . +locate_submit_filter(FilterName) :- + clause(user:submit_filter(_,_), _), + FilterName = user:submit_filter + . + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% find_label/3: +%% +%% Finds labels successively and fails when there are no more results. +%% +:- public find_label/3. +%% +find_label([], _, _) :- !, fail. +find_label(List, Name, Label) :- + List = [_ | _], + !, + find_label2(List, Name, Label). +find_label(S, Name, Label) :- + S =.. [submit | Ls], + find_label2(Ls, Name, Label). + +find_label2([L | _ ], Name, L) :- L = label(Name, _). +find_label2([_ | Ls], Name, L) :- find_label2(Ls, Name, L). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% remove_label/3: +%% +%% Removes all occurances of label(Name, Status). +%% +:- public remove_label/3. +%% +remove_label([], _, []) :- !. +remove_label(List, Label, Out) :- + List = [_ | _], + !, + subtract1(List, Label, Out). +remove_label(S, Label, Out) :- + S =.. [submit | Ls], + subtract1(Ls, Label, Tmp), + Out =.. [submit | Tmp]. + +subtract1([], _, []) :- !. +subtract1([E | L], E, R) :- !, subtract1(L, E, R). +subtract1([H | L], E, [H | R]) :- subtract1(L, E, R). + + %% commit_author/1: %% :- public commit_author/1. diff --git a/gerrit-server/src/test/resources/com/google/gerrit/rules/gerrit_common_test.pl b/gerrit-server/src/test/resources/com/google/gerrit/rules/gerrit_common_test.pl index 6aae5094..db899a7a 100644 --- a/gerrit-server/src/test/resources/com/google/gerrit/rules/gerrit_common_test.pl +++ b/gerrit-server/src/test/resources/com/google/gerrit/rules/gerrit_common_test.pl @@ -87,6 +87,57 @@ test(can_submit_not_ready) :- C = label('Code-Review', ok(test_user(alice))), V = label('Verified', need(1)). +test(can_submit_only_verified_not_ready) :- + can_submit(submit_only_verified, S), + S = not_ready(submit(V)), + V = label('Verified', need(1)). + + +%% filter_submit_results +%% +test(filter_submit_remove_verified) :- + can_submit(gerrit:default_submit, R), + filter_submit_results(filter_out_v, [R], S), + S = [ok(submit(C))], + C = label('Code-Review', ok(test_user(alice))). + +test(filter_submit_add_code_review) :- + set_commit_labels([ + commit_label( label('Code-Review', 2), test_user(alice) ), + commit_label( label('Verified', 1), test_user(builder) ) + ]), + can_submit(submit_only_verified, R), + filter_submit_results(filter_in_cr, [R], S), + S = [ok(submit(C, V))], + C = label('Code-Review', ok(test_user(alice))), + V = label('Verified', ok(test_user(builder))). + + +%% find_label +%% +test(find_default_code_review) :- + can_submit(gerrit:default_submit, R), + arg(1, R, S), + find_label(S, 'Code-Review', L), + L = label('Code-Review', ok(test_user(alice))). + +test(find_default_verified) :- + can_submit(gerrit:default_submit, R), + arg(1, R, S), + find_label(S, 'Verified', L), + L = label('Verified', need(1)). + + +%% remove_label +%% +test(remove_default_code_review) :- + can_submit(gerrit:default_submit, R), + arg(1, R, S), + C = label('Code-Review', ok(test_user(alice))), + remove_label(S, C, Out), + Out = submit(V), + V = label('Verified', need(1)). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @@ -107,6 +158,21 @@ all_commit_labels(Ls) :- commit_label( label('You-Fail', -1), test_user(alice) ) ]. +submit_only_verified(P) :- + max_with_block('Verified', -1, 1, Status), + P = submit(label('Verified', Status)). + +filter_out_v(R, S) :- + find_label(R, 'Verified', Verified), !, + remove_label(R, Verified, S). +filter_out_v(R, S). + +filter_in_cr(R, S) :- + R =.. [submit | Labels], + max_with_block('Code-Review', -2, 2, Status), + CR = label('Code-Review', Status), + S =.. [submit , CR | Labels]. + :- package user. test_grant('Code-Review', test_user(alice), range(-2, 2)). test_grant('Verified', test_user(builder), range(-1, 1)). |