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:
parent
1e1af68b34
commit
b1f730b894
@ -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
|
||||
|
@ -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
|
||||
~~~~~~~~~~
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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');
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user