From cfed131070e85a90c1e41af492235b8cf488a8eb Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 6 Nov 2017 11:25:35 +0100 Subject: [PATCH] SONAR-10040 add maximum value to ws API --- .../sonar/server/ce/ws/ActivityAction.java | 2 - .../sonar/server/component/ws/TreeAction.java | 1 - .../measure/ws/ComponentTreeAction.java | 1 - .../organization/ws/SearchMembersAction.java | 2 - .../qualityprofile/ws/ProjectsAction.java | 2 - .../org/sonar/server/ws/ws/ListAction.java | 13 +- .../org/sonar/server/ws/ws/list-example.json | 6 + .../server/ce/ws/ActivityActionTest.java | 4 +- .../server/component/ws/TreeActionTest.java | 4 +- .../measure/ws/ComponentTreeActionTest.java | 7 +- .../ws/SearchMembersActionTest.java | 2 +- .../qualityprofile/ws/ProjectsActionTest.java | 2 +- .../sonar/server/ws/ws/WebServicesWsTest.java | 1 + .../list_including_internals.json | 216 ++++++++++-------- .../org/sonar/api/server/ws/WebService.java | 30 ++- .../server/ws/internal/ValidatingRequest.java | 59 +++-- .../org/sonar/api/server/ws/RequestTest.java | 24 +- .../sonar/api/server/ws/WebServiceTest.java | 14 ++ 18 files changed, 240 insertions(+), 150 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java index e54d06826cf..3d2d55203ca 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java @@ -294,8 +294,6 @@ public class ActivityAction implements CeWsAction { checkRequest(activityWsRequest.getComponentId() == null || activityWsRequest.getQuery() == null, "%s and %s must not be set at the same time", PARAM_COMPONENT_ID, PARAM_COMPONENT_QUERY); - checkRequest(activityWsRequest.getPageSize() <= MAX_PAGE_SIZE, "The '%s' parameter must be less than %d", Param.PAGE_SIZE, MAX_PAGE_SIZE); - return activityWsRequest; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java index 0fc4dd804aa..5e742a29e82 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java @@ -296,7 +296,6 @@ public class TreeAction implements ComponentsWsAction { .setAsc(request.mandatoryParamAsBoolean(Param.ASCENDING)) .setPage(request.mandatoryParamAsInt(Param.PAGE)) .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)); - checkRequest(treeWsRequest.getPageSize() <= MAX_SIZE, "The '%s' parameter must be less than %d", Param.PAGE_SIZE, MAX_SIZE); String searchQuery = treeWsRequest.getQuery(); checkRequest(searchQuery == null || searchQuery.length() >= QUERY_MINIMUM_LENGTH, "The '%s' parameter must have at least %d characters", Param.TEXT_QUERY, QUERY_MINIMUM_LENGTH); diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java index 188fce832df..892d64fc303 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java @@ -308,7 +308,6 @@ public class ComponentTreeAction implements MeasuresWsAction { .setPage(request.mandatoryParamAsInt(Param.PAGE)) .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) .setQuery(request.param(Param.TEXT_QUERY)); - checkRequest(componentTreeWsRequest.getPageSize() <= MAX_SIZE, "The '%s' parameter must be less than %d", Param.PAGE_SIZE, MAX_SIZE); String searchQuery = componentTreeWsRequest.getQuery(); checkRequest(searchQuery == null || searchQuery.length() >= QUERY_MINIMUM_LENGTH, "The '%s' parameter must have at least %d characters", Param.TEXT_QUERY, QUERY_MINIMUM_LENGTH); diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMembersAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMembersAction.java index 1d87cfaac36..deb4db66886 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMembersAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMembersAction.java @@ -160,8 +160,6 @@ public class SearchMembersAction implements OrganizationsWsAction { private static SearchOptions buildSearchOptions(Request request) { int pageSize = request.mandatoryParamAsInt(Param.PAGE_SIZE); - checkArgument(pageSize <= SearchOptions.MAX_LIMIT, "Page size must lower than or equal to %s", SearchOptions.MAX_LIMIT); - return new SearchOptions().setPage(request.mandatoryParamAsInt(Param.PAGE), pageSize); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java index cdb9f246679..abde10c79bf 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java @@ -41,7 +41,6 @@ import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Comparator.comparing; import static org.sonar.api.utils.Paging.forPageIndex; import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; @@ -99,7 +98,6 @@ public class ProjectsAction implements QProfileWsAction { String query = request.param(Param.TEXT_QUERY); int page = request.mandatoryParamAsInt(Param.PAGE); int pageSize = request.mandatoryParamAsInt(Param.PAGE_SIZE); - checkArgument(pageSize <= MAX_PAGE_SIZE, "The '%s' parameter must be less than %s", Param.PAGE_SIZE, MAX_PAGE_SIZE); List projects = loadAllProjects(profileKey, session, selected, query).stream() .sorted(comparing(ProjectQprofileAssociationDto::getProjectName) diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/ws/ws/ListAction.java index 8f91b4d0279..126bfd341f6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ws/ws/ListAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ws/ws/ListAction.java @@ -22,7 +22,6 @@ package org.sonar.server.ws.ws; import com.google.common.collect.Ordering; import java.util.Comparator; import java.util.List; -import java.util.Set; import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; @@ -31,6 +30,7 @@ import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.util.stream.MoreCollectors; import static com.google.common.base.Preconditions.checkState; +import static java.util.Optional.ofNullable; public class ListAction implements WebServicesWsAction { private WebService.Context context; @@ -134,14 +134,9 @@ public class ListAction implements WebServicesWsAction { writer.prop("deprecatedKey", param.deprecatedKey()); writer.prop("deprecatedKeySince", param.deprecatedKeySince()); writer.prop("maxValuesAllowed", param.maxValuesAllowed()); - Set possibleValues = param.possibleValues(); - if (possibleValues != null) { - writer.name("possibleValues").beginArray().values(possibleValues).endArray(); - } - Integer maximumLength = param.maximumLength(); - if (maximumLength != null) { - writer.prop("maximumLength", maximumLength); - } + ofNullable(param.possibleValues()).ifPresent(possibleValues -> writer.name("possibleValues").beginArray().values(possibleValues).endArray()); + ofNullable(param.maximumLength()).ifPresent(maximumLength -> writer.prop("maximumLength", maximumLength)); + ofNullable(param.maximumValue()).ifPresent(maximumValue -> writer.prop("maximumValue", maximumValue)); writer.endObject(); } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/ws/ws/list-example.json b/server/sonar-server/src/main/resources/org/sonar/server/ws/ws/list-example.json index 2734b17d98c..ade270b1dbd 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/ws/ws/list-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/ws/ws/list-example.json @@ -28,6 +28,12 @@ } ], "params": [ + { + "key": "maximum_value", + "maximumValue": 12, + "required": false, + "internal": false + }, { "key": "name", "required": false, diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java index 856ef29b1d6..0f97dde5b8a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java @@ -395,8 +395,8 @@ public class ActivityActionTest { @Test public void fail_if_page_size_greater_than_1000() { - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("The 'ps' parameter must be less than 1000"); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("'ps' value (1001) must be less than 1000"); ws.newRequest() .setParam(Param.PAGE_SIZE, "1001") diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java index dc8ffbf335d..4efbd778940 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java @@ -386,8 +386,8 @@ public class TreeActionTest { @Test public void fail_when_page_size_above_500() { - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("The 'ps' parameter must be less than 500"); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("'ps' value (501) must be less than 500"); componentDb.insertComponent(newPrivateProjectDto(db.getDefaultOrganization(), "project-uuid")); db.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java index 52911d542b7..0b9e192bea0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java @@ -71,7 +71,6 @@ import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.api.web.UserRole.USER; import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; -import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.component.ComponentTesting.newProjectCopy; import static org.sonar.db.component.SnapshotTesting.newAnalysis; import static org.sonar.db.measure.MeasureTesting.newMeasureDto; @@ -765,14 +764,14 @@ public class ComponentTreeActionTest { db.components().insertSnapshot(project); insertNclocMetric(); - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("The 'ps' parameter must be less than 500"); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("'ps' value (2540) must be less than 500"); ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) .setParam(PARAM_METRIC_KEYS, "ncloc") .setParam(Param.PAGE_SIZE, "2540") - .executeProtobuf(ComponentTreeWsResponse.class); + .execute(); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java index 584dadca43b..4b9608912d7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java @@ -268,7 +268,7 @@ public class SearchMembersActionTest { request.setPageSize(501); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Page size must lower than or equal to 500"); + expectedException.expectMessage("'ps' value (501) must be less than 500"); call(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ProjectsActionTest.java index 7051702e9e4..477ef473107 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ProjectsActionTest.java @@ -200,7 +200,7 @@ public class ProjectsActionTest { @Test public void fail_if_page_size_greater_than_500() throws Exception { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("The 'ps' parameter must be less than 500"); + expectedException.expectMessage("'ps' value (501) must be less than 500"); newRequest().setParam(PARAM_KEY, xooP1.getKee()).setParam(Param.PAGE_SIZE, "501").execute(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/ws/ws/WebServicesWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/ws/ws/WebServicesWsTest.java index dd8110eb008..d9e1fc2fc4f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ws/ws/WebServicesWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ws/ws/WebServicesWsTest.java @@ -125,6 +125,7 @@ public class WebServicesWsTest { .setDefaultValue("BLOCKER"); create.createParam("name"); create.createParam("internal").setInternal(true); + create.createParam("maximum_value").setMaximumValue(12); newController.createAction("internal_action") .setDescription("Internal Action Description") diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ws/ws/WebServicesWsTest/list_including_internals.json b/server/sonar-server/src/test/resources/org/sonar/server/ws/ws/WebServicesWsTest/list_including_internals.json index e4e70d2d40f..dddcd8edbe6 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/ws/ws/WebServicesWsTest/list_including_internals.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/ws/ws/WebServicesWsTest/list_including_internals.json @@ -1,101 +1,115 @@ -{"webServices": [ - { - "path": "api/metric", - "since": "3.2", - "description": "Metrics", - "actions": [ - { - "key": "create", - "description": "Create metric", - "since": "4.1", - "internal": false, - "post": true, - "hasResponseExample": true, - "params": [ - { - "key": "name", - "required": false, - "internal": false - }, - { - "key": "severity", - "description": "Severity", - "required": false, - "internal": false, - "defaultValue": "BLOCKER", - "exampleValue": "INFO", - "possibleValues": ["BLOCKER", "INFO"] - }, - { - "key": "internal", - "required": false, - "internal": true - } - ] - }, - { - "key": "internal_action", - "since": "5.3", - "internal": true, - "post": false, - "hasResponseExample": true - }, - { - "key": "show", - "since": "3.2", - "internal": false, - "post": false, - "hasResponseExample": true - } - ] - }, - { - "path": "api/webservices", - "description": "Get information on the web api supported on this instance.", - "since": "4.2", - "actions": [ - { - "key": "list", - "since": "4.2", - "description": "List web services", - "internal": false, - "post": false, - "hasResponseExample": true, - "params": [ - { - "key": "include_internals", - "description": "Include web services that are implemented for internal use only. Their forward-compatibility is not assured", - "required": false, - "internal": false, - "defaultValue": "false", - "possibleValues": ["true", "false", "yes", "no"] - } - ] - }, - { - "key": "response_example", - "since": "4.4", - "description": "Display web service response example", - "internal": false, - "post": false, - "hasResponseExample": true, - "params": [ - { - "key": "action", - "required": true, - "internal": false, - "description": "Action of the web service", - "exampleValue": "search" - }, - { - "key": "controller", - "required": true, - "internal": false, - "description": "Controller of the web service", - "exampleValue": "api/issues" - } - ] - } - ] - } -]} +{ + "webServices": [ + { + "path": "api/metric", + "since": "3.2", + "description": "Metrics", + "actions": [ + { + "key": "create", + "description": "Create metric", + "since": "4.1", + "internal": false, + "post": true, + "hasResponseExample": true, + "params": [ + { + "key": "maximum_value", + "maximumValue": 12 + }, + { + "key": "name", + "required": false, + "internal": false + }, + { + "key": "severity", + "description": "Severity", + "required": false, + "internal": false, + "defaultValue": "BLOCKER", + "exampleValue": "INFO", + "possibleValues": [ + "BLOCKER", + "INFO" + ] + }, + { + "key": "internal", + "required": false, + "internal": true + } + ] + }, + { + "key": "internal_action", + "since": "5.3", + "internal": true, + "post": false, + "hasResponseExample": true + }, + { + "key": "show", + "since": "3.2", + "internal": false, + "post": false, + "hasResponseExample": true + } + ] + }, + { + "path": "api/webservices", + "description": "Get information on the web api supported on this instance.", + "since": "4.2", + "actions": [ + { + "key": "list", + "since": "4.2", + "description": "List web services", + "internal": false, + "post": false, + "hasResponseExample": true, + "params": [ + { + "key": "include_internals", + "description": "Include web services that are implemented for internal use only. Their forward-compatibility is not assured", + "required": false, + "internal": false, + "defaultValue": "false", + "possibleValues": [ + "true", + "false", + "yes", + "no" + ] + } + ] + }, + { + "key": "response_example", + "since": "4.4", + "description": "Display web service response example", + "internal": false, + "post": false, + "hasResponseExample": true, + "params": [ + { + "key": "action", + "required": true, + "internal": false, + "description": "Action of the web service", + "exampleValue": "search" + }, + { + "key": "controller", + "required": true, + "internal": false, + "description": "Controller of the web service", + "exampleValue": "api/issues" + } + ] + } + ] + } + ] +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java index 9d07ee5307a..02285cab9ba 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java @@ -386,10 +386,11 @@ public interface WebService extends Definable { public NewParam createPageSize(int defaultPageSize, int maxPageSize) { return createParam(Param.PAGE_SIZE) - .setDescription("Page size. Must be greater than 0 and less than " + maxPageSize) - .setExampleValue("20") .setDeprecatedKey("pageSize", "5.2") - .setDefaultValue(String.valueOf(defaultPageSize)); + .setDefaultValue(String.valueOf(defaultPageSize)) + .setMaximumValue(maxPageSize) + .setDescription("Page size. Must be greater than 0 and less than " + maxPageSize) + .setExampleValue("20"); } /** @@ -635,6 +636,7 @@ public interface WebService extends Definable { private Set possibleValues = null; private Integer maxValuesAllowed; private Integer maximumLength; + private Integer maximumValue; private NewParam(String key) { this.key = key; @@ -779,6 +781,14 @@ public interface WebService extends Definable { return this; } + /** + * @since 7.0 + */ + public NewParam setMaximumValue(@Nullable Integer maximumValue) { + this.maximumValue = maximumValue; + return this; + } + @Override public String toString() { return key; @@ -834,6 +844,7 @@ public interface WebService extends Definable { private final boolean internal; private final Set possibleValues; private final Integer maximumLength; + private final Integer maximumValue; private final Integer maxValuesAllowed; protected Param(Action action, NewParam newParam) { @@ -850,6 +861,7 @@ public interface WebService extends Definable { this.possibleValues = newParam.possibleValues; this.maxValuesAllowed = newParam.maxValuesAllowed; this.maximumLength = newParam.maximumLength; + this.maximumValue = newParam.maximumValue; checkArgument(!required || defaultValue == null, "Default value must not be set on parameter '%s?%s' as it's marked as required", action, key); } @@ -938,7 +950,7 @@ public interface WebService extends Definable { } /** - * Specify the maximum number of values allowed when using this a parameter + * Specify the maximum number of values allowed when using {@link Request#multiParam(String)} * * @since 6.4 */ @@ -956,6 +968,16 @@ public interface WebService extends Definable { return maximumLength; } + /** + * Specify the maximum value of the numeric variable used in this parameter + * + * @since 7.0 + */ + @CheckForNull + public Integer maximumValue() { + return maximumValue; + } + @Override public String toString() { return key; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java index 495cd8e5d82..a9141a3943d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java @@ -32,6 +32,7 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.WebService; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.Objects.requireNonNull; @@ -68,21 +69,15 @@ public abstract class ValidatingRequest extends Request { @CheckForNull public String param(String key) { WebService.Param definition = action.param(key); - String value = defaultString(readParam(key, definition), definition.defaultValue()); - - Integer maximumLength = definition.maximumLength(); - if (value != null && maximumLength != null) { - int valueLength = value.length(); - checkArgument(valueLength <= maximumLength, - "'%s' length (%s) is longer than the maximum authorized (%s)", - key, valueLength, maximumLength); - } - - String trimmedValue = value == null ? null : CharMatcher.WHITESPACE.trimFrom(value); - if (trimmedValue != null) { - validateValue(trimmedValue, definition); + String valueOrDefault = defaultString(readParam(key, definition), definition.defaultValue()); + String value = valueOrDefault == null ? null : CharMatcher.WHITESPACE.trimFrom(valueOrDefault); + if (value == null) { + return null; } - return trimmedValue; + validatePossibleValues(key, value, definition); + validateMaximumLength(key, definition, valueOrDefault); + validateMaximumValue(key, definition, value); + return value; } @Override @@ -167,12 +162,42 @@ public abstract class ValidatingRequest extends Request { private static List validateValues(List values, WebService.Param definition) { Integer maximumValues = definition.maxValuesAllowed(); checkArgument(maximumValues == null || values.size() <= maximumValues, "'%s' can contains only %s values, got %s", definition.key(), maximumValues, values.size()); - values.forEach(value -> validateValue(value, definition)); + values.forEach(value -> validatePossibleValues(definition.key(), value, definition)); return values; } - private static void validateValue(String value, WebService.Param definition) { + private static void validatePossibleValues(String key, String value, WebService.Param definition) { Set possibleValues = definition.possibleValues(); - checkArgument(possibleValues == null || possibleValues.contains(value), "Value of parameter '%s' (%s) must be one of: %s", definition.key(), value, possibleValues); + if (possibleValues == null) { + return; + } + checkArgument(possibleValues.contains(value), "Value of parameter '%s' (%s) must be one of: %s", key, value, possibleValues); } + + private static void validateMaximumLength(String key, WebService.Param definition, String valueOrDefault) { + Integer maximumLength = definition.maximumLength(); + if (maximumLength == null) { + return; + } + int valueLength = valueOrDefault.length(); + checkArgument(valueLength <= maximumLength, "'%s' length (%s) is longer than the maximum authorized (%s)", key, valueLength, maximumLength); + } + + private static void validateMaximumValue(String key, WebService.Param definition, String value) { + Integer maximumValue = definition.maximumValue(); + if (maximumValue == null) { + return; + } + int valueAsInt = validateAsNumeric(key, value); + checkArgument(valueAsInt <= maximumValue, "'%s' value (%s) must be less than %s", key, valueAsInt, maximumValue); + } + + private static int validateAsNumeric(String key, String value) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException exception) { + throw new IllegalStateException(format("'%s' value '%s' cannot be parsed as an integer", key, value), exception); + } + } + } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java index e95cf3077db..4f3ee5aaa69 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java @@ -121,6 +121,28 @@ public class RequestTest { underTest.setParam(parameter, repeat("X", 11)).param(parameter); } + @Test + public void maximum_value_ok() { + String param = "maximum_value_param"; + defineParameterTestAction(newParam -> newParam.setMaximumValue(10), param); + String expected = "10"; + + String actual = underTest.setParam(param, expected).param(param); + + assertThat(actual).isEqualTo(expected); + } + + @Test + public void maximum_value_not_ok() { + String param = "maximum_value_param"; + defineParameterTestAction(newParam -> newParam.setMaximumValue(10), param); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage(format("'%s' value (11) must be less than 10", param)); + + underTest.setParam(param, "11").param(param); + } + @Test public void required_param_as_strings() { underTest.setParam("a_required_string", "foo,bar"); @@ -495,7 +517,7 @@ public class RequestTest { @DataProvider public static Object[][] date_times() { - return new Object[][]{ + return new Object[][] { {"2014-05-27", parseDate("2014-05-27")}, {"2014-05-27T15:50:45+0100", parseDateTime("2014-05-27T15:50:45+0100")}, {null, null} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java index 4b674ea392c..3cf9d04101f 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java @@ -315,6 +315,20 @@ public class WebServiceTest { assertThat(action.param("string_value").maximumLength()).isEqualTo(24); } + @Test + public void param_with_maximum_value() { + ((WebService) context -> { + NewController newController = context.createController("api/custom_measures"); + NewAction create = newDefaultAction(newController, "create"); + create.createParam("numeric_value") + .setMaximumValue(10); + newController.done(); + }).define(context); + + WebService.Action action = context.controller("api/custom_measures").action("create"); + assertThat(action.param("numeric_value").maximumValue()).isEqualTo(10); + } + @Test public void fail_if_required_param_has_default_value() { expectedException.expect(IllegalArgumentException.class); -- 2.39.5