Add REST APIs to test submit_rule and submit_filter

These are available over SSH, but not on HTTP. Make them available
on HTTP with REST API views on the revision resource. Use the REST
implementations to back the SSH commands, so the implementation is
not duplicated.

To match the REST API the SSH test-submit rule command no longer
accepts a format parameter. Output is in pretty formatted JSON.

Change-Id: I6a57b4561067eaa32d407a426c95ea61a96f1948
This commit is contained in:
Shawn Pearce 2013-03-04 07:54:09 -08:00
parent 1e1af68b34
commit b1f730b894
14 changed files with 635 additions and 312 deletions

View File

@ -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 <port> <host> 'gerrit test-submit-rule'
'ssh' -p <port> <host> '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 <test@email.com>
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 <test@email.com>
NOT_READY
$ ssh -p 29418 review.example.com gerrit test-submit rule I78f2c6673db24e4e92ed32f604c960dc952437d9 --no-filters
[
{
"status": "NOT_READY",
"need": {
"Code-Review": {}
"Verified": {}
}
}
]
====
SCRIPTING

View File

@ -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
~~~~~~~~~~

View File

@ -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.
*

View File

@ -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);

View File

@ -31,7 +31,7 @@ import com.google.inject.Provider;
import java.util.Collections;
import java.util.List;
class Revisions implements ChildCollection<ChangeResource, RevisionResource> {
public class Revisions implements ChildCollection<ChangeResource, RevisionResource> {
private final DynamicMap<RestView<RevisionResource>> views;
private final Provider<ReviewDb> dbProvider;

View File

@ -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<RevisionResource, Input> {
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<Term> results;
try {
results = eval(evaluator);
} catch (RuleEvalException e) {
String msg = Joiner.on(": ").skipNulls().join(Iterables.transform(
Throwables.getCausalChain(e),
new Function<Throwable, String>() {
@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<SubmitRecord> records = rsrc.getControl().resultsToSubmitRecord(
evaluator.getSubmitRule(),
results);
List<Record> 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<Term> eval(SubmitRuleEvaluator evaluator)
throws RuleEvalException {
return evaluator.evaluate().toJava();
}
static class Record {
SubmitRecord.Status status;
String errorMessage;
Map<String, AccountInfo> ok;
Map<String, AccountInfo> reject;
Map<String, None> need;
Map<String, AccountInfo> may;
Map<String, None> 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 {
}
}

View File

@ -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<RevisionResource, Input> {
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<RevisionResource> {
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);
}
}
}

View File

@ -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<RevisionResource, Input> 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);
}
}
}

View File

@ -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<Change> 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;
}
}

View File

@ -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);
}
}

View File

@ -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<SubmitRecord> 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<SubmitLabelAttribute> submitLabels = new LinkedList<SubmitLabelAttribute>();
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<SubmitRecordAttribute>() {}.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");
}
}
}
}

View File

@ -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<TestSubmitRule> view;
@Override
protected RestModifyView<RevisionResource, Input> createView() {
return view.get();
}
}

View File

@ -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<String> 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');
}
}

View File

@ -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<TestSubmitType> view;
@Override
protected RestModifyView<RevisionResource, Input> createView() {
return view.get();
}
}