james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From btell...@apache.org
Subject [3/9] james-project git commit: JAMES-1759 WebAdminServer should allow CRUD operations on users
Date Wed, 22 Jun 2016 08:36:43 GMT
JAMES-1759 WebAdminServer should allow CRUD operations on users


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/ef155e3d
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ef155e3d
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ef155e3d

Branch: refs/heads/master
Commit: ef155e3d81bdf4aa2ce69798a332b965ddc29391
Parents: 6053d80
Author: Benoit Tellier <btellier@linagora.com>
Authored: Thu Jun 16 19:13:41 2016 +0700
Committer: Benoit Tellier <btellier@linagora.com>
Committed: Wed Jun 22 15:34:15 2016 +0700

----------------------------------------------------------------------
 server/protocols/webadmin/pom.xml               |   6 +
 .../james/webadmin/model/AddUserRequest.java    |  39 ++
 .../james/webadmin/model/UserResponse.java      |  33 ++
 .../james/webadmin/routes/UserRoutes.java       | 104 +++++
 .../james/webadmin/service/UserService.java     |  94 +++++
 .../webadmin/utils/JsonExtractException.java    |  27 ++
 .../james/webadmin/utils/JsonExtractor.java     |  44 ++
 .../james/webadmin/routes/UsersRoutesTest.java  | 415 +++++++++++++++++++
 .../james/webadmin/utils/JsonExtractorTest.java | 114 +++++
 9 files changed, 876 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/pom.xml b/server/protocols/webadmin/pom.xml
index 13b0598..a23ee8e 100644
--- a/server/protocols/webadmin/pom.xml
+++ b/server/protocols/webadmin/pom.xml
@@ -199,6 +199,12 @@
                     <scope>test</scope>
                 </dependency>
                 <dependency>
+                    <groupId>org.assertj</groupId>
+                    <artifactId>assertj-core</artifactId>
+                    <version>${assertj-3.version}</version>
+                    <scope>test</scope>
+                </dependency>
+                <dependency>
                     <groupId>org.mockito</groupId>
                     <artifactId>mockito-core</artifactId>
                     <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/AddUserRequest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/AddUserRequest.java
b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/AddUserRequest.java
new file mode 100644
index 0000000..b23c7e2
--- /dev/null
+++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/AddUserRequest.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.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+
+public class AddUserRequest {
+
+    private final char[] password;
+
+    @JsonCreator
+    public AddUserRequest(@JsonProperty("password") char[] password) {
+        Preconditions.checkNotNull(password);
+        this.password = password;
+    }
+
+    public char[] getPassword() {
+        return password;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/UserResponse.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/UserResponse.java
b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/UserResponse.java
new file mode 100644
index 0000000..9c41c77
--- /dev/null
+++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/UserResponse.java
@@ -0,0 +1,33 @@
+/****************************************************************
+ * 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.model;
+
+public class UserResponse {
+
+    private final String username;
+
+    public UserResponse(String username) {
+        this.username = username;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
new file mode 100644
index 0000000..d9f577f
--- /dev/null
+++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java
@@ -0,0 +1,104 @@
+/****************************************************************
+ * 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.apache.james.webadmin.Constants.SEPARATOR;
+
+import javax.inject.Inject;
+
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.model.AddUserRequest;
+import org.apache.james.webadmin.service.UserService;
+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.Request;
+import spark.Response;
+import spark.Service;
+
+public class UserRoutes implements Routes {
+
+    private static final String USER_NAME = ":userName";
+    private static final String EMPTY_BODY = "";
+    private static final Logger LOGGER = LoggerFactory.getLogger(UserRoutes.class);
+
+    public static final String USERS = "/users";
+
+    private final UserService userService;
+    private final JsonTransformer jsonTransformer;
+    private final JsonExtractor<AddUserRequest> jsonExtractor;
+
+    @Inject
+    public UserRoutes(UserService userService, JsonTransformer jsonTransformer) {
+        this.userService = userService;
+        this.jsonTransformer = jsonTransformer;
+        this.jsonExtractor = new JsonExtractor<>(AddUserRequest.class);
+    }
+
+    @Override
+    public void define(Service service) {
+        service.get(USERS,
+            (request, response) -> userService.getUsers(),
+            jsonTransformer);
+
+        service.put(USERS + SEPARATOR + USER_NAME, this::upsertUser);
+
+        service.delete(USERS + SEPARATOR + USER_NAME, this::removeUser);
+    }
+
+    private String removeUser(Request request, Response response) {
+        String username = request.params(USER_NAME);
+        try {
+            userService.removeUser(username);
+            response.status(204);
+            return EMPTY_BODY;
+        } catch (UsersRepositoryException e) {
+            response.status(204);
+            return "The user " + username + " does not exists";
+        } catch (IllegalArgumentException e) {
+            LOGGER.info("Invalid user path", e);
+            response.status(400);
+            return EMPTY_BODY;
+        }
+    }
+
+    private String upsertUser(Request request, Response response) throws UsersRepositoryException
{
+        try {
+            return userService.upsertUser(request.params(USER_NAME),
+                jsonExtractor.parse(request.body()).getPassword(),
+                response);
+        } catch (JsonExtractException e) {
+            LOGGER.info("Error while deserializing addUser request", e);
+            response.status(400);
+            return EMPTY_BODY;
+        } catch (IllegalArgumentException e) {
+            LOGGER.info("Invalid user path", e);
+            response.status(400);
+            return EMPTY_BODY;
+        }
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserService.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserService.java
b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserService.java
new file mode 100644
index 0000000..bbe4e4e
--- /dev/null
+++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserService.java
@@ -0,0 +1,94 @@
+/****************************************************************
+ * 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.service;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.james.user.api.model.User;
+import org.apache.james.util.streams.ImmutableCollectors;
+import org.apache.james.util.streams.Iterators;
+import org.apache.james.webadmin.model.UserResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+import spark.Response;
+
+public class UserService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);
+    private static final String EMPTY_BODY = "";
+    public static final int MAXIMUM_MAIL_ADDRESS_LENGTH = 255;
+
+    private final UsersRepository usersRepository;
+
+    @Inject
+    public UserService(UsersRepository usersRepository) {
+        this.usersRepository = usersRepository;
+    }
+
+    public List<UserResponse> getUsers() throws UsersRepositoryException {
+        return  Optional.ofNullable(usersRepository.list())
+            .map(Iterators::toStream)
+            .orElse(Stream.of())
+            .map(UserResponse::new)
+            .collect(ImmutableCollectors.toImmutableList());
+    }
+
+    public void removeUser(String username) throws UsersRepositoryException {
+        usernamePreconditions(username);
+        usersRepository.removeUser(username);
+    }
+
+    public String upsertUser(String username, char[] password, Response response) throws
UsersRepositoryException {
+        usernamePreconditions(username);
+        User user = usersRepository.getUserByName(username);
+        try {
+            upsert(user, username, password);
+            response.status(204);
+        } catch (UsersRepositoryException e) {
+            LOGGER.info("Error creating or updating user : {}", e.getMessage());
+            response.status(409);
+        }
+        return EMPTY_BODY;
+    }
+
+    private void usernamePreconditions(String username) {
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(username));
+        Preconditions.checkArgument(username.length() < MAXIMUM_MAIL_ADDRESS_LENGTH);
+    }
+
+    private void upsert(User user, String username, char[] password) throws UsersRepositoryException
{
+        if (user == null) {
+            usersRepository.addUser(username, new String(password));
+        } else {
+            user.setPassword(new String(password));
+            usersRepository.updateUser(user);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java
b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java
new file mode 100644
index 0000000..dc6b578
--- /dev/null
+++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java
@@ -0,0 +1,27 @@
+/****************************************************************
+ * 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.utils;
+
+public class JsonExtractException extends Exception {
+
+    public JsonExtractException(Throwable throwable) {
+        super(throwable);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
new file mode 100644
index 0000000..f4eb420
--- /dev/null
+++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
@@ -0,0 +1,44 @@
+/****************************************************************
+ * 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.utils;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonExtractor<Request> {
+
+    private final ObjectMapper objectMapper;
+    private final Class<Request> type;
+
+    public JsonExtractor(Class<Request> type) {
+        this.objectMapper = new ObjectMapper();
+        this.type = type;
+    }
+
+    public Request parse(String text) throws JsonExtractException {
+        try {
+            return objectMapper.readValue(text, type);
+        } catch (IOException e) {
+            throw new JsonExtractException(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java
b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java
new file mode 100644
index 0000000..9ae9ab7
--- /dev/null
+++ b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java
@@ -0,0 +1,415 @@
+/****************************************************************
+ * 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 com.jayway.restassured.RestAssured.given;
+import static com.jayway.restassured.RestAssured.when;
+import static com.jayway.restassured.RestAssured.with;
+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.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.james.user.api.model.User;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.service.UserService;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.google.common.base.Charsets;
+import com.jayway.restassured.RestAssured;
+import com.jayway.restassured.parsing.Parser;
+
+import de.bechte.junit.runners.context.HierarchicalContextRunner;
+
+@RunWith(HierarchicalContextRunner.class)
+public class UsersRoutesTest {
+
+    public static final String DOMAIN = "domain";
+    public static final String USERNAME = "username@" + DOMAIN;
+    private WebAdminServer webAdminServer;
+
+    private void createServer(UsersRepository usersRepository) throws Exception {
+        webAdminServer = new WebAdminServer(new UserRoutes(new UserService(usersRepository),
new JsonTransformer()));
+        webAdminServer.configure(NO_CONFIGURATION);
+        webAdminServer.await();
+
+        RestAssured.port = webAdminServer.getPort().toInt();
+        RestAssured.config = newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8));
+        RestAssured.defaultParser = Parser.JSON;
+        RestAssured.basePath = UserRoutes.USERS;
+    }
+
+    @After
+    public void stop() {
+        webAdminServer.destroy();
+    }
+
+    public class NormalBehaviour {
+
+        @Before
+        public void setUp() throws Exception {
+            DomainList domainList = mock(DomainList.class);
+            when(domainList.containsDomain(DOMAIN)).thenReturn(true);
+
+            MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting();
+            usersRepository.setDomainList(domainList);
+
+            createServer(usersRepository);
+        }
+
+        @Test
+        public void getUsersShouldBeEmptyByDefault() {
+            when()
+                .get()
+            .then()
+                .statusCode(200)
+                .body(is("[]"));
+        }
+
+        @Test
+        public void putShouldReturnUserErrorWhenNoBody() {
+            when()
+                .put(USERNAME)
+            .then()
+                .statusCode(400);
+        }
+
+        @Test
+        public void postShouldReturnUserErrorWhenEmptyJsonBody() {
+            given()
+                .body("{}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(400);
+        }
+
+        @Test
+        public void postShouldReturnUserErrorWhenWrongJsonBody() {
+            given()
+                .body("{\"bad\":\"any\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(400);
+        }
+
+        @Test
+        public void postShouldReturnOkWhenValidJsonBody() {
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(204);
+        }
+
+        @Test
+        public void postShouldReturnRequireNonNullPassword() {
+            given()
+                .body("{\"password\":null}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(400);
+        }
+
+        @Test
+        public void postShouldAddTheUser() {
+            with()
+                .body("{\"password\":\"password\"}")
+            .put(USERNAME);
+
+            when()
+                .get()
+            .then()
+                .statusCode(200)
+                .body(equalTo("[{\"username\":\"" + USERNAME + "\"}]"));
+        }
+
+        @Test
+        public void postingTwoTimesShouldBeAllowed() {
+            // Given
+            with()
+                .body("{\"password\":\"password\"}")
+            .put(USERNAME);
+
+            // When
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(204);
+
+            // Then
+            when()
+                .get()
+            .then()
+                .statusCode(200)
+                .body(equalTo("[{\"username\":\"" + USERNAME + "\"}]"));
+        }
+
+        @Test
+        public void deleteShouldReturnOk() {
+            when()
+                .delete(USERNAME)
+            .then()
+                .statusCode(204);
+        }
+
+        @Test
+        public void deleteShouldReturnBadRequestWhenEmptyUserName() {
+            when()
+                .delete("/")
+            .then()
+                .statusCode(404);
+        }
+
+        @Test
+        public void deleteShouldReturnBadRequestWhenUsernameIsTooLong() {
+            when()
+                .delete(USERNAME + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789."
+
+                    "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789."
+
+                    "0123456789.0123456789.0123456789.")
+            .then()
+                .statusCode(400);
+        }
+
+        @Test
+        public void deleteShouldReturnNotFoundWhenUsernameContainsSlash() {
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME + "/" + USERNAME)
+            .then()
+                .statusCode(404);
+        }
+
+        @Test
+        public void putShouldReturnBadRequestWhenEmptyUserName() {
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put("/")
+            .then()
+                .statusCode(404);
+        }
+
+        @Test
+        public void putShouldReturnBadRequestWhenUsernameIsTooLong() {
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789."
+
+                    "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789."
+
+                    "0123456789.0123456789.0123456789.")
+            .then()
+                .statusCode(400);
+        }
+
+        @Test
+        public void putShouldReturnNotFoundWhenUsernameContainsSlash() {
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME + "/" + USERNAME)
+            .then()
+                .statusCode(404);
+        }
+
+        @Test
+        public void deleteShouldRemoveAssociatedUser() {
+            // Given
+            with()
+                .body("{\"password\":\"password\"}")
+            .put(USERNAME);
+
+            // When
+            when()
+                .delete(USERNAME)
+            .then()
+                .statusCode(204);
+
+            // Then
+            when()
+                .get()
+            .then()
+                .statusCode(200)
+                .body(equalTo("[]"));
+        }
+
+        @Test
+        public void deleteShouldStillBeValidWithExtraBody() {
+            given()
+                .body("{\"bad\":\"any\"}")
+            .when()
+                .delete(USERNAME)
+            .then()
+                .statusCode(204);
+        }
+    }
+
+    public class ErrorHandling {
+
+        private UsersRepository usersRepository;
+        private String username;
+        private String password;
+
+        @Before
+        public void setUp() throws Exception {
+            usersRepository = mock(UsersRepository.class);
+            createServer(usersRepository);
+            username = "username@domain";
+            password = "password";
+        }
+
+        @Test
+        public void deleteShouldStillBeOkWhenNoUser() throws Exception {
+            doThrow(new UsersRepositoryException("message")).when(usersRepository).removeUser(username);
+
+            when()
+                .delete(USERNAME)
+            .then()
+                .statusCode(204);
+        }
+
+        @Test
+        public void getShouldFailOnRepositoryException() throws Exception {
+            when(usersRepository.list()).thenThrow(new UsersRepositoryException("message"));
+
+            when()
+                .get()
+            .then()
+                .statusCode(500);
+        }
+
+        @Test
+        public void postShouldFailOnRepositoryExceptionOnGetUserByName() throws Exception
{
+            when(usersRepository.getUserByName(username)).thenThrow(new UsersRepositoryException("message"));
+
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(500);
+        }
+
+        @Test
+        public void postShouldNotFailOnRepositoryExceptionOnAddUser() throws Exception {
+            when(usersRepository.getUserByName(username)).thenReturn(null);
+            doThrow(new UsersRepositoryException("message")).when(usersRepository).addUser(username,
password);
+
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(409);
+        }
+
+        @Test
+        public void postShouldFailOnRepositoryExceptionOnUpdateUser() throws Exception {
+            when(usersRepository.getUserByName(username)).thenReturn(mock(User.class));
+            doThrow(new UsersRepositoryException("message")).when(usersRepository).updateUser(any());
+
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(409);
+        }
+
+
+        @Test
+        public void deleteShouldFailOnUnknownException() throws Exception {
+            doThrow(new RuntimeException()).when(usersRepository).removeUser(username);
+
+            when()
+                .delete(USERNAME)
+            .then()
+                .statusCode(500);
+        }
+
+        @Test
+        public void getShouldFailOnUnknownException() throws Exception {
+            when(usersRepository.list()).thenThrow(new RuntimeException());
+
+            when()
+                .get()
+            .then()
+                .statusCode(500);
+        }
+
+        @Test
+        public void postShouldFailOnUnknownExceptionOnGetUserByName() throws Exception {
+            when(usersRepository.getUserByName(username)).thenThrow(new RuntimeException());
+
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(500);
+        }
+
+        @Test
+        public void postShouldFailOnUnknownExceptionOnAddUser() throws Exception {
+            when(usersRepository.getUserByName(username)).thenReturn(null);
+            doThrow(new RuntimeException()).when(usersRepository).addUser(username, password);
+
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(500);
+        }
+
+        @Test
+        public void postShouldFailOnUnknownExceptionOnGetUpdateUser() throws Exception {
+            when(usersRepository.getUserByName(username)).thenReturn(mock(User.class));
+            doThrow(new RuntimeException()).when(usersRepository).updateUser(any());
+
+            given()
+                .body("{\"password\":\"password\"}")
+            .when()
+                .put(USERNAME)
+            .then()
+                .statusCode(500);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ef155e3d/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java
b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java
new file mode 100644
index 0000000..ff5f2cb
--- /dev/null
+++ b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java
@@ -0,0 +1,114 @@
+/****************************************************************
+ * 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.utils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+
+public class JsonExtractorTest {
+
+    private JsonExtractor<Request> jsonExtractor;
+
+    @Before
+    public void setUp() {
+        jsonExtractor = new JsonExtractor<>(Request.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnNullInput() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse(null)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnEmptyInput() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse("")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnBrokenJson() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"broken")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnEmptyJson() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse("{}")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnMissingMandatoryField() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"any\"}")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnValidationProblemIllegalArgumentException() throws Exception
{
+        assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"\",\"field2\":\"any\"}")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnValidationProblemNPE() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":null,\"field2\":\"any\"}")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldThrowOnExtraFiled() throws Exception {
+        assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"value\",\"field2\":\"any\",\"extra\":\"extra\"}")).isInstanceOf(JsonExtractException.class);
+    }
+
+    @Test
+    public void parseShouldInstantiateDestinationClass() throws Exception {
+        String field1 = "value1";
+        String field2 = "value2";
+        Request request = jsonExtractor.parse("{\"field1\":\"" + field1 + "\",\"field2\":\""
+ field2 + "\"}");
+
+        assertThat(request.getField1()).isEqualTo(field1);
+        assertThat(request.getField2()).isEqualTo(field2);
+    }
+
+    static class Request {
+        private final String field1;
+        private final String field2;
+
+        @JsonCreator
+        public Request(@JsonProperty("field1") String field1,
+                       @JsonProperty("field2") String field2) {
+            Preconditions.checkNotNull(field1);
+            Preconditions.checkNotNull(field2);
+            Preconditions.checkArgument(!field1.isEmpty());
+            this.field1 = field1;
+            this.field2 = field2;
+        }
+
+        public String getField1() {
+            return field1;
+        }
+
+        public String getField2() {
+            return field2;
+        }
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Mime
View raw message