diff --git a/Documentation/cmd-test-submit-rule.txt b/Documentation/cmd-test-submit-rule.txt index 5b70bd1e51..ae68b802f8 100644 --- a/Documentation/cmd-test-submit-rule.txt +++ b/Documentation/cmd-test-submit-rule.txt @@ -1,17 +1,16 @@ -gerrit test-submit-rule +gerrit test-submit rule ======================= NAME ---- -gerrit test-submit-rule - Test prolog submit rules with a chosen changeset. +gerrit test-submit rule - Test prolog submit rules with a chosen changeset. SYNOPSIS -------- [verse] -'ssh' -p 'gerrit test-submit-rule' +'ssh' -p 'gerrit test-submit rule' [-s] [--no-filters] - [--format {TEXT | JSON}] CHANGE DESCRIPTION @@ -26,15 +25,6 @@ OPTIONS --no-filters:: Don't run the submit_filter/2 from the parent projects of the specified change. ---format:: - What output format to display the results in. -+ --- -`text`:: Simple text based format. -`json`:: A JSON object described in link:json.html#submitRecord[submit record]. -`json_compact`:: Minimized JSON output. --- - ACCESS ------ Can be used by anyone that has permission to read the specified changeset. @@ -42,46 +32,31 @@ Can be used by anyone that has permission to read the specified changeset. EXAMPLES -------- - -Test submit_rule from stdin. -==== - $ cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule -s I78f2c6673db24e4e92ed32f604c960dc952437d9 - Non-Author-Code-Review: NOT_READY - Verified: NOT_READY - Code-Review: NOT_READY by Anonymous Coward - - NOT_READY -==== - Test submit_rule from stdin and return the results as JSON. ==== - cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule --format=JSON -s I78f2c6673db24e4e92ed32f604c960dc952437d9 - { - "approvals": [ - { - "type": "Verified", - "value": "NEED" - }, - { - "type": "Code-Review", - "value": "OK", - "by": { - "email": "test@email.com", - "username": "test" - } - } - ], - "value": "NOT_READY" - } + cat rules.pl | ssh -p 29418 review.example.com gerrit test-submit rule -s I78f2c6673db24e4e92ed32f604c960dc952437d9 + [ + { + "status": "NOT_READY", + "reject": { + "Any-Label-Name": {} + } + } + ] ==== Test the active submit_rule from the refs/meta/config branch, ignoring filters in the project parents. ==== - $ ssh -p 29418 review.example.com gerrit test-submit-rule I78f2c6673db24e4e92ed32f604c960dc952437d9 --no-filters - Verified: NOT_READY - Code-Review: NOT_READY by Anonymous Coward - - NOT_READY + $ ssh -p 29418 review.example.com gerrit test-submit rule I78f2c6673db24e4e92ed32f604c960dc952437d9 --no-filters + [ + { + "status": "NOT_READY", + "need": { + "Code-Review": {} + "Verified": {} + } + } + ] ==== SCRIPTING diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt index 9762465647..46a51eccb0 100644 --- a/Documentation/rest-api-changes.txt +++ b/Documentation/rest-api-changes.txt @@ -1185,6 +1185,93 @@ message is contained in the response body. "revision 674ac754f91e64a0efb8087e59a176484bd534d1 is not current revision" ---- +[[submit-type]] +GET /changes/\{change-id\}/revisions/\{revision-id\}/submit_type (Get Submit Type) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Gets the method the server will use to submit (merge) the change. + +.Request +---- + GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/current/submit_type HTTP/1.0 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + "MERGE_IF_NECESSARY" +---- + +[[test-submit-type]] +POST /changes/\{change-id\}/revisions/\{revision-id\}/test.submit_type (Test Submit Type) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Tests the submit_type Prolog rule in the project, or the one given. + +Request body may be either the Prolog code as `text/plain` or a +link:#rule-input[RuleInput] object. The query parameter `filters` +may be set to `SKIP` to bypass parent project filters while testing +a project-specific rule. + +.Request +---- + POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/current/test.submit_type HTTP/1.0 + Content-Type: text/plain;charset-UTF-8 + + submit_type(cherry_pick). +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + "cherry_pick" +---- + +[[test-submit-rule]] +POST /changes/\{change-id\}/revisions/\{revision-id\}/test.submit_rule (Test Submit Rule) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Tests the submit_rule Prolog rule in the project, or the one given. + +Request body may be either the Prolog code as `text/plain` or a +link:#rule-input[RuleInput] object. The query parameter `filters` +may be set to `SKIP` to bypass parent project filters while testing +a project-specific rule. + +.Request +---- + POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/current/test.submit_type?filters=SKIP HTTP/1.0 + Content-Type: text/plain;charset-UTF-8 + + submit_rule(submit(R)) :- + R = label('Any-Label-Name', reject(_)). +---- + +The response is a list of link:#submit-record[SubmitRecord] entries +describing the permutations that satisfy the tested submit rule. + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + [ + { + "status": "NOT_READY", + "reject": { + "Any-Label-Name": {} + } + } + ] +---- + [[list-drafts]] GET /changes/\{change-id\}/revisions/\{revision-id\}/drafts/ (List Drafts) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1888,6 +1975,24 @@ The files of the patch set as a map that maps the file names to link:#file-info[FileInfo] entities. |=========================== +[[rule-input]] +RuleInput +~~~~~~~~~ +The `RuleInput` entity contains information to test a Prolog rule. + +[options="header",width="50%",cols="1,^1,5"] +|=========================== +|Field Name ||Description +|`rule`|| +Prolog code to execute instead of the code in `refs/meta/config`. +|`filters`|`RUN` if not set| +When `RUN` filter rules in the parent projects are called to +post-process the results of the project specific rule. This +behavior matches how the rule will execute if installed. + +If `SKIP` the parent filters are not called, allowing the test +to return results from the input rule. +|=========================== + [[submit-info]] SubmitInfo ~~~~~~~~~~ @@ -1920,6 +2025,39 @@ added to the merge queue and the caller can't know whether the change could be merged successfully. |=========================== +[[submit-record]] +SubmitRecord +~~~~~~~~~~~~ +The `SubmitRecord` entity describes results from a submit_rule. + +[options="header",width="50%",cols="1,^1,5"] +|=========================== +|Field Name ||Description +|`status`|| +`OK`, the change can be submitted. + +`NOT_READY`, additional labels are required before submit. + +`CLOSED`, closed changes cannot be submitted. + +`RULE_ERROR`, rule code failed with an error. +|`ok`|optional| +Map of labels that are approved; an link:#account-info[AccountInfo] +identifies the voter chosen by the rule. +|`reject`|optional| +Map of labels that are preventing submit; AccountInfo identifies voter. +|`need`|optional| +Map of labels that need to be given to submit. The value is +currently an empty object. +|`may`|optional| +Map of labels that can be used, but do not affect submit. +AccountInfo identifies voter, if the label has been applied. +|`impossible`|optional| +Map of labels that should have been in `need` but cannot be +used by any user because of access restrictions. The value +is currently an empty object. +|`error_message`|optional| +When status is RULE_ERROR this message provides some text describing +the failure of the rule predicate. +|=========================== + [[topic-input]] TopicInput ~~~~~~~~~~ diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java index f625c0b8d7..dad53b9f4c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java @@ -116,6 +116,10 @@ public class RulesCache { defaultMachine = save(newEmptyMachine(systemLoader)); } + public boolean isProjectRulesEnabled() { + return enableProjectRules; + } + /** * Locate a cached Prolog machine state, or create one if not available. * diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java index c89e518762..1bf254214a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java @@ -59,6 +59,9 @@ public class Module extends RestApiModule { get(REVISION_KIND, "review").to(GetReview.class); post(REVISION_KIND, "review").to(PostReview.class); post(REVISION_KIND, "submit").to(Submit.class); + get(REVISION_KIND, "submit_type").to(TestSubmitType.Get.class); + post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class); + post(REVISION_KIND, "test.submit_type").to(TestSubmitType.class); child(REVISION_KIND, "drafts").to(Drafts.class); put(REVISION_KIND, "drafts").to(CreateDraft.class); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java index 2970852a19..68ef63cedf 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java @@ -31,7 +31,7 @@ import com.google.inject.Provider; import java.util.Collections; import java.util.List; -class Revisions implements ChildCollection { +public class Revisions implements ChildCollection { private final DynamicMap> views; private final Provider dbProvider; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java new file mode 100644 index 0000000000..fdf63be0bf --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java @@ -0,0 +1,198 @@ +// Copyright (C) 2013 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.gerrit.common.data.SubmitRecord; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.DefaultInput; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.rules.RulesCache; +import com.google.gerrit.server.change.TestSubmitRule.Input; +import com.google.gerrit.server.project.RuleEvalException; +import com.google.gerrit.server.project.SubmitRuleEvaluator; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; + +import com.googlecode.prolog_cafe.lang.Term; + +import org.kohsuke.args4j.Option; + +import java.io.ByteArrayInputStream; +import java.util.List; +import java.util.Map; + +public class TestSubmitRule implements RestModifyView { + public enum Filters { + RUN, SKIP; + } + + public static class Input { + @DefaultInput + public String rule; + public Filters filters; + } + + private final ReviewDb db; + private final RulesCache rules; + private final AccountInfo.Loader.Factory accountInfoFactory; + + @Option(name = "--filters", usage = "impact of filters in parent projects") + private Filters filters = Filters.RUN; + + @Inject + TestSubmitRule(ReviewDb db, RulesCache rules, + AccountInfo.Loader.Factory infoFactory) { + this.db = db; + this.rules = rules; + this.accountInfoFactory = infoFactory; + } + + @Override + public Object apply(RevisionResource rsrc, Input input) throws OrmException, + BadRequestException, AuthException { + if (input == null) { + input = new Input(); + } + if (input.rule != null && !rules.isProjectRulesEnabled()) { + throw new AuthException("project rules are disabled"); + } + input.filters = Objects.firstNonNull(input.filters, filters); + + SubmitRuleEvaluator evaluator = new SubmitRuleEvaluator( + db, + rsrc.getPatchSet(), + rsrc.getControl().getProjectControl(), + rsrc.getControl(), + rsrc.getChange(), + new ChangeData(rsrc.getChange()), + false, + "locate_submit_rule", "can_submit", + "locate_submit_filter", "filter_submit_results", + input.filters == Filters.SKIP, + input.rule != null + ? new ByteArrayInputStream(input.rule.getBytes(Charsets.UTF_8)) + : null); + + List results; + try { + results = eval(evaluator); + } catch (RuleEvalException e) { + String msg = Joiner.on(": ").skipNulls().join(Iterables.transform( + Throwables.getCausalChain(e), + new Function() { + @Override + public String apply(Throwable in) { + return in.getMessage(); + } + })); + throw new BadRequestException("rule failed: " + msg); + } + if (results.isEmpty()) { + throw new BadRequestException(String.format( + "rule %s has no solutions", + evaluator.getSubmitRule().toString())); + } + + List records = rsrc.getControl().resultsToSubmitRecord( + evaluator.getSubmitRule(), + results); + List out = Lists.newArrayListWithCapacity(records.size()); + AccountInfo.Loader accounts = accountInfoFactory.create(true); + for (SubmitRecord r : records) { + out.add(new Record(r, accounts)); + } + accounts.fill(); + return out; + } + + @SuppressWarnings("unchecked") + private static List eval(SubmitRuleEvaluator evaluator) + throws RuleEvalException { + return evaluator.evaluate().toJava(); + } + + static class Record { + SubmitRecord.Status status; + String errorMessage; + Map ok; + Map reject; + Map need; + Map may; + Map impossible; + + Record(SubmitRecord r, AccountInfo.Loader accounts) { + this.status = r.status; + this.errorMessage = r.errorMessage; + + if (r.labels != null) { + for (SubmitRecord.Label n : r.labels) { + AccountInfo who = n.appliedBy != null + ? accounts.get(n.appliedBy) + : new AccountInfo(null); + label(n, who); + } + } + } + + private void label(SubmitRecord.Label n, AccountInfo who) { + switch (n.status) { + case OK: + if (ok == null) { + ok = Maps.newLinkedHashMap(); + } + ok.put(n.label, who); + break; + case REJECT: + if (reject == null) { + reject = Maps.newLinkedHashMap(); + } + reject.put(n.label, who); + break; + case NEED: + if (need == null) { + need = Maps.newLinkedHashMap(); + } + need.put(n.label, new None()); + break; + case MAY: + if (may == null) { + may = Maps.newLinkedHashMap(); + } + may.put(n.label, who); + break; + case IMPOSSIBLE: + if (impossible == null) { + impossible = Maps.newLinkedHashMap(); + } + impossible.put(n.label, new None()); + break; + } + } + } + + static class None { + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java new file mode 100644 index 0000000000..f58fe75149 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java @@ -0,0 +1,116 @@ +// Copyright (C) 2013 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.common.base.Charsets; +import com.google.common.base.Objects; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.rules.RulesCache; +import com.google.gerrit.server.change.TestSubmitRule.Filters; +import com.google.gerrit.server.change.TestSubmitRule.Input; +import com.google.gerrit.server.project.RuleEvalException; +import com.google.gerrit.server.project.SubmitRuleEvaluator; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; + +import com.googlecode.prolog_cafe.lang.ListTerm; +import com.googlecode.prolog_cafe.lang.Term; + +import org.kohsuke.args4j.Option; + +import java.io.ByteArrayInputStream; + +public class TestSubmitType implements RestModifyView { + private final ReviewDb db; + private final RulesCache rules; + + @Option(name = "--filters", usage = "impact of filters in parent projects") + private Filters filters = Filters.RUN; + + @Inject + TestSubmitType(ReviewDb db, RulesCache rules) { + this.db = db; + this.rules = rules; + } + + @Override + public String apply(RevisionResource rsrc, Input input) throws OrmException, + BadRequestException, AuthException { + if (input == null) { + input = new Input(); + } + if (input.rule != null && !rules.isProjectRulesEnabled()) { + throw new AuthException("project rules are disabled"); + } + input.filters = Objects.firstNonNull(input.filters, filters); + + SubmitRuleEvaluator evaluator = new SubmitRuleEvaluator( + db, + rsrc.getPatchSet(), + rsrc.getControl().getProjectControl(), + rsrc.getControl(), + rsrc.getChange(), + new ChangeData(rsrc.getChange()), + false, + "locate_submit_type", "get_submit_type", + "locate_submit_type_filter", "filter_submit_type_results", + input.filters == Filters.SKIP, + input.rule != null + ? new ByteArrayInputStream(input.rule.getBytes(Charsets.UTF_8)) + : null); + + ListTerm results; + try { + results = evaluator.evaluate(); + } catch (RuleEvalException e) { + throw new BadRequestException(String.format( + "rule failed with exception: %s", + e.getMessage())); + } + if (results.isNil()) { + throw new BadRequestException(String.format( + "rule %s has no solution", + evaluator.getSubmitRule())); + } + Term type = results.car(); + if (!type.isSymbol()) { + throw new BadRequestException(String.format( + "rule %s produced invalid result: %s", + evaluator.getSubmitRule().toString(), + type)); + } + return type.toString(); + } + + static class Get implements RestReadView { + private final TestSubmitType test; + + @Inject + Get(TestSubmitType test) { + this.test = test; + } + + @Override + public String apply(RevisionResource resource) throws BadRequestException, + OrmException, AuthException { + return test.apply(resource, null); + } + } +} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java new file mode 100644 index 0000000000..0e13fd6bc0 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java @@ -0,0 +1,81 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.TopLevelResource; +import com.google.gerrit.server.OutputFormat; +import com.google.gerrit.server.change.ChangesCollection; +import com.google.gerrit.server.change.RevisionResource; +import com.google.gerrit.server.change.Revisions; +import com.google.gerrit.server.change.TestSubmitRule.Filters; +import com.google.gerrit.server.change.TestSubmitRule.Input; +import com.google.gerrit.sshd.SshCommand; +import com.google.inject.Inject; + +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.RawParseUtils; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +import java.nio.ByteBuffer; + +abstract class BaseTestPrologCommand extends SshCommand { + private Input input = new Input(); + + @Inject + private ChangesCollection changes; + + @Inject + private Revisions revisions; + + @Argument(index = 0, required = true, usage = "ChangeId to load in prolog environment") + protected String changeId; + + @Option(name = "-s", + usage = "Read prolog script from stdin instead of reading rules.pl from the refs/meta/config branch") + protected boolean useStdin; + + @Option(name = "--no-filters", aliases = {"-n"}, + usage = "Don't run the submit_filter/2 from the parent projects") + void setNoFilters(boolean no) { + input.filters = no ? Filters.SKIP : Filters.RUN; + } + + protected abstract RestModifyView createView(); + + protected final void run() throws UnloggedFailure { + try { + RevisionResource revision = revisions.parse( + changes.parse( + TopLevelResource.INSTANCE, + IdString.fromUrl(changeId)), + IdString.fromUrl("current")); + if (useStdin) { + ByteBuffer buf = IO.readWholeStream(in, 4096); + input.rule = RawParseUtils.decode( + buf.array(), + buf.arrayOffset(), + buf.limit()); + } + Object result = createView().apply(revision, input); + OutputFormat.JSON.newGson().toJson(result, stdout); + stdout.print('\n'); + } catch (Exception e) { + throw new UnloggedFailure("Processing of prolog script failed: " + e); + } + } +} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestSubmit.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestSubmit.java deleted file mode 100644 index b8a71159be..0000000000 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestSubmit.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2012 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.sshd.commands; - -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.OutputFormat; -import com.google.gerrit.server.account.AccountCache; -import com.google.gerrit.server.config.AnonymousCowardName; -import com.google.gerrit.server.project.ChangeControl; -import com.google.gerrit.server.project.NoSuchChangeException; -import com.google.gerrit.server.project.SubmitRuleEvaluator; -import com.google.gerrit.sshd.SshCommand; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Inject; - -import com.googlecode.prolog_cafe.lang.ListTerm; -import com.googlecode.prolog_cafe.lang.Term; - -import org.kohsuke.args4j.Argument; -import org.kohsuke.args4j.Option; - -import java.util.List; - -abstract class BaseTestSubmit extends SshCommand { - @Inject - protected ReviewDb db; - - @Inject - private ChangeControl.Factory ccFactory; - - @Inject - protected AccountCache accountCache; - - @Inject - @AnonymousCowardName - protected String anonymousCowardName; - - @Argument(index = 0, required = true, usage = "ChangeId to load in prolog environment") - protected String changeId; - - @Option(name = "-s", - usage = "Read prolog script from stdin instead of reading rules.pl from the refs/meta/config branch") - protected boolean useStdin; - - @Option(name = "--format", metaVar = "FMT", usage = "Output display format") - protected OutputFormat format = OutputFormat.TEXT; - - @Option(name = "--no-filters", aliases = {"-n"}, - usage = "Don't run the submit_filter/2 from the parent projects") - protected boolean skipSubmitFilters; - - private Change change; - - private ChangeControl changeControl; - - protected abstract SubmitRuleEvaluator createEvaluator(PatchSet ps) - throws Exception; - protected abstract void processResults(ListTerm results, Term submitRule) - throws Exception; - - protected final void run() throws UnloggedFailure { - try { - PatchSet ps = db.patchSets().get(getChange().currentPatchSetId()); - SubmitRuleEvaluator evaluator = createEvaluator(ps); - processResults(evaluator.evaluate(), evaluator.getSubmitRule()); - } catch (Exception e) { - throw new UnloggedFailure("Processing of prolog script failed: " + e); - } - } - - protected final Change getChange() throws OrmException, UnloggedFailure { - if (change == null) { - List changeList = - db.changes().byKey(new Change.Key(changeId)).toList(); - if (changeList.size() != 1) - throw new UnloggedFailure(1, "Invalid ChangeId"); - - change = changeList.get(0); - } - return change; - } - - protected final ChangeControl getChangeControl() throws OrmException, - NoSuchChangeException, UnloggedFailure { - if (changeControl == null) { - // Will throw exception if current user can not access this change, and - // thus will leak information that a change-id is valid even though the - // user are not allowed to see the change. - // See http://code.google.com/p/gerrit/issues/detail?id=1586 - changeControl = ccFactory.controlFor(getChange()); - } - return changeControl; - } -} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java index 7838b45344..30f3c95b2d 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java @@ -32,7 +32,6 @@ public class MasterCommandModule extends CommandModule { command(gerrit, RenameGroupCommand.class); command(gerrit, CreateProjectCommand.class); command(gerrit, AdminQueryShell.class); - command(gerrit, TestSubmitRule.class); command(gerrit, SetReviewersCommand.class); command(gerrit, Receive.class); command(gerrit, AdminSetParent.class); @@ -43,7 +42,7 @@ public class MasterCommandModule extends CommandModule { command(gerrit, SetProjectCommand.class); command(gerrit, "test-submit").toProvider(new DispatchCommandProvider(testSubmit)); - command(testSubmit, TestSubmitRule.class); - command(testSubmit, TestSubmitType.class); + command(testSubmit, TestSubmitRuleCommand.class); + command(testSubmit, TestSubmitTypeCommand.class); } } diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRule.java deleted file mode 100644 index b0729d948e..0000000000 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRule.java +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2012 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.sshd.commands; - -import com.google.gerrit.common.data.AccountInfo; -import com.google.gerrit.common.data.SubmitRecord; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.server.events.AccountAttribute; -import com.google.gerrit.server.events.SubmitLabelAttribute; -import com.google.gerrit.server.events.SubmitRecordAttribute; -import com.google.gerrit.server.project.ChangeControl; -import com.google.gerrit.server.project.SubmitRuleEvaluator; -import com.google.gerrit.sshd.CommandMetaData; -import com.google.gson.reflect.TypeToken; - -import com.googlecode.prolog_cafe.lang.ListTerm; -import com.googlecode.prolog_cafe.lang.Term; - -import java.util.LinkedList; -import java.util.List; - -/** Command that allows testing of prolog submit-rules in a live instance. */ -@CommandMetaData(name = "rule", descr = "Test prolog submit rules") -final class TestSubmitRule extends BaseTestSubmit { - - protected SubmitRuleEvaluator createEvaluator(PatchSet ps) throws Exception { - ChangeControl cc = getChangeControl(); - return new SubmitRuleEvaluator( - db, ps, cc.getProjectControl(), cc, getChange(), null, - false, "locate_submit_rule", "can_submit", - "locate_submit_filter", "filter_submit_results", - skipSubmitFilters, useStdin ? in : null); - } - - protected void processResults(ListTerm results, Term submitRule) throws Exception { - @SuppressWarnings("unchecked") - List res = getChangeControl().resultsToSubmitRecord(submitRule, - results.toJava()); - if (res.isEmpty()) { - // Should never occur for a well written rule - Change c = getChange(); - stderr.print("Submit rule " + submitRule + " for change " + c.getChangeId() - + " of " + c.getProject().get() + " has no solution"); - return; - } - for (SubmitRecord r : res) { - if (format.isJson()) { - SubmitRecordAttribute submitRecord = new SubmitRecordAttribute(); - submitRecord.status = r.status.name(); - - List submitLabels = new LinkedList(); - for(SubmitRecord.Label l : r.labels) { - SubmitLabelAttribute label = new SubmitLabelAttribute(); - label.label = l.label; - label.status= l.status.name(); - if(l.appliedBy != null) { - Account a = accountCache.get(l.appliedBy).getAccount(); - label.by = new AccountAttribute(); - label.by.email = a.getPreferredEmail(); - label.by.name = a.getFullName(); - label.by.username = a.getUserName(); - } - submitLabels.add(label); - } - submitRecord.labels = submitLabels; - format.newGson().toJson(submitRecord, new TypeToken() {}.getType(), stdout); - stdout.print('\n'); - } else { - for(SubmitRecord.Label l : r.labels) { - stdout.print(l.label + ": " + l.status); - if(l.appliedBy != null) { - AccountInfo a = new AccountInfo(accountCache.get(l.appliedBy).getAccount()); - stdout.print(" by " + a.getNameEmail(anonymousCowardName)); - } - stdout.print('\n'); - } - stdout.print("\n" + r.status.name() + "\n"); - } - } - } -} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java new file mode 100644 index 0000000000..6335160ac7 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java @@ -0,0 +1,35 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.server.change.RevisionResource; +import com.google.gerrit.server.change.TestSubmitRule; +import com.google.gerrit.server.change.TestSubmitRule.Input; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.inject.Inject; +import com.google.inject.Provider; + +/** Command that allows testing of prolog submit-rules in a live instance. */ +@CommandMetaData(name = "rule", descr = "Test prolog submit rules") +final class TestSubmitRuleCommand extends BaseTestPrologCommand { + @Inject + private Provider view; + + @Override + protected RestModifyView createView() { + return view.get(); + } +} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitType.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitType.java deleted file mode 100644 index fb4a811426..0000000000 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitType.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2012 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package com.google.gerrit.sshd.commands; - -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.server.project.ChangeControl; -import com.google.gerrit.server.project.SubmitRuleEvaluator; -import com.google.gerrit.sshd.CommandMetaData; - -import com.googlecode.prolog_cafe.lang.ListTerm; -import com.googlecode.prolog_cafe.lang.Term; - -import java.util.List; - -@CommandMetaData(name = "type", descr = "Test prolog submit type") -final class TestSubmitType extends BaseTestSubmit { - - @Override - protected SubmitRuleEvaluator createEvaluator(PatchSet ps) throws Exception { - ChangeControl cc = getChangeControl(); - return new SubmitRuleEvaluator( - db, ps, cc.getProjectControl(), cc, getChange(), null, - false, "locate_submit_type", "get_submit_type", - "locate_submit_type_filter", "filter_submit_type_results", - skipSubmitFilters, useStdin ? in : null); - } - - @Override - protected void processResults(ListTerm resultsTerm, Term submitRule) - throws Exception { - @SuppressWarnings("unchecked") - List results = resultsTerm.toJava(); - if (results.isEmpty()) { - // Should never occur for a well written rule - Change c = getChange(); - stderr.print("Submit rule " + submitRule + " for change " + c.getChangeId() - + " of " + c.getProject().get() + " has no solution"); - return; - } - String typeName = results.get(0); - stdout.print(typeName); - stdout.print('\n'); - } -} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java new file mode 100644 index 0000000000..326ff46d3b --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java @@ -0,0 +1,35 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package com.google.gerrit.sshd.commands; + +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.server.change.RevisionResource; +import com.google.gerrit.server.change.TestSubmitRule.Input; +import com.google.gerrit.server.change.TestSubmitType; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.inject.Inject; +import com.google.inject.Provider; + +@CommandMetaData(name = "type", descr = "Test prolog submit type") +final class TestSubmitTypeCommand extends BaseTestPrologCommand { + @Inject + private Provider view; + + @Override + protected RestModifyView createView() { + return view.get(); + } +}