Merge "Add REST endpoint to batch update label definitions"
This commit is contained in:
@@ -3233,6 +3233,66 @@ If a label was deleted the response is "`204 No Content`".
|
|||||||
HTTP/1.1 204 No Content
|
HTTP/1.1 204 No Content
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[batch-update-labels]]
|
||||||
|
=== Batch Update Labels
|
||||||
|
--
|
||||||
|
'POST /projects/link:#project-name[\{project-name\}]/labels/'
|
||||||
|
--
|
||||||
|
|
||||||
|
Creates/updates/deletes multiple label definitions in this project at once.
|
||||||
|
|
||||||
|
The calling user must have write access to the `refs/meta/config` branch of the
|
||||||
|
project.
|
||||||
|
|
||||||
|
The updates must be specified in the request body as
|
||||||
|
link:#batch-label-input[BatchLabelInput] entity.
|
||||||
|
|
||||||
|
The updates are processed in the following order:
|
||||||
|
|
||||||
|
1. label deletions
|
||||||
|
2. label creations
|
||||||
|
3. label updates
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
POST /projects/My-Project/labels/ HTTP/1.0
|
||||||
|
Content-Type: application/json; charset=UTF-8
|
||||||
|
|
||||||
|
{
|
||||||
|
"commit_message": "Update Labels",
|
||||||
|
"delete": [
|
||||||
|
"Old-Review",
|
||||||
|
"Unused-Review"
|
||||||
|
],
|
||||||
|
"create": [
|
||||||
|
{
|
||||||
|
"name": "Foo-Review",
|
||||||
|
"values": {
|
||||||
|
" 0": "No score",
|
||||||
|
"-1": "I would prefer this is not merged as is",
|
||||||
|
"-2": "This shall not be merged",
|
||||||
|
"+1": "Looks good to me, but someone else must approve",
|
||||||
|
"+2": "Looks good to me, approved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"update:" {
|
||||||
|
"Bar-Review": {
|
||||||
|
"function": "MaxWithBlock"
|
||||||
|
},
|
||||||
|
"Baz-Review": {
|
||||||
|
"copy_min_score": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
If the label updates were done successfully the response is "`200 OK`".
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
[[ids]]
|
[[ids]]
|
||||||
== IDs
|
== IDs
|
||||||
@@ -3874,9 +3934,14 @@ review label].
|
|||||||
|Field Name ||Description
|
|Field Name ||Description
|
||||||
|`commit_message`|optional|
|
|`commit_message`|optional|
|
||||||
Message that should be used to commit the change of the label in the
|
Message that should be used to commit the change of the label in the
|
||||||
`project.config` file to the `refs/meta/config` branch.
|
`project.config` file to the `refs/meta/config` branch.+
|
||||||
|
Must not be set if this `LabelDefinitionInput` entity is contained in a
|
||||||
|
link:#batch-label-input[BatchLabelInput] entity.
|
||||||
|`name` |optional|
|
|`name` |optional|
|
||||||
The new link:config-labels.html#label_name[name] of the label.
|
The new link:config-labels.html#label_name[name] of the label.+
|
||||||
|
For label creation the name is required if this `LabelDefinitionInput` entity
|
||||||
|
is contained in a link:#batch-label-input[BatchLabelInput]
|
||||||
|
entity.
|
||||||
|`function` |optional|
|
|`function` |optional|
|
||||||
The new link:config-labels.html#label_function[function] of the label (can be
|
The new link:config-labels.html#label_function[function] of the label (can be
|
||||||
`MaxWithBlock`, `AnyWithBlock`, `MaxNoBlock`, `NoBlock`, `NoOp` and `PatchSetLock`.
|
`MaxWithBlock`, `AnyWithBlock`, `MaxNoBlock`, `NoBlock`, `NoOp` and `PatchSetLock`.
|
||||||
@@ -3959,6 +4024,27 @@ the parent project or global config. +
|
|||||||
Not set if not inherited or overridden.
|
Not set if not inherited or overridden.
|
||||||
|===============================
|
|===============================
|
||||||
|
|
||||||
|
[[batch-label-input]]
|
||||||
|
=== BatchLabelInput
|
||||||
|
The `BatchLabelInput` entity contains information for batch updating label
|
||||||
|
definitions in a project.
|
||||||
|
|
||||||
|
[options="header",cols="1,^2,4"]
|
||||||
|
|=============================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`commit_message`|optional|
|
||||||
|
Message that should be used to commit the label updates in the
|
||||||
|
`project.config` file to the `refs/meta/config` branch.
|
||||||
|
|`delete` |optional|
|
||||||
|
List of labels that should be deleted.
|
||||||
|
|`create` |optional|
|
||||||
|
List of link:#label-definition-input[LabelDefinitionInput] entities that
|
||||||
|
describe labels that should be created.
|
||||||
|
|`update` |optional|
|
||||||
|
Map of label names to link:#label-definition-input[LabelDefinitionInput]
|
||||||
|
entities that describe the updates that should be done for the labels.
|
||||||
|
|=============================
|
||||||
|
|
||||||
[[project-access-input]]
|
[[project-access-input]]
|
||||||
=== ProjectAccessInput
|
=== ProjectAccessInput
|
||||||
The `ProjectAccessInput` describes changes that should be applied to a project
|
The `ProjectAccessInput` describes changes that should be applied to a project
|
||||||
|
@@ -18,6 +18,7 @@ import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
|||||||
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
|
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
|
||||||
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
|
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
|
||||||
import com.google.gerrit.extensions.api.config.AccessCheckInput;
|
import com.google.gerrit.extensions.api.config.AccessCheckInput;
|
||||||
|
import com.google.gerrit.extensions.common.BatchLabelInput;
|
||||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||||
import com.google.gerrit.extensions.common.LabelDefinitionInfo;
|
import com.google.gerrit.extensions.common.LabelDefinitionInfo;
|
||||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||||
@@ -220,6 +221,13 @@ public interface ProjectApi {
|
|||||||
|
|
||||||
LabelApi label(String labelName) throws RestApiException;
|
LabelApi label(String labelName) throws RestApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds, updates and deletes label definitions in a batch.
|
||||||
|
*
|
||||||
|
* @param input input that describes additions, updates and deletions of label definitions
|
||||||
|
*/
|
||||||
|
void labels(BatchLabelInput input) throws RestApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A default implementation which allows source compatibility when adding new methods to the
|
* A default implementation which allows source compatibility when adding new methods to the
|
||||||
* interface.
|
* interface.
|
||||||
@@ -404,5 +412,10 @@ public interface ProjectApi {
|
|||||||
public LabelApi label(String labelName) throws RestApiException {
|
public LabelApi label(String labelName) throws RestApiException {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void labels(BatchLabelInput input) throws RestApiException {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (C) 2019 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.extensions.common;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Input for the REST API that describes additions, updates and deletions of label definitions. */
|
||||||
|
public class BatchLabelInput {
|
||||||
|
public String commitMessage;
|
||||||
|
public List<String> delete;
|
||||||
|
public List<LabelDefinitionInput> create;
|
||||||
|
public Map<String, LabelDefinitionInput> update;
|
||||||
|
}
|
@@ -43,6 +43,7 @@ import com.google.gerrit.extensions.api.projects.ProjectApi;
|
|||||||
import com.google.gerrit.extensions.api.projects.ProjectInput;
|
import com.google.gerrit.extensions.api.projects.ProjectInput;
|
||||||
import com.google.gerrit.extensions.api.projects.TagApi;
|
import com.google.gerrit.extensions.api.projects.TagApi;
|
||||||
import com.google.gerrit.extensions.api.projects.TagInfo;
|
import com.google.gerrit.extensions.api.projects.TagInfo;
|
||||||
|
import com.google.gerrit.extensions.common.BatchLabelInput;
|
||||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||||
import com.google.gerrit.extensions.common.Input;
|
import com.google.gerrit.extensions.common.Input;
|
||||||
import com.google.gerrit.extensions.common.LabelDefinitionInfo;
|
import com.google.gerrit.extensions.common.LabelDefinitionInfo;
|
||||||
@@ -77,6 +78,7 @@ import com.google.gerrit.server.restapi.project.ListBranches;
|
|||||||
import com.google.gerrit.server.restapi.project.ListDashboards;
|
import com.google.gerrit.server.restapi.project.ListDashboards;
|
||||||
import com.google.gerrit.server.restapi.project.ListLabels;
|
import com.google.gerrit.server.restapi.project.ListLabels;
|
||||||
import com.google.gerrit.server.restapi.project.ListTags;
|
import com.google.gerrit.server.restapi.project.ListTags;
|
||||||
|
import com.google.gerrit.server.restapi.project.PostLabels;
|
||||||
import com.google.gerrit.server.restapi.project.ProjectsCollection;
|
import com.google.gerrit.server.restapi.project.ProjectsCollection;
|
||||||
import com.google.gerrit.server.restapi.project.PutConfig;
|
import com.google.gerrit.server.restapi.project.PutConfig;
|
||||||
import com.google.gerrit.server.restapi.project.PutDescription;
|
import com.google.gerrit.server.restapi.project.PutDescription;
|
||||||
@@ -131,6 +133,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
private final Index index;
|
private final Index index;
|
||||||
private final IndexChanges indexChanges;
|
private final IndexChanges indexChanges;
|
||||||
private final Provider<ListLabels> listLabels;
|
private final Provider<ListLabels> listLabels;
|
||||||
|
private final PostLabels postLabels;
|
||||||
private final LabelApiImpl.Factory labelApi;
|
private final LabelApiImpl.Factory labelApi;
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
@@ -168,6 +171,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
Index index,
|
Index index,
|
||||||
IndexChanges indexChanges,
|
IndexChanges indexChanges,
|
||||||
Provider<ListLabels> listLabels,
|
Provider<ListLabels> listLabels,
|
||||||
|
PostLabels postLabels,
|
||||||
LabelApiImpl.Factory labelApi,
|
LabelApiImpl.Factory labelApi,
|
||||||
@Assisted ProjectResource project) {
|
@Assisted ProjectResource project) {
|
||||||
this(
|
this(
|
||||||
@@ -205,6 +209,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
index,
|
index,
|
||||||
indexChanges,
|
indexChanges,
|
||||||
listLabels,
|
listLabels,
|
||||||
|
postLabels,
|
||||||
labelApi,
|
labelApi,
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
@@ -244,6 +249,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
Index index,
|
Index index,
|
||||||
IndexChanges indexChanges,
|
IndexChanges indexChanges,
|
||||||
Provider<ListLabels> listLabels,
|
Provider<ListLabels> listLabels,
|
||||||
|
PostLabels postLabels,
|
||||||
LabelApiImpl.Factory labelApi,
|
LabelApiImpl.Factory labelApi,
|
||||||
@Assisted String name) {
|
@Assisted String name) {
|
||||||
this(
|
this(
|
||||||
@@ -281,6 +287,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
index,
|
index,
|
||||||
indexChanges,
|
indexChanges,
|
||||||
listLabels,
|
listLabels,
|
||||||
|
postLabels,
|
||||||
labelApi,
|
labelApi,
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
@@ -320,6 +327,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
Index index,
|
Index index,
|
||||||
IndexChanges indexChanges,
|
IndexChanges indexChanges,
|
||||||
Provider<ListLabels> listLabels,
|
Provider<ListLabels> listLabels,
|
||||||
|
PostLabels postLabels,
|
||||||
LabelApiImpl.Factory labelApi,
|
LabelApiImpl.Factory labelApi,
|
||||||
String name) {
|
String name) {
|
||||||
this.permissionBackend = permissionBackend;
|
this.permissionBackend = permissionBackend;
|
||||||
@@ -357,6 +365,7 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
this.index = index;
|
this.index = index;
|
||||||
this.indexChanges = indexChanges;
|
this.indexChanges = indexChanges;
|
||||||
this.listLabels = listLabels;
|
this.listLabels = listLabels;
|
||||||
|
this.postLabels = postLabels;
|
||||||
this.labelApi = labelApi;
|
this.labelApi = labelApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,4 +721,13 @@ public class ProjectApiImpl implements ProjectApi {
|
|||||||
throw asRestApiException("Cannot parse label", e);
|
throw asRestApiException("Cannot parse label", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void labels(BatchLabelInput input) throws RestApiException {
|
||||||
|
try {
|
||||||
|
postLabels.apply(checkExists(), input);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw asRestApiException("Cannot update labels", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -91,85 +91,7 @@ public class CreateLabel
|
|||||||
try (MetaDataUpdate md = updateFactory.create(rsrc.getNameKey())) {
|
try (MetaDataUpdate md = updateFactory.create(rsrc.getNameKey())) {
|
||||||
ProjectConfig config = projectConfigFactory.read(md);
|
ProjectConfig config = projectConfigFactory.read(md);
|
||||||
|
|
||||||
if (config.getLabelSections().containsKey(id.get())) {
|
LabelType labelType = createLabel(config, id.get(), input);
|
||||||
throw new ResourceConflictException(String.format("label %s already exists", id.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String labelName : config.getLabelSections().keySet()) {
|
|
||||||
if (labelName.equalsIgnoreCase(id.get())) {
|
|
||||||
throw new ResourceConflictException(
|
|
||||||
String.format("label %s conflicts with existing label %s", id.get(), labelName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.values == null || input.values.isEmpty()) {
|
|
||||||
throw new BadRequestException("values are required");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<LabelValue> values = LabelDefinitionInputParser.parseValues(input.values);
|
|
||||||
|
|
||||||
LabelType labelType;
|
|
||||||
try {
|
|
||||||
labelType = new LabelType(id.get(), values);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new BadRequestException("invalid name: " + id.get(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.function != null && !input.function.trim().isEmpty()) {
|
|
||||||
labelType.setFunction(LabelDefinitionInputParser.parseFunction(input.function));
|
|
||||||
} else {
|
|
||||||
labelType.setFunction(LabelFunction.MAX_WITH_BLOCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.defaultValue != null) {
|
|
||||||
labelType.setDefaultValue(
|
|
||||||
LabelDefinitionInputParser.parseDefaultValue(labelType, input.defaultValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.branches != null) {
|
|
||||||
labelType.setRefPatterns(LabelDefinitionInputParser.parseBranches(input.branches));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.canOverride != null) {
|
|
||||||
labelType.setCanOverride(input.canOverride);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAnyScore != null) {
|
|
||||||
labelType.setCopyAnyScore(input.copyAnyScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyMinScore != null) {
|
|
||||||
labelType.setCopyMinScore(input.copyMinScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyMaxScore != null) {
|
|
||||||
labelType.setCopyMaxScore(input.copyMaxScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresIfNoChange != null) {
|
|
||||||
labelType.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresIfNoCodeChange != null) {
|
|
||||||
labelType.setCopyAllScoresIfNoCodeChange(input.copyAllScoresIfNoCodeChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresOnTrivialRebase != null) {
|
|
||||||
labelType.setCopyAllScoresOnTrivialRebase(input.copyAllScoresOnTrivialRebase);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresOnMergeFirstParentUpdate != null) {
|
|
||||||
labelType.setCopyAllScoresOnMergeFirstParentUpdate(
|
|
||||||
input.copyAllScoresOnMergeFirstParentUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.allowPostSubmit != null) {
|
|
||||||
labelType.setAllowPostSubmit(input.allowPostSubmit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.ignoreSelfApproval != null) {
|
|
||||||
labelType.setIgnoreSelfApproval(input.ignoreSelfApproval);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.commitMessage != null) {
|
if (input.commitMessage != null) {
|
||||||
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
||||||
@@ -177,7 +99,6 @@ public class CreateLabel
|
|||||||
md.setMessage("Update label");
|
md.setMessage("Update label");
|
||||||
}
|
}
|
||||||
|
|
||||||
config.getLabelSections().put(labelType.getName(), labelType);
|
|
||||||
config.commit(md);
|
config.commit(md);
|
||||||
|
|
||||||
projectCache.evict(rsrc.getProjectState().getProject());
|
projectCache.evict(rsrc.getProjectState().getProject());
|
||||||
@@ -185,4 +106,101 @@ public class CreateLabel
|
|||||||
return Response.created(LabelDefinitionJson.format(rsrc.getNameKey(), labelType));
|
return Response.created(LabelDefinitionJson.format(rsrc.getNameKey(), labelType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new label.
|
||||||
|
*
|
||||||
|
* @param config the project config
|
||||||
|
* @param label the name of the new label
|
||||||
|
* @param input the input that describes the new label
|
||||||
|
* @return the created label type
|
||||||
|
* @throws BadRequestException if there was invalid data in the input
|
||||||
|
* @throws ResourceConflictException if the label cannot be created due to a conflict
|
||||||
|
*/
|
||||||
|
public LabelType createLabel(ProjectConfig config, String label, LabelDefinitionInput input)
|
||||||
|
throws BadRequestException, ResourceConflictException {
|
||||||
|
if (config.getLabelSections().containsKey(label)) {
|
||||||
|
throw new ResourceConflictException(String.format("label %s already exists", label));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String labelName : config.getLabelSections().keySet()) {
|
||||||
|
if (labelName.equalsIgnoreCase(label)) {
|
||||||
|
throw new ResourceConflictException(
|
||||||
|
String.format("label %s conflicts with existing label %s", label, labelName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.values == null || input.values.isEmpty()) {
|
||||||
|
throw new BadRequestException("values are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LabelValue> values = LabelDefinitionInputParser.parseValues(input.values);
|
||||||
|
|
||||||
|
LabelType labelType;
|
||||||
|
try {
|
||||||
|
labelType = new LabelType(label, values);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new BadRequestException("invalid name: " + label, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.function != null && !input.function.trim().isEmpty()) {
|
||||||
|
labelType.setFunction(LabelDefinitionInputParser.parseFunction(input.function));
|
||||||
|
} else {
|
||||||
|
labelType.setFunction(LabelFunction.MAX_WITH_BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.defaultValue != null) {
|
||||||
|
labelType.setDefaultValue(
|
||||||
|
LabelDefinitionInputParser.parseDefaultValue(labelType, input.defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.branches != null) {
|
||||||
|
labelType.setRefPatterns(LabelDefinitionInputParser.parseBranches(input.branches));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.canOverride != null) {
|
||||||
|
labelType.setCanOverride(input.canOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAnyScore != null) {
|
||||||
|
labelType.setCopyAnyScore(input.copyAnyScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyMinScore != null) {
|
||||||
|
labelType.setCopyMinScore(input.copyMinScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyMaxScore != null) {
|
||||||
|
labelType.setCopyMaxScore(input.copyMaxScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresIfNoChange != null) {
|
||||||
|
labelType.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresIfNoCodeChange != null) {
|
||||||
|
labelType.setCopyAllScoresIfNoCodeChange(input.copyAllScoresIfNoCodeChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresOnTrivialRebase != null) {
|
||||||
|
labelType.setCopyAllScoresOnTrivialRebase(input.copyAllScoresOnTrivialRebase);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresOnMergeFirstParentUpdate != null) {
|
||||||
|
labelType.setCopyAllScoresOnMergeFirstParentUpdate(
|
||||||
|
input.copyAllScoresOnMergeFirstParentUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.allowPostSubmit != null) {
|
||||||
|
labelType.setAllowPostSubmit(input.allowPostSubmit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.ignoreSelfApproval != null) {
|
||||||
|
labelType.setIgnoreSelfApproval(input.ignoreSelfApproval);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.getLabelSections().put(labelType.getName(), labelType);
|
||||||
|
|
||||||
|
return labelType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -77,12 +77,10 @@ public class DeleteLabel implements RestModifyView<LabelResource, InputWithCommi
|
|||||||
try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) {
|
try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) {
|
||||||
ProjectConfig config = projectConfigFactory.read(md);
|
ProjectConfig config = projectConfigFactory.read(md);
|
||||||
|
|
||||||
if (!config.getLabelSections().containsKey(rsrc.getLabelType().getName())) {
|
if (!deleteLabel(config, rsrc.getLabelType().getName())) {
|
||||||
throw new ResourceNotFoundException(IdString.fromDecoded(rsrc.getLabelType().getName()));
|
throw new ResourceNotFoundException(IdString.fromDecoded(rsrc.getLabelType().getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
config.getLabelSections().remove(rsrc.getLabelType().getName());
|
|
||||||
|
|
||||||
if (input.commitMessage != null) {
|
if (input.commitMessage != null) {
|
||||||
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
||||||
} else {
|
} else {
|
||||||
@@ -96,4 +94,20 @@ public class DeleteLabel implements RestModifyView<LabelResource, InputWithCommi
|
|||||||
|
|
||||||
return Response.none();
|
return Response.none();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the given label from the given project config.
|
||||||
|
*
|
||||||
|
* @param config the project config from which the label should be deleted
|
||||||
|
* @param labelName the name of the label that should be deleted
|
||||||
|
* @return {@code true} if the label was deleted, {@code false} if the label was not found
|
||||||
|
*/
|
||||||
|
public boolean deleteLabel(ProjectConfig config, String labelName) {
|
||||||
|
if (!config.getLabelSections().containsKey(labelName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.getLabelSections().remove(labelName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,7 @@ public class Module extends RestApiModule {
|
|||||||
get(LABEL_KIND).to(GetLabel.class);
|
get(LABEL_KIND).to(GetLabel.class);
|
||||||
put(LABEL_KIND).to(SetLabel.class);
|
put(LABEL_KIND).to(SetLabel.class);
|
||||||
delete(LABEL_KIND).to(DeleteLabel.class);
|
delete(LABEL_KIND).to(DeleteLabel.class);
|
||||||
|
postOnCollection(LABEL_KIND).to(PostLabels.class);
|
||||||
|
|
||||||
get(PROJECT_KIND, "HEAD").to(GetHead.class);
|
get(PROJECT_KIND, "HEAD").to(GetHead.class);
|
||||||
put(PROJECT_KIND, "HEAD").to(SetHead.class);
|
put(PROJECT_KIND, "HEAD").to(SetHead.class);
|
||||||
|
148
java/com/google/gerrit/server/restapi/project/PostLabels.java
Normal file
148
java/com/google/gerrit/server/restapi/project/PostLabels.java
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// Copyright (C) 2019 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.restapi.project;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.gerrit.common.data.LabelType;
|
||||||
|
import com.google.gerrit.extensions.common.BatchLabelInput;
|
||||||
|
import com.google.gerrit.extensions.common.LabelDefinitionInput;
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
|
import com.google.gerrit.extensions.restapi.Response;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
|
||||||
|
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||||
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.git.meta.MetaDataUpdate;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
|
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||||
|
import com.google.gerrit.server.project.LabelResource;
|
||||||
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
|
import com.google.gerrit.server.project.ProjectConfig;
|
||||||
|
import com.google.gerrit.server.project.ProjectResource;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
|
/** REST endpoint that allows to add, update and delete label definitions in a batch. */
|
||||||
|
@Singleton
|
||||||
|
public class PostLabels
|
||||||
|
implements RestCollectionModifyView<ProjectResource, LabelResource, BatchLabelInput> {
|
||||||
|
private final Provider<CurrentUser> user;
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
|
private final MetaDataUpdate.User updateFactory;
|
||||||
|
private final ProjectConfig.Factory projectConfigFactory;
|
||||||
|
private final DeleteLabel deleteLabel;
|
||||||
|
private final CreateLabel createLabel;
|
||||||
|
private final SetLabel setLabel;
|
||||||
|
private final ProjectCache projectCache;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public PostLabels(
|
||||||
|
Provider<CurrentUser> user,
|
||||||
|
PermissionBackend permissionBackend,
|
||||||
|
MetaDataUpdate.User updateFactory,
|
||||||
|
ProjectConfig.Factory projectConfigFactory,
|
||||||
|
DeleteLabel deleteLabel,
|
||||||
|
CreateLabel createLabel,
|
||||||
|
SetLabel setLabel,
|
||||||
|
ProjectCache projectCache) {
|
||||||
|
this.user = user;
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
|
this.updateFactory = updateFactory;
|
||||||
|
this.projectConfigFactory = projectConfigFactory;
|
||||||
|
this.deleteLabel = deleteLabel;
|
||||||
|
this.createLabel = createLabel;
|
||||||
|
this.setLabel = setLabel;
|
||||||
|
this.projectCache = projectCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response<?> apply(ProjectResource rsrc, BatchLabelInput input)
|
||||||
|
throws AuthException, UnprocessableEntityException, PermissionBackendException, IOException,
|
||||||
|
ConfigInvalidException, BadRequestException, ResourceConflictException {
|
||||||
|
if (!user.get().isIdentifiedUser()) {
|
||||||
|
throw new AuthException("Authentication required");
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionBackend
|
||||||
|
.currentUser()
|
||||||
|
.project(rsrc.getNameKey())
|
||||||
|
.check(ProjectPermission.WRITE_CONFIG);
|
||||||
|
|
||||||
|
if (input == null) {
|
||||||
|
input = new BatchLabelInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (MetaDataUpdate md = updateFactory.create(rsrc.getNameKey())) {
|
||||||
|
boolean dirty = false;
|
||||||
|
|
||||||
|
ProjectConfig config = projectConfigFactory.read(md);
|
||||||
|
|
||||||
|
if (input.delete != null && !input.delete.isEmpty()) {
|
||||||
|
for (String labelName : input.delete) {
|
||||||
|
if (!deleteLabel.deleteLabel(config, labelName.trim())) {
|
||||||
|
throw new UnprocessableEntityException(String.format("label %s not found", labelName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.create != null && !input.create.isEmpty()) {
|
||||||
|
for (LabelDefinitionInput labelInput : input.create) {
|
||||||
|
if (labelInput.name == null || labelInput.name.trim().isEmpty()) {
|
||||||
|
throw new BadRequestException("label name is required for new label");
|
||||||
|
}
|
||||||
|
if (labelInput.commitMessage != null) {
|
||||||
|
throw new BadRequestException("commit message on label definition input not supported");
|
||||||
|
}
|
||||||
|
createLabel.createLabel(config, labelInput.name.trim(), labelInput);
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.update != null && !input.update.isEmpty()) {
|
||||||
|
for (Entry<String, LabelDefinitionInput> e : input.update.entrySet()) {
|
||||||
|
LabelType labelType = config.getLabelSections().get(e.getKey().trim());
|
||||||
|
if (labelType == null) {
|
||||||
|
throw new UnprocessableEntityException(String.format("label %s not found", e.getKey()));
|
||||||
|
}
|
||||||
|
if (e.getValue().commitMessage != null) {
|
||||||
|
throw new BadRequestException("commit message on label definition input not supported");
|
||||||
|
}
|
||||||
|
setLabel.updateLabel(config, labelType, e.getValue());
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.commitMessage != null) {
|
||||||
|
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
||||||
|
} else {
|
||||||
|
md.setMessage("Update labels");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
config.commit(md);
|
||||||
|
projectCache.evict(rsrc.getProjectState().getProject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok("");
|
||||||
|
}
|
||||||
|
}
|
@@ -80,117 +80,9 @@ public class SetLabel implements RestModifyView<LabelResource, LabelDefinitionIn
|
|||||||
LabelType labelType = rsrc.getLabelType();
|
LabelType labelType = rsrc.getLabelType();
|
||||||
|
|
||||||
try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) {
|
try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) {
|
||||||
boolean dirty = false;
|
|
||||||
|
|
||||||
ProjectConfig config = projectConfigFactory.read(md);
|
ProjectConfig config = projectConfigFactory.read(md);
|
||||||
config.getLabelSections().remove(labelType.getName());
|
|
||||||
|
|
||||||
if (input.name != null) {
|
|
||||||
String newName = input.name.trim();
|
|
||||||
if (newName.isEmpty()) {
|
|
||||||
throw new BadRequestException("name cannot be empty");
|
|
||||||
}
|
|
||||||
if (!newName.equals(labelType.getName())) {
|
|
||||||
if (config.getLabelSections().containsKey(newName)) {
|
|
||||||
throw new ResourceConflictException(String.format("name %s already in use", newName));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String labelName : config.getLabelSections().keySet()) {
|
|
||||||
if (labelName.equalsIgnoreCase(newName)) {
|
|
||||||
throw new ResourceConflictException(
|
|
||||||
String.format("name %s conflicts with existing label %s", newName, labelName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
labelType.setName(newName);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new BadRequestException("invalid name: " + input.name, e);
|
|
||||||
}
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.function != null) {
|
|
||||||
if (input.function.trim().isEmpty()) {
|
|
||||||
throw new BadRequestException("function cannot be empty");
|
|
||||||
}
|
|
||||||
labelType.setFunction(LabelDefinitionInputParser.parseFunction(input.function));
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.values != null) {
|
|
||||||
if (input.values.isEmpty()) {
|
|
||||||
throw new BadRequestException("values cannot be empty");
|
|
||||||
}
|
|
||||||
labelType.setValues(LabelDefinitionInputParser.parseValues(input.values));
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.defaultValue != null) {
|
|
||||||
labelType.setDefaultValue(
|
|
||||||
LabelDefinitionInputParser.parseDefaultValue(labelType, input.defaultValue));
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.branches != null) {
|
|
||||||
labelType.setRefPatterns(LabelDefinitionInputParser.parseBranches(input.branches));
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.canOverride != null) {
|
|
||||||
labelType.setCanOverride(input.canOverride);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAnyScore != null) {
|
|
||||||
labelType.setCopyAnyScore(input.copyAnyScore);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyMinScore != null) {
|
|
||||||
labelType.setCopyMinScore(input.copyMinScore);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyMaxScore != null) {
|
|
||||||
labelType.setCopyMaxScore(input.copyMaxScore);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresIfNoChange != null) {
|
|
||||||
labelType.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresIfNoCodeChange != null) {
|
|
||||||
labelType.setCopyAllScoresIfNoCodeChange(input.copyAllScoresIfNoCodeChange);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresOnTrivialRebase != null) {
|
|
||||||
labelType.setCopyAllScoresOnTrivialRebase(input.copyAllScoresOnTrivialRebase);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.copyAllScoresOnMergeFirstParentUpdate != null) {
|
|
||||||
labelType.setCopyAllScoresOnMergeFirstParentUpdate(
|
|
||||||
input.copyAllScoresOnMergeFirstParentUpdate);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.allowPostSubmit != null) {
|
|
||||||
labelType.setAllowPostSubmit(input.allowPostSubmit);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.ignoreSelfApproval != null) {
|
|
||||||
labelType.setIgnoreSelfApproval(input.ignoreSelfApproval);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirty) {
|
|
||||||
config.getLabelSections().put(labelType.getName(), labelType);
|
|
||||||
|
|
||||||
|
if (updateLabel(config, labelType, input)) {
|
||||||
if (input.commitMessage != null) {
|
if (input.commitMessage != null) {
|
||||||
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
|
||||||
} else {
|
} else {
|
||||||
@@ -203,4 +95,128 @@ public class SetLabel implements RestModifyView<LabelResource, LabelDefinitionIn
|
|||||||
}
|
}
|
||||||
return Response.ok(LabelDefinitionJson.format(rsrc.getProject().getNameKey(), labelType));
|
return Response.ok(LabelDefinitionJson.format(rsrc.getProject().getNameKey(), labelType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the given label.
|
||||||
|
*
|
||||||
|
* @param config the project config
|
||||||
|
* @param labelType the label type that should be updated
|
||||||
|
* @param input the input that describes the label update
|
||||||
|
* @return whether the label type was modified
|
||||||
|
* @throws BadRequestException if there was invalid data in the input
|
||||||
|
* @throws ResourceConflictException if the update cannot be applied due to a conflict
|
||||||
|
*/
|
||||||
|
public boolean updateLabel(ProjectConfig config, LabelType labelType, LabelDefinitionInput input)
|
||||||
|
throws BadRequestException, ResourceConflictException {
|
||||||
|
boolean dirty = false;
|
||||||
|
|
||||||
|
config.getLabelSections().remove(labelType.getName());
|
||||||
|
|
||||||
|
if (input.name != null) {
|
||||||
|
String newName = input.name.trim();
|
||||||
|
if (newName.isEmpty()) {
|
||||||
|
throw new BadRequestException("name cannot be empty");
|
||||||
|
}
|
||||||
|
if (!newName.equals(labelType.getName())) {
|
||||||
|
if (config.getLabelSections().containsKey(newName)) {
|
||||||
|
throw new ResourceConflictException(String.format("name %s already in use", newName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String labelName : config.getLabelSections().keySet()) {
|
||||||
|
if (labelName.equalsIgnoreCase(newName)) {
|
||||||
|
throw new ResourceConflictException(
|
||||||
|
String.format("name %s conflicts with existing label %s", newName, labelName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
labelType.setName(newName);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new BadRequestException("invalid name: " + input.name, e);
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.function != null) {
|
||||||
|
if (input.function.trim().isEmpty()) {
|
||||||
|
throw new BadRequestException("function cannot be empty");
|
||||||
|
}
|
||||||
|
labelType.setFunction(LabelDefinitionInputParser.parseFunction(input.function));
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.values != null) {
|
||||||
|
if (input.values.isEmpty()) {
|
||||||
|
throw new BadRequestException("values cannot be empty");
|
||||||
|
}
|
||||||
|
labelType.setValues(LabelDefinitionInputParser.parseValues(input.values));
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.defaultValue != null) {
|
||||||
|
labelType.setDefaultValue(
|
||||||
|
LabelDefinitionInputParser.parseDefaultValue(labelType, input.defaultValue));
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.branches != null) {
|
||||||
|
labelType.setRefPatterns(LabelDefinitionInputParser.parseBranches(input.branches));
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.canOverride != null) {
|
||||||
|
labelType.setCanOverride(input.canOverride);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAnyScore != null) {
|
||||||
|
labelType.setCopyAnyScore(input.copyAnyScore);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyMinScore != null) {
|
||||||
|
labelType.setCopyMinScore(input.copyMinScore);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyMaxScore != null) {
|
||||||
|
labelType.setCopyMaxScore(input.copyMaxScore);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresIfNoChange != null) {
|
||||||
|
labelType.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresIfNoCodeChange != null) {
|
||||||
|
labelType.setCopyAllScoresIfNoCodeChange(input.copyAllScoresIfNoCodeChange);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresOnTrivialRebase != null) {
|
||||||
|
labelType.setCopyAllScoresOnTrivialRebase(input.copyAllScoresOnTrivialRebase);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.copyAllScoresOnMergeFirstParentUpdate != null) {
|
||||||
|
labelType.setCopyAllScoresOnMergeFirstParentUpdate(
|
||||||
|
input.copyAllScoresOnMergeFirstParentUpdate);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.allowPostSubmit != null) {
|
||||||
|
labelType.setAllowPostSubmit(input.allowPostSubmit);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.ignoreSelfApproval != null) {
|
||||||
|
labelType.setIgnoreSelfApproval(input.ignoreSelfApproval);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.getLabelSections().put(labelType.getName(), labelType);
|
||||||
|
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -83,7 +83,8 @@ public class ProjectsRestApiBindingsIT extends AbstractDaemonTest {
|
|||||||
.expectedResponseCode(SC_NOT_FOUND)
|
.expectedResponseCode(SC_NOT_FOUND)
|
||||||
.build(),
|
.build(),
|
||||||
RestCall.get("/projects/%s/dashboards"),
|
RestCall.get("/projects/%s/dashboards"),
|
||||||
RestCall.put("/projects/%s/labels/new-label"));
|
RestCall.put("/projects/%s/labels/new-label"),
|
||||||
|
RestCall.post("/projects/%s/labels/"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Child project REST endpoints to be tested, each URL contains placeholders for the parent
|
* Child project REST endpoints to be tested, each URL contains placeholders for the parent
|
||||||
|
@@ -0,0 +1,456 @@
|
|||||||
|
// Copyright (C) 2019 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.acceptance.rest.project;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
|
||||||
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||||
|
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
|
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
|
||||||
|
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
|
||||||
|
import com.google.gerrit.common.data.LabelFunction;
|
||||||
|
import com.google.gerrit.common.data.Permission;
|
||||||
|
import com.google.gerrit.entities.RefNames;
|
||||||
|
import com.google.gerrit.extensions.common.BatchLabelInput;
|
||||||
|
import com.google.gerrit.extensions.common.LabelDefinitionInfo;
|
||||||
|
import com.google.gerrit.extensions.common.LabelDefinitionInput;
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
|
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||||
|
import com.google.gerrit.server.restapi.project.PostLabels;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/** Tests for the {@link PostLabels} REST endpoint. */
|
||||||
|
public class PostLabelsIT extends AbstractDaemonTest {
|
||||||
|
@Inject private RequestScopeOperations requestScopeOperations;
|
||||||
|
@Inject private ProjectOperations projectOperations;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void anonymous() throws Exception {
|
||||||
|
requestScopeOperations.setApiUserAnonymous();
|
||||||
|
AuthException thrown =
|
||||||
|
assertThrows(
|
||||||
|
AuthException.class,
|
||||||
|
() -> gApi.projects().name(allProjects.get()).labels(new BatchLabelInput()));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("Authentication required");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAllowed() throws Exception {
|
||||||
|
projectOperations
|
||||||
|
.project(allProjects)
|
||||||
|
.forUpdate()
|
||||||
|
.add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS))
|
||||||
|
.update();
|
||||||
|
|
||||||
|
requestScopeOperations.setApiUser(user.id());
|
||||||
|
AuthException thrown =
|
||||||
|
assertThrows(
|
||||||
|
AuthException.class,
|
||||||
|
() -> gApi.projects().name(allProjects.get()).labels(new BatchLabelInput()));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("write refs/meta/config not permitted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteNonExistingLabel() throws Exception {
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Foo");
|
||||||
|
|
||||||
|
UnprocessableEntityException thrown =
|
||||||
|
assertThrows(
|
||||||
|
UnprocessableEntityException.class,
|
||||||
|
() -> gApi.projects().name(allProjects.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Foo not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteLabels() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
configLabel("Bar", LabelFunction.NO_OP);
|
||||||
|
assertThat(gApi.projects().name(project.get()).labels().get()).isNotEmpty();
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Foo", "Bar");
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
assertThat(gApi.projects().name(project.get()).labels().get()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteLabels_labelNamesAreTrimmed() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
configLabel("Bar", LabelFunction.NO_OP);
|
||||||
|
assertThat(gApi.projects().name(project.get()).labels().get()).isNotEmpty();
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of(" Foo ", " Bar ");
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
assertThat(gApi.projects().name(project.get()).labels().get()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotDeleteTheSameLabelTwice() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Foo", "Foo");
|
||||||
|
|
||||||
|
UnprocessableEntityException thrown =
|
||||||
|
assertThrows(
|
||||||
|
UnprocessableEntityException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Foo not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotCreateLabelWithNameThatIsAlreadyInUse() throws Exception {
|
||||||
|
LabelDefinitionInput labelInput = new LabelDefinitionInput();
|
||||||
|
labelInput.name = "Code-Review";
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(labelInput);
|
||||||
|
|
||||||
|
ResourceConflictException thrown =
|
||||||
|
assertThrows(
|
||||||
|
ResourceConflictException.class,
|
||||||
|
() -> gApi.projects().name(allProjects.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Code-Review already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotCreateTwoLabelsWithTheSameName() throws Exception {
|
||||||
|
LabelDefinitionInput fooInput = new LabelDefinitionInput();
|
||||||
|
fooInput.name = "Foo";
|
||||||
|
fooInput.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(fooInput, fooInput);
|
||||||
|
|
||||||
|
ResourceConflictException thrown =
|
||||||
|
assertThrows(
|
||||||
|
ResourceConflictException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Foo already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotCreateTwoLabelsWithNamesThatAreTheSameAfterTrim() throws Exception {
|
||||||
|
LabelDefinitionInput foo1Input = new LabelDefinitionInput();
|
||||||
|
foo1Input.name = "Foo";
|
||||||
|
foo1Input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
LabelDefinitionInput foo2Input = new LabelDefinitionInput();
|
||||||
|
foo2Input.name = " Foo ";
|
||||||
|
foo2Input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(foo1Input, foo2Input);
|
||||||
|
|
||||||
|
ResourceConflictException thrown =
|
||||||
|
assertThrows(
|
||||||
|
ResourceConflictException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Foo already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotCreateTwoLabelsWithConflictingNames() throws Exception {
|
||||||
|
LabelDefinitionInput foo1Input = new LabelDefinitionInput();
|
||||||
|
foo1Input.name = "Foo";
|
||||||
|
foo1Input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
LabelDefinitionInput foo2Input = new LabelDefinitionInput();
|
||||||
|
foo2Input.name = "foo";
|
||||||
|
foo2Input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(foo1Input, foo2Input);
|
||||||
|
|
||||||
|
ResourceConflictException thrown =
|
||||||
|
assertThrows(
|
||||||
|
ResourceConflictException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label foo conflicts with existing label Foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createLabels() throws Exception {
|
||||||
|
LabelDefinitionInput fooInput = new LabelDefinitionInput();
|
||||||
|
fooInput.name = "Foo";
|
||||||
|
fooInput.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
LabelDefinitionInput barInput = new LabelDefinitionInput();
|
||||||
|
barInput.name = "Bar";
|
||||||
|
barInput.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(fooInput, barInput);
|
||||||
|
|
||||||
|
gApi.projects().name(allProjects.get()).labels(input);
|
||||||
|
assertThat(gApi.projects().name(allProjects.get()).label("Foo").get()).isNotNull();
|
||||||
|
assertThat(gApi.projects().name(allProjects.get()).label("Bar").get()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createLabels_labelNamesAreTrimmed() throws Exception {
|
||||||
|
LabelDefinitionInput fooInput = new LabelDefinitionInput();
|
||||||
|
fooInput.name = " Foo ";
|
||||||
|
fooInput.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
LabelDefinitionInput barInput = new LabelDefinitionInput();
|
||||||
|
barInput.name = " Bar ";
|
||||||
|
barInput.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(fooInput, barInput);
|
||||||
|
|
||||||
|
gApi.projects().name(allProjects.get()).labels(input);
|
||||||
|
assertThat(gApi.projects().name(allProjects.get()).label("Foo").get()).isNotNull();
|
||||||
|
assertThat(gApi.projects().name(allProjects.get()).label("Bar").get()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotCreateLabelWithoutName() throws Exception {
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(new LabelDefinitionInput());
|
||||||
|
|
||||||
|
BadRequestException thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class, () -> gApi.projects().name(allProjects.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label name is required for new label");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotSetCommitMessageOnLabelDefinitionInputForCreate() throws Exception {
|
||||||
|
LabelDefinitionInput labelInput = new LabelDefinitionInput();
|
||||||
|
labelInput.name = "Foo";
|
||||||
|
labelInput.commitMessage = "Create Label Foo";
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(labelInput);
|
||||||
|
|
||||||
|
BadRequestException thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class, () -> gApi.projects().name(allProjects.get()).labels(input));
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains("commit message on label definition input not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateNonExistingLabel() throws Exception {
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.update = ImmutableMap.of("Foo", new LabelDefinitionInput());
|
||||||
|
|
||||||
|
UnprocessableEntityException thrown =
|
||||||
|
assertThrows(
|
||||||
|
UnprocessableEntityException.class,
|
||||||
|
() -> gApi.projects().name(allProjects.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Foo not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateLabels() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
configLabel("Bar", LabelFunction.NO_OP);
|
||||||
|
|
||||||
|
LabelDefinitionInput fooUpdate = new LabelDefinitionInput();
|
||||||
|
fooUpdate.function = LabelFunction.MAX_WITH_BLOCK.getFunctionName();
|
||||||
|
LabelDefinitionInput barUpdate = new LabelDefinitionInput();
|
||||||
|
barUpdate.name = "Baz";
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.update = ImmutableMap.of("Foo", fooUpdate, "Bar", barUpdate);
|
||||||
|
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
|
||||||
|
assertThat(gApi.projects().name(project.get()).label("Foo").get().function)
|
||||||
|
.isEqualTo(fooUpdate.function);
|
||||||
|
assertThat(gApi.projects().name(project.get()).label("Baz").get()).isNotNull();
|
||||||
|
assertThrows(
|
||||||
|
ResourceNotFoundException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).label("Bar").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateLabels_labelNamesAreTrimmed() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
configLabel("Bar", LabelFunction.NO_OP);
|
||||||
|
|
||||||
|
LabelDefinitionInput fooUpdate = new LabelDefinitionInput();
|
||||||
|
fooUpdate.function = LabelFunction.MAX_WITH_BLOCK.getFunctionName();
|
||||||
|
LabelDefinitionInput barUpdate = new LabelDefinitionInput();
|
||||||
|
barUpdate.name = "Baz";
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.update = ImmutableMap.of(" Foo ", fooUpdate, " Bar ", barUpdate);
|
||||||
|
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
|
||||||
|
assertThat(gApi.projects().name(project.get()).label("Foo").get().function)
|
||||||
|
.isEqualTo(fooUpdate.function);
|
||||||
|
assertThat(gApi.projects().name(project.get()).label("Baz").get()).isNotNull();
|
||||||
|
assertThrows(
|
||||||
|
ResourceNotFoundException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).label("Bar").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotSetCommitMessageOnLabelDefinitionInputForUpdate() throws Exception {
|
||||||
|
LabelDefinitionInput labelInput = new LabelDefinitionInput();
|
||||||
|
labelInput.commitMessage = "Update label";
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.update = ImmutableMap.of("Code-Review", labelInput);
|
||||||
|
|
||||||
|
BadRequestException thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class, () -> gApi.projects().name(allProjects.get()).labels(input));
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains("commit message on label definition input not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteAndRecreateLabel() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
|
||||||
|
LabelDefinitionInput fooInput = new LabelDefinitionInput();
|
||||||
|
fooInput.name = "Foo";
|
||||||
|
fooInput.function = LabelFunction.MAX_NO_BLOCK.getFunctionName();
|
||||||
|
fooInput.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Foo");
|
||||||
|
input.create = ImmutableList.of(fooInput);
|
||||||
|
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
|
||||||
|
LabelDefinitionInfo fooLabel = gApi.projects().name(project.get()).label("Foo").get();
|
||||||
|
assertThat(fooLabel.function).isEqualTo(fooInput.function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteRecreateAndUpdateLabel() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
|
||||||
|
LabelDefinitionInput fooCreateInput = new LabelDefinitionInput();
|
||||||
|
fooCreateInput.name = "Foo";
|
||||||
|
fooCreateInput.function = LabelFunction.MAX_NO_BLOCK.getFunctionName();
|
||||||
|
fooCreateInput.values =
|
||||||
|
ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
LabelDefinitionInput fooUpdateInput = new LabelDefinitionInput();
|
||||||
|
fooUpdateInput.function = LabelFunction.ANY_WITH_BLOCK.getFunctionName();
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Foo");
|
||||||
|
input.create = ImmutableList.of(fooCreateInput);
|
||||||
|
input.update = ImmutableMap.of("Foo", fooUpdateInput);
|
||||||
|
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
|
||||||
|
LabelDefinitionInfo fooLabel = gApi.projects().name(project.get()).label("Foo").get();
|
||||||
|
assertThat(fooLabel.function).isEqualTo(fooUpdateInput.function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cannotDeleteAndUpdateLabel() throws Exception {
|
||||||
|
configLabel("Foo", LabelFunction.NO_OP);
|
||||||
|
|
||||||
|
LabelDefinitionInput fooInput = new LabelDefinitionInput();
|
||||||
|
fooInput.function = LabelFunction.MAX_NO_BLOCK.getFunctionName();
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Foo");
|
||||||
|
input.update = ImmutableMap.of("Foo", fooInput);
|
||||||
|
|
||||||
|
UnprocessableEntityException thrown =
|
||||||
|
assertThrows(
|
||||||
|
UnprocessableEntityException.class,
|
||||||
|
() -> gApi.projects().name(project.get()).labels(input));
|
||||||
|
assertThat(thrown).hasMessageThat().contains("label Foo not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createAndUpdateLabel() throws Exception {
|
||||||
|
LabelDefinitionInput fooCreateInput = new LabelDefinitionInput();
|
||||||
|
fooCreateInput.name = "Foo";
|
||||||
|
fooCreateInput.function = LabelFunction.MAX_NO_BLOCK.getFunctionName();
|
||||||
|
fooCreateInput.values =
|
||||||
|
ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
|
||||||
|
|
||||||
|
LabelDefinitionInput fooUpdateInput = new LabelDefinitionInput();
|
||||||
|
fooUpdateInput.function = LabelFunction.ANY_WITH_BLOCK.getFunctionName();
|
||||||
|
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.create = ImmutableList.of(fooCreateInput);
|
||||||
|
input.update = ImmutableMap.of("Foo", fooUpdateInput);
|
||||||
|
|
||||||
|
gApi.projects().name(project.get()).labels(input);
|
||||||
|
|
||||||
|
LabelDefinitionInfo fooLabel = gApi.projects().name(project.get()).label("Foo").get();
|
||||||
|
assertThat(fooLabel.function).isEqualTo(fooUpdateInput.function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noOpUpdate() throws Exception {
|
||||||
|
RevCommit refsMetaConfigHead =
|
||||||
|
projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG);
|
||||||
|
|
||||||
|
gApi.projects().name(allProjects.get()).labels(new BatchLabelInput());
|
||||||
|
|
||||||
|
assertThat(projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG))
|
||||||
|
.isEqualTo(refsMetaConfigHead);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void defaultCommitMessage() throws Exception {
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.delete = ImmutableList.of("Code-Review");
|
||||||
|
gApi.projects().name(allProjects.get()).labels(input);
|
||||||
|
assertThat(
|
||||||
|
projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG).getShortMessage())
|
||||||
|
.isEqualTo("Update labels");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withCommitMessage() throws Exception {
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.commitMessage = "Batch Update Labels";
|
||||||
|
input.delete = ImmutableList.of("Code-Review");
|
||||||
|
gApi.projects().name(allProjects.get()).labels(input);
|
||||||
|
assertThat(
|
||||||
|
projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG).getShortMessage())
|
||||||
|
.isEqualTo(input.commitMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void commitMessageIsTrimmed() throws Exception {
|
||||||
|
BatchLabelInput input = new BatchLabelInput();
|
||||||
|
input.commitMessage = " Batch Update Labels ";
|
||||||
|
input.delete = ImmutableList.of("Code-Review");
|
||||||
|
gApi.projects().name(allProjects.get()).labels(input);
|
||||||
|
assertThat(
|
||||||
|
projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG).getShortMessage())
|
||||||
|
.isEqualTo("Batch Update Labels");
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user