Repository: james-project Updated Branches: refs/heads/master c8d7d02c9 -> fce7b92ba
JAMES-1961 Implement a route in WebAdmin for MaxQuotas Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/3b0bc0b2 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/3b0bc0b2 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/3b0bc0b2 Branch: refs/heads/master Commit: 3b0bc0b2010edcd7afb64b0d871cb176abfebe5f Parents: c8d7d02 Author: tlcong <tlc...@linagora.com> Authored: Fri Mar 10 10:58:09 2017 +0700 Committer: benwa <btell...@linagora.com> Committed: Tue Mar 21 09:56:58 2017 +0700 ---------------------------------------------------------------------- .../org/apache/james/webadmin/dto/QuotaDTO.java | 73 ++++++ .../apache/james/webadmin/dto/QuotaRequest.java | 39 +++ .../webadmin/routes/GlobalQuotaRoutes.java | 130 +++++++++ .../james/webadmin/dto/QuotaRequestTest.java | 59 +++++ .../webadmin/routes/GlobalQuotaRoutesTest.java | 261 +++++++++++++++++++ 5 files changed, 562 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/3b0bc0b2/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java new file mode 100644 index 0000000..b3403ae --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java @@ -0,0 +1,73 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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 org.apache.james.webadmin.dto; + +import org.apache.james.mailbox.model.Quota; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.common.base.Preconditions; + +@JsonDeserialize(builder = QuotaDTO.Builder.class) +public class QuotaDTO { + public static Builder builder() { + return new Builder(); + } + + @JsonPOJOBuilder(withPrefix="") + public static class Builder { + private long count; + private long size; + + public Builder count(long count) { + this.count = count; + return this; + } + + public Builder size(long size) { + this.size = size; + return this; + } + + public QuotaDTO build() { + return new QuotaDTO(count, size); + } + + } + + private final long count; + private final long size; + + private QuotaDTO(long count, long size) { + Preconditions.checkArgument(count >= Quota.UNLIMITED); + Preconditions.checkArgument(size >= Quota.UNLIMITED); + this.count = count; + this.size = size; + } + + public long getCount() { + return count; + } + + public long getSize() { + return size; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/3b0bc0b2/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java new file mode 100644 index 0000000..7ae9e85 --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java @@ -0,0 +1,39 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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 org.apache.james.webadmin.dto; + +import com.google.common.base.Preconditions; + +public class QuotaRequest { + public static QuotaRequest parse(String serialized) { + return new QuotaRequest(Long.valueOf(serialized)); + } + + private final long value; + + public QuotaRequest(long value) { + Preconditions.checkArgument(value >= 0); + this.value = value; + } + + public long getValue() { + return value; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/3b0bc0b2/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java new file mode 100644 index 0000000..eeca83a --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java @@ -0,0 +1,130 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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 org.apache.james.webadmin.routes; + +import javax.inject.Inject; + +import org.apache.james.mailbox.model.Quota; +import org.apache.james.mailbox.quota.MaxQuotaManager; +import org.apache.james.webadmin.Constants; +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.dto.QuotaDTO; +import org.apache.james.webadmin.dto.QuotaRequest; +import org.apache.james.webadmin.utils.JsonExtractException; +import org.apache.james.webadmin.utils.JsonExtractor; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import spark.Service; + +public class GlobalQuotaRoutes implements Routes { + + public static final String QUOTA_ENDPOINT = "/quota"; + public static final String COUNT_ENDPOINT = QUOTA_ENDPOINT + "/count"; + public static final String SIZE_ENDPOINT = QUOTA_ENDPOINT + "/size"; + private static final Logger LOGGER = LoggerFactory.getLogger(Routes.class); + + private final MaxQuotaManager maxQuotaManager; + private final JsonTransformer jsonTransformer; + private final JsonExtractor<QuotaDTO> jsonExtractor; + + @Inject + public GlobalQuotaRoutes(MaxQuotaManager maxQuotaManager, JsonTransformer jsonTransformer) { + this.maxQuotaManager = maxQuotaManager; + this.jsonTransformer = jsonTransformer; + this.jsonExtractor = new JsonExtractor<>(QuotaDTO.class); + } + + @Override + public void define(Service service) { + service.get(COUNT_ENDPOINT, (request, response) -> { + long value = maxQuotaManager.getDefaultMaxMessage(); + response.status(200); + return value; + }, jsonTransformer); + + service.delete(COUNT_ENDPOINT, (request, response) -> { + maxQuotaManager.setDefaultMaxMessage(Quota.UNLIMITED); + response.status(204); + return Constants.EMPTY_BODY; + }); + + service.put(COUNT_ENDPOINT, (request, response) -> { + try { + QuotaRequest quotaRequest = QuotaRequest.parse(request.body()); + maxQuotaManager.setDefaultMaxMessage(quotaRequest.getValue()); + response.status(204); + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid quota. Need to be an integer value greater than 0"); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.get(SIZE_ENDPOINT, (request, response) -> { + long value = maxQuotaManager.getDefaultMaxStorage(); + response.status(200); + return value; + }, jsonTransformer); + + service.delete(SIZE_ENDPOINT, (request, response) -> { + maxQuotaManager.setDefaultMaxStorage(Quota.UNLIMITED); + response.status(204); + return Constants.EMPTY_BODY; + }); + + service.put(SIZE_ENDPOINT, (request, response) -> { + try { + QuotaRequest quotaRequest = QuotaRequest.parse(request.body()); + maxQuotaManager.setDefaultMaxStorage(quotaRequest.getValue()); + response.status(204); + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid quota. Need to be an integer value greater than 0"); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.get(QUOTA_ENDPOINT, (request, response) -> { + QuotaDTO quotaDTO = QuotaDTO.builder() + .count(maxQuotaManager.getDefaultMaxMessage()) + .size(maxQuotaManager.getDefaultMaxStorage()).build(); + response.status(200); + return quotaDTO; + }, jsonTransformer); + + service.put(QUOTA_ENDPOINT, ((request, response) -> { + try { + QuotaDTO quotaDTO = jsonExtractor.parse(request.body()); + maxQuotaManager.setDefaultMaxMessage(quotaDTO.getCount()); + maxQuotaManager.setDefaultMaxStorage(quotaDTO.getSize()); + response.status(204); + } catch (JsonExtractException e) { + LOGGER.info("Malformed JSON", e); + response.status(400); + } catch (IllegalArgumentException e) { + LOGGER.info("Quota should be positive or unlimited (-1)", e); + response.status(400); + } + return Constants.EMPTY_BODY; + })); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/3b0bc0b2/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/dto/QuotaRequestTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/dto/QuotaRequestTest.java b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/dto/QuotaRequestTest.java new file mode 100644 index 0000000..34df209 --- /dev/null +++ b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/dto/QuotaRequestTest.java @@ -0,0 +1,59 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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 org.apache.james.webadmin.dto; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class QuotaRequestTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void parseShouldThrowWhenNotANumber() { + expectedException.expect(NumberFormatException.class); + + QuotaRequest.parse("invalid"); + } + + @Test + public void parseShouldThrowOnNegativeNumber() { + expectedException.expect(IllegalArgumentException.class); + + QuotaRequest.parse("-1"); + } + + @Test + public void parseShouldParseZero() { + assertThat(QuotaRequest.parse("0").getValue()) + .isEqualTo(0); + } + + @Test + public void parseShouldParsePositiveValue() { + assertThat(QuotaRequest.parse("42").getValue()) + .isEqualTo(42); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/3b0bc0b2/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/GlobalQuotaRoutesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/GlobalQuotaRoutesTest.java b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/GlobalQuotaRoutesTest.java new file mode 100644 index 0000000..0e2d5f6 --- /dev/null +++ b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/GlobalQuotaRoutesTest.java @@ -0,0 +1,261 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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 org.apache.james.webadmin.routes; + +import static org.assertj.core.api.Assertions.assertThat; +import static com.jayway.restassured.RestAssured.given; +import static com.jayway.restassured.config.EncoderConfig.encoderConfig; +import static com.jayway.restassured.config.RestAssuredConfig.newConfig; +import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION; +import static org.hamcrest.CoreMatchers.is; + +import org.apache.james.mailbox.inmemory.quota.InMemoryPerUserMaxQuotaManager; +import org.apache.james.mailbox.model.Quota; +import org.apache.james.metrics.logger.DefaultMetricFactory; +import org.apache.james.webadmin.WebAdminServer; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.base.Charsets; +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +public class GlobalQuotaRoutesTest { + + private WebAdminServer webAdminServer; + private InMemoryPerUserMaxQuotaManager maxQuotaManager; + + @Before + public void setUp() throws Exception { + maxQuotaManager = new InMemoryPerUserMaxQuotaManager(); + webAdminServer = new WebAdminServer( + new DefaultMetricFactory(), + new GlobalQuotaRoutes(maxQuotaManager, new JsonTransformer())); + webAdminServer.configure(NO_CONFIGURATION); + webAdminServer.await(); + + RestAssured.requestSpecification = new RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setAccept(ContentType.JSON) + .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8))) + .setPort(webAdminServer.getPort().toInt()) + .build(); + } + + @After + public void stop() { + webAdminServer.destroy(); + } + + @Test + public void getCountQuotaCountShouldReturnUnlimitedByDefault() { + given() + .get(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(200) + .body(is(String.valueOf(Quota.UNLIMITED))); + } + + @Test + public void getCountShouldReturnStoredValue() throws Exception{ + int value = 42; + maxQuotaManager.setDefaultMaxMessage(value); + + given() + .get(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(200) + .body(is(String.valueOf(value))); + } + + @Test + public void putCountShouldRejectInvalid() throws Exception { + given() + .body("invalid") + .put(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(400); + } + + @Test + public void putCountShouldRejectNegative() throws Exception { + given() + .body("-1") + .put(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(400); + } + + @Test + public void putCountShouldAcceptValidValue() throws Exception { + given() + .body("42") + .put(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(204); + + assertThat(maxQuotaManager.getDefaultMaxMessage()).isEqualTo(42); + } + + @Test + public void deleteCountShouldSetQuotaToUnlimited() throws Exception { + maxQuotaManager.setDefaultMaxMessage(42); + + given() + .delete(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(204); + + assertThat(maxQuotaManager.getDefaultMaxMessage()).isEqualTo(Quota.UNLIMITED); + } + + @Test + public void getSizeQuotaCountShouldReturnUnlimitedByDefault() { + given() + .get(GlobalQuotaRoutes.SIZE_ENDPOINT) + .then() + .statusCode(200) + .body(is(String.valueOf(Quota.UNLIMITED))); + } + + @Test + public void getSizeShouldReturnStoredValue() throws Exception{ + int value = 42; + maxQuotaManager.setDefaultMaxStorage(value); + + given() + .get(GlobalQuotaRoutes.SIZE_ENDPOINT) + .then() + .statusCode(200) + .body(is(String.valueOf(value))); + } + + @Test + public void putSizeShouldRejectInvalid() throws Exception { + given() + .body("invalid") + .put(GlobalQuotaRoutes.SIZE_ENDPOINT) + .then() + .statusCode(400); + } + + @Test + public void putSizeShouldRejectNegative() throws Exception { + given() + .body("-1") + .put(GlobalQuotaRoutes.SIZE_ENDPOINT) + .then() + .statusCode(400); + } + + @Test + public void putSizeShouldAcceptValidValue() throws Exception { + given() + .body("42") + .put(GlobalQuotaRoutes.SIZE_ENDPOINT) + .then() + .statusCode(204); + + assertThat(maxQuotaManager.getDefaultMaxStorage()).isEqualTo(42); + } + + @Test + public void deleteSizeShouldSetQuotaToUnlimited() throws Exception { + maxQuotaManager.setDefaultMaxStorage(42); + + given() + .delete(GlobalQuotaRoutes.COUNT_ENDPOINT) + .then() + .statusCode(204); + + assertThat(maxQuotaManager.getDefaultMaxMessage()).isEqualTo(Quota.UNLIMITED); + } + + @Test + public void getQuotaShouldReturnBothWhenValueSpecified() throws Exception { + maxQuotaManager.setDefaultMaxStorage(42); + maxQuotaManager.setDefaultMaxMessage(52); + + given() + .get(GlobalQuotaRoutes.QUOTA_ENDPOINT) + .then() + .statusCode(200) + .body(is("{\"count\":52,\"size\":42}")); + } + + @Test + public void getQuotaShouldReturnBothDefaultValues() throws Exception { + given() + .get(GlobalQuotaRoutes.QUOTA_ENDPOINT) + .then() + .statusCode(200) + .body(is("{\"count\":-1,\"size\":-1}")); + } + + @Test + public void getQuotaShouldReturnBothWhenNoCount() throws Exception { + maxQuotaManager.setDefaultMaxStorage(42); + + given() + .get(GlobalQuotaRoutes.QUOTA_ENDPOINT) + .then() + .statusCode(200) + .body(is("{\"count\":-1,\"size\":42}")); + } + + @Test + public void getQuotaShouldReturnBothWhenNoSize() throws Exception { + maxQuotaManager.setDefaultMaxMessage(42); + + given() + .get(GlobalQuotaRoutes.QUOTA_ENDPOINT) + .then() + .statusCode(200) + .body(is("{\"count\":42,\"size\":-1}")); + } + + @Test + public void putQuotaShouldUpdateBothQuota() throws Exception { + given() + .body("{\"count\":52,\"size\":42}") + .put(GlobalQuotaRoutes.QUOTA_ENDPOINT) + .then() + .statusCode(204); + + assertThat(maxQuotaManager.getDefaultMaxMessage()).isEqualTo(52); + assertThat(maxQuotaManager.getDefaultMaxStorage()).isEqualTo(42); + } + + @Test + public void putQuotaShouldBaAbleToRemoveBothQuota() throws Exception { + given() + .body("{\"count\":-1,\"size\":-1}") + .put(GlobalQuotaRoutes.QUOTA_ENDPOINT) + .then() + .statusCode(204); + + assertThat(maxQuotaManager.getDefaultMaxMessage()).isEqualTo(Quota.UNLIMITED); + assertThat(maxQuotaManager.getDefaultMaxStorage()).isEqualTo(Quota.UNLIMITED); + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org