From d1aa2cfed7ba57b3ca27ae6ad116415a146ad6bb Mon Sep 17 00:00:00 2001 From: Yevhenii Voevodin Date: Mon, 27 Jun 2016 09:24:06 +0300 Subject: [PATCH] User structural refactoring --- .../che/wsagent/server/WsAgentModule.java | 2 +- .../che/api/deploy/WsMasterModule.java | 4 +- .../che/api/core/model/user/Profile.java | 38 ++ .../eclipse/che/api/core/model/user/User.java | 61 ++ .../eclipse/che/api/core/model/Model.gwt.xml | 1 + core/commons/che-core-commons-test/pom.xml | 22 + .../che/commons/test/tck/TckModule.java | 59 ++ .../commons/test/tck/TckModuleFactory.java | 127 ++++ .../test/tck/repository/TckRepository.java | 51 ++ .../repository/TckRepositoryException.java | 31 + .../commons/test/tck/DBServerListener.java | 50 ++ .../commons/test/tck/TckComponentsTest.java | 48 ++ .../che/commons/test/tck/TestModule1.java | 36 + .../che/commons/test/tck/TestModule2.java | 26 +- ...org.eclipse.che.commons.test.tck.TckModule | 2 + .../services/org.testng.ITestNGListener | 1 + .../api/user/PreferencesServiceClient.java | 39 ++ .../user/PreferencesServiceClientImpl.java | 67 ++ .../add-registry/add-registry.controller.js | 6 +- .../docker-registry-list.controller.js | 10 +- dashboard/src/app/index.module.js | 16 +- dashboard/src/app/navbar/navbar.controller.js | 6 +- .../list-projects/list-projects.controller.js | 4 +- .../workspace-details-projects.controller.js | 4 +- .../api/builder/che-profile-builder.js | 3 +- .../src/components/api/che-api-config.js | 2 + .../src/components/api/che-api.factory.js | 11 +- .../components/api/che-preferences.factory.js | 190 ++++++ .../components/api/che-preferences.spec.js | 137 ++++ .../src/components/api/che-profile.factory.js | 159 +---- .../src/components/api/che-profile.spec.js | 8 +- .../components/api/test/che-http-backend.js | 25 +- .../learn-more/che-learn-more.controller.js | 10 +- .../eclipse/che/ide/api/app/CurrentUser.java | 13 +- .../api/user/PreferencesServiceClient.java | 39 ++ .../user/PreferencesServiceClientImpl.java | 67 ++ .../api/user/UserProfileServiceClient.java | 49 +- .../user/UserProfileServiceClientImpl.java | 54 +- .../che/ide/api/user/UserServiceClient.java | 10 +- .../ide/api/user/UserServiceClientImpl.java | 10 +- .../eclipse/che/ide/core/CoreGinModule.java | 3 + .../che/ide/core/ProfileComponent.java | 8 +- .../preferences/PreferencesManagerImpl.java | 18 +- ...ificDockerRegistryCredentialsProvider.java | 18 +- ...DockerRegistryCredentialsProviderTest.java | 8 +- .../machine/integration/ServiceTest.java | 54 +- .../GitHubAuthenticatorImpl.java | 2 +- .../GitHubAuthenticatorImplTest.java | 23 +- .../page/GithubImporterPagePresenterTest.java | 12 +- .../main/java/org/eclipse/ui/IWorkingSet.java | 8 +- .../sufficientinfo/MachineInfoPresenter.java | 8 +- .../MachineInfoPresenterTest.java | 24 +- .../client/manage/SshKeyManagerPresenter.java | 2 +- .../CurrentUserPreferencesAccessImpl.java | 5 +- .../server/SubversionProjectImporterTest.java | 7 +- .../plugin/svn/server/utils/TestUtils.java | 8 +- pom.xml | 6 + .../che/api/git/LocalGitUserResolver.java | 2 +- .../project/server/ProjectServiceTest.java | 2 +- .../org/eclipse/che/RemotePreferenceDao.java | 7 +- .../RemotePreferenceDaoCompatibilityTest.java | 27 +- .../api/factory/server/FactoryService.java | 2 +- .../impl/FactoryAcceptValidatorImpl.java | 2 +- .../server/impl/FactoryBaseValidator.java | 3 +- .../impl/FactoryCreateValidatorImpl.java | 2 +- .../factory/server/FactoryServiceTest.java | 10 +- .../server/impl/FactoryBaseValidatorTest.java | 8 +- ...oryCreateAndAcceptValidatorsImplsTest.java | 4 +- .../impl/TesterFactoryBaseValidator.java | 2 +- wsmaster/che-core-api-user-shared/pom.xml | 4 + .../api/user/shared/dto/MembershipDto.java | 52 -- ...ProfileDescriptor.java => ProfileDto.java} | 34 +- .../dto/{UserDescriptor.java => UserDto.java} | 24 +- wsmaster/che-core-api-user/pom.xml | 44 ++ .../che/api/user/server/Constants.java | 42 +- .../che/api/user/server/DtoConverter.java | 10 +- .../api/user/server/PreferenceManager.java | 176 +++++ .../api/user/server/PreferencesService.java | 124 ++++ .../api/user/server/ProfileLinksInjector.java | 83 +++ .../che/api/user/server/ProfileManager.java | 115 ++++ .../che/api/user/server/ProfileService.java | 173 +++++ .../che/api/user/server/TokenValidator.java | 4 +- ...ksInjector.java => UserLinksInjector.java} | 92 ++- .../che/api/user/server/UserManager.java | 187 ++++-- .../api/user/server/UserProfileService.java | 442 ------------ .../che/api/user/server/UserService.java | 371 +++------- ...rNameValidator.java => UserValidator.java} | 79 ++- .../che/api/user/server/dao/UserDao.java | 120 ---- .../api/user/server/dao/UserProfileDao.java | 60 -- .../impl/ProfileImpl.java} | 79 +-- .../User.java => model/impl/UserImpl.java} | 142 ++-- .../server/{dao => spi}/PreferenceDao.java | 11 +- .../che/api/user/server/spi/ProfileDao.java | 85 +++ .../che/api/user/server/spi/UserDao.java | 166 +++++ .../user/server/PreferenceManagerTest.java | 152 +++++ .../user/server/PreferencesServiceTest.java | 169 +++++ .../user/server/ProfileLinksInjectorTest.java | 79 +++ .../api/user/server/ProfileManagerTest.java | 96 +++ .../api/user/server/ProfileServiceTest.java | 219 ++++++ .../user/server/UserLinksInjectorTest.java | 81 +++ .../che/api/user/server/UserManagerTest.java | 209 +++++- .../user/server/UserProfileServiceTest.java | 390 ----------- .../che/api/user/server/UserServiceTest.java | 633 +++++++++--------- ...idatorTest.java => UserValidatorTest.java} | 10 +- .../server/model/impl/DataObjectsTest.java | 230 +++++++ .../user/server/spi/tck/ProfileDaoTest.java | 168 +++++ .../api/user/server/spi/tck/UserDaoTest.java | 337 ++++++++++ wsmaster/wsmaster-local/pom.xml | 52 ++ .../che/api/local/DummyTokenValidator.java | 6 +- .../api/local/LocalInfrastructureModule.java | 25 +- .../che/api/local/LocalPreferenceDaoImpl.java | 5 +- .../che/api/local/LocalProfileDaoImpl.java | 66 +- .../che/api/local/LocalUserDaoImpl.java | 318 +++++---- .../api/local/LocalProfileTckRepository.java | 39 ++ .../eclipse/che/api/local/LocalTckModule.java | 61 ++ .../che/api/local/LocalUserTckRepository.java | 38 ++ ...org.eclipse.che.commons.test.tck.TckModule | 1 + 117 files changed, 5253 insertions(+), 2633 deletions(-) create mode 100644 core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/Profile.java create mode 100644 core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/User.java create mode 100644 core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModule.java create mode 100644 core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModuleFactory.java create mode 100644 core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepository.java create mode 100644 core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepositoryException.java create mode 100644 core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/DBServerListener.java create mode 100644 core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TckComponentsTest.java create mode 100644 core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule1.java rename wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/model/Membership.java => core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule2.java (53%) create mode 100644 core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule create mode 100644 core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.testng.ITestNGListener create mode 100644 core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java create mode 100644 core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java create mode 100644 dashboard/src/components/api/che-preferences.factory.js create mode 100644 dashboard/src/components/api/che-preferences.spec.js create mode 100644 ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java create mode 100644 ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java delete mode 100644 wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/MembershipDto.java rename wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/{ProfileDescriptor.java => ProfileDto.java} (68%) rename wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/{UserDescriptor.java => UserDto.java} (72%) create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferenceManager.java create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferencesService.java create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileLinksInjector.java create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileManager.java create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileService.java rename wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/{LinksInjector.java => UserLinksInjector.java} (54%) delete mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserProfileService.java rename wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/{UserNameValidator.java => UserValidator.java} (51%) delete mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserDao.java delete mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserProfileDao.java rename wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/{dao/Profile.java => model/impl/ProfileImpl.java} (55%) rename wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/{dao/User.java => model/impl/UserImpl.java} (58%) rename wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/{dao => spi}/PreferenceDao.java (89%) create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/ProfileDao.java create mode 100644 wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/UserDao.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferenceManagerTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferencesServiceTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileLinksInjectorTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileManagerTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileServiceTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserLinksInjectorTest.java delete mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserProfileServiceTest.java rename wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/{UserNameValidatorTest.java => UserValidatorTest.java} (92%) create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/model/impl/DataObjectsTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/ProfileDaoTest.java create mode 100644 wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/UserDaoTest.java create mode 100644 wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalProfileTckRepository.java create mode 100644 wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalTckModule.java create mode 100644 wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalUserTckRepository.java create mode 100644 wsmaster/wsmaster-local/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule diff --git a/assembly/assembly-wsagent-war/src/main/java/org/eclipse/che/wsagent/server/WsAgentModule.java b/assembly/assembly-wsagent-war/src/main/java/org/eclipse/che/wsagent/server/WsAgentModule.java index b8e7c88848..5d7edff736 100644 --- a/assembly/assembly-wsagent-war/src/main/java/org/eclipse/che/wsagent/server/WsAgentModule.java +++ b/assembly/assembly-wsagent-war/src/main/java/org/eclipse/che/wsagent/server/WsAgentModule.java @@ -29,7 +29,7 @@ import org.eclipse.che.api.git.GitUserResolver; import org.eclipse.che.api.project.server.ProjectApiModule; import org.eclipse.che.api.ssh.server.HttpSshServiceClient; import org.eclipse.che.api.ssh.server.SshServiceClient; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.commons.lang.Pair; import org.eclipse.che.everrest.CheAsynchronousJobPool; import org.eclipse.che.api.git.LocalGitUserResolver; diff --git a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java index 072480ae51..acbe238904 100644 --- a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java +++ b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java @@ -15,6 +15,7 @@ import com.google.inject.multibindings.Multibinder; import com.google.inject.name.Names; import org.eclipse.che.api.machine.shared.Constants; +import org.eclipse.che.api.user.server.ProfileService; import org.eclipse.che.inject.DynaModule; import org.everrest.guice.ServiceBindingHelper; @@ -30,7 +31,8 @@ public class WsMasterModule extends AbstractModule { bind(org.eclipse.che.api.ssh.server.SshService.class); bind(org.eclipse.che.api.machine.server.recipe.RecipeService.class); bind(org.eclipse.che.api.user.server.UserService.class); - bind(org.eclipse.che.api.user.server.UserProfileService.class); + bind(org.eclipse.che.api.user.server.ProfileService.class); + bind(org.eclipse.che.api.user.server.PreferencesService.class); bind(org.eclipse.che.api.workspace.server.stack.StackLoader.class); bind(org.eclipse.che.api.workspace.server.stack.StackService.class); bind(org.eclipse.che.api.workspace.server.WorkspaceService.class); diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/Profile.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/Profile.java new file mode 100644 index 0000000000..d7a03e5a54 --- /dev/null +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/Profile.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.model.user; + +import java.util.Map; + +/** + * Defines the user's profile model. + * + *

User's profile describes an additional user information such as his + * job title or company name which is not related to application business logic. + * If it is necessary to manage business logic related attributes then + * user's preferences should be used instead. + * + * @author Yevhenii Voevodin + * @see User + */ +public interface Profile { + + /** + * Returns the identifier of the user {@link User#getId()} + * whom this profile belongs to. + */ + String getUserId(); + + /** + * Returns the user profile attributes (e.g. job title). + */ + Map getAttributes(); +} diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/User.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/User.java new file mode 100644 index 0000000000..1536ab541e --- /dev/null +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/user/User.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.model.user; + +import org.eclipse.che.commons.annotation.Nullable; + +import java.util.List; + +/** + * Defines the user model. + * + * @author Yevhenii Voevodin + */ +public interface User { + + /** + * Returns the identifier of the user (e.g. "user0x124567890"). + * The identifier value is unique and mandatory. + */ + String getId(); + + /** + * Returns the user's email (e.g. user@codenvy.com). + * The email is unique, mandatory and updatable. + */ + String getEmail(); + + /** + * Returns the user's name (e.g. name_example). + * The name is unique, mandatory and updatable. + */ + String getName(); + + /** + * Returns the list of the user's aliases, the aliases are the values + * which identify user in the system with third party ids (e.g. if user is registered + * within google oauth the aliases list may contain 'google:user_identifier' alias). + * + *

Note that user's {@link #getEmail() email} and {@link #getName() name} + * are not a part of the result, and returned list never contains those values. + * Also note that returned aliases are unique, so there are no two users + * who have the alias in common. + */ + List getAliases(); + + /** + * Returns the user's password. + * The returned value may be the password placeholder such as 'none' or + * even null, depends on the context. + */ + @Nullable + String getPassword(); +} diff --git a/core/che-core-api-model/src/main/resources/org/eclipse/che/api/core/model/Model.gwt.xml b/core/che-core-api-model/src/main/resources/org/eclipse/che/api/core/model/Model.gwt.xml index ba274e4e70..dab00cbbdc 100644 --- a/core/che-core-api-model/src/main/resources/org/eclipse/che/api/core/model/Model.gwt.xml +++ b/core/che-core-api-model/src/main/resources/org/eclipse/che/api/core/model/Model.gwt.xml @@ -18,5 +18,6 @@ + diff --git a/core/commons/che-core-commons-test/pom.xml b/core/commons/che-core-commons-test/pom.xml index 64474bce88..9339b40210 100644 --- a/core/commons/che-core-commons-test/pom.xml +++ b/core/commons/che-core-commons-test/pom.xml @@ -21,10 +21,32 @@ che-core-commons-test jar Che Core :: Commons :: Utilities for tests + + true + org.mockito mockito-core + + org.testng + testng + + + com.google.guava + guava + provided + + + com.google.inject + guice + provided + + + javax.inject + javax.inject + provided + diff --git a/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModule.java b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModule.java new file mode 100644 index 0000000000..26dcb8bbdb --- /dev/null +++ b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModule.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; + +import org.testng.IModuleFactory; +import org.testng.ITestContext; + +import java.util.ServiceLoader; + +/** + * Abstract class for those Guice {@link Module modules} which provide + * TCK tests components, which will be injected directly into the test class. + * + *

The {@link ServiceLoader} mechanism is used for loading such modules + * and for injecting them later. So each module which is TCK module must + * provide the implementations list(as described by {@code ServiceLoader} mechanism) + * in the file named org.eclipse.che.commons.test.tck.TckModule usually under + * test/resources/META-INF/services directory, then the {@link TckModuleFactory} + * will recognise and load it. + * + * @author Yevhenii Voevodin + * @see TckModuleFactory + */ +public abstract class TckModule extends AbstractModule { + + /** + * It is guaranteed that this field is always present and + * can be reused by implementation, the value is equal to the + * {@link IModuleFactory#createModule(ITestContext, Class)} first + * parameter and will be set by {@link TckModuleFactory} immediately after module + * implementation is loaded by {@link ServiceLoader}. + */ + private ITestContext testContext; + + /** Returns the {@link ITestContext context} of currently executing test suite. */ + protected ITestContext getTestContext() { + return testContext; + } + + /** + * Sets the context of currently executing test suite. + * This method designed to be used by {@link TckModuleFactory} for setting + * the context before installing modules. + */ + void setTestContext(ITestContext testContext) { + this.testContext = testContext; + } +} diff --git a/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModuleFactory.java b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModuleFactory.java new file mode 100644 index 0000000000..8802f12b25 --- /dev/null +++ b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/TckModuleFactory.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; + +import org.testng.IModuleFactory; +import org.testng.ITestContext; + +import java.util.Iterator; +import java.util.ServiceLoader; + +import static java.lang.String.format; + +/** + * The factory is designed to instantiate {@link TckModule tck modules} + * using {@link ServiceLoader} mechanism. The components + * provided by those modules will be injected into a test class + * whether it's necessary to do so. + * + *

The factory expects at least one implementation of {@code TckModule} + * to be configured, if it doesn't find any of the {@code TckModule} + * implementations then it will report an appropriate exception + * and TckTest will fail(as it requires components to be injected into it). + * If it finds more than one {@code TckModule} implementation it will + * use all of the found. + * + *

The usage example: + *

+ * package org.eclipse.mycomponent;
+ *
+ * @org.testng.annotations.Guice(moduleFactory = TckModuleFactory.class)
+ * // Good practice to define suiteName for TCK tests as it makes easier
+ * // to implement logic related to certain tests in ITestNGListener implementations
+ * @org.testng.annotations.Test(suiteName = "MySuite")
+ * class SubjectTest {
+ *
+ *     @javax.inject.Inject
+ *     private Component1 component1.
+ *     @javax.inject.Inject
+ *     private Component2 component2;
+ *
+ *     @org.testng.annotations.Test
+ *     public void test() {
+ *          // use components
+ *     }
+ * }
+ *
+ * class MyTckModule extends TckModule {
+ *     public void configure() {
+ *         bind(Component1.class).to(...);
+ *         bind(Component2.class).toInstance(new Component2(() -> testContext.getAttribute("server_url").toString()));
+ *     }
+ * }
+ *
+ * // Allows to add pre/post test actions like db server start/stop
+ * class DBServerListener implements ITestListener {
+ *      // ...
+ *      public void onStart(ITestContext context) {
+ *          String url = dbServer.start();
+ *          context.setAttribute("server_url", url)l
+ *      }
+ *
+ *      public void onFinish(ITestContext context) {
+ *          dbServer.stop();
+ *      }
+ *      // ...
+ * }
+ * 
+ * + *

Configuring: + *

+ * META-INF/services/org.eclipse.che.commons.test.tck.TckModule
+ * org.eclipse.mycomponent.MyTckModule
+ *
+ * META-INF/services/org.testng.ITestNGListener
+ * org.eclipse.mycomponent.DBServerListener
+ * 
+ * + * @author Yevhenii Voevodin + * @see org.testng.annotations.Guice + * @see IModuleFactory + */ +public class TckModuleFactory implements IModuleFactory { + + @Override + public Module createModule(ITestContext context, Class testClass) { + final Iterator moduleIterator = ServiceLoader.load(TckModule.class).iterator(); + if (!moduleIterator.hasNext()) { + throw new IllegalStateException(format("Couldn't find a TckModule configuration. " + + "You probably forgot to configure resources/META-INF/services/%s, or even " + + "provide an implementation of the TckModule which is required by the tck test class %s", + TckModule.class.getName(), + testClass.getName())); + } + return new CompoundModule(context, moduleIterator); + } + + private static class CompoundModule extends AbstractModule { + private final ITestContext testContext; + private final Iterator moduleIterator; + + private CompoundModule(ITestContext testContext, Iterator moduleIterator) { + this.testContext = testContext; + this.moduleIterator = moduleIterator; + } + + @Override + protected void configure() { + bind(ITestContext.class).toInstance(testContext); + while (moduleIterator.hasNext()) { + final TckModule module = moduleIterator.next(); + module.setTestContext(testContext); + install(module); + } + } + } +} diff --git a/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepository.java b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepository.java new file mode 100644 index 0000000000..bfb2cd8ea0 --- /dev/null +++ b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepository.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck.repository; + +import java.util.Collection; + +/** + * The interface which allows to create TCK tests for DAO interfaces + * by providing operations for creating/removing batch of elements. + * The interface is designed to work with entities, which means + * that entity marshaling and unmarshalling to/from db objects must be + * tested by implementation separately. + * + * @param + * the type of the object managed by the repository + * @author Yevhenii Voevodin + */ +public interface TckRepository { + + /** + * Creates all the given {@code entities} in the storage. + * + *

Note that implementation must fail if it is impossible to + * create any of the given entities. + * + * @param entities + * elements to create + * @throws TckRepositoryException + * when any error occurs during the storing + */ + void createAll(Collection entities) throws TckRepositoryException; + + /** + * Clears the storage. + * + *

Note that implementation must fail if it is impossible to + * remove all the entities. + * + * @throws TckRepositoryException + * when any error occurs during the clearing + */ + void removeAll() throws TckRepositoryException; +} diff --git a/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepositoryException.java b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepositoryException.java new file mode 100644 index 0000000000..33ce50511a --- /dev/null +++ b/core/commons/che-core-commons-test/src/main/java/org/eclipse/che/commons/test/tck/repository/TckRepositoryException.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck.repository; + +import java.util.Collection; + +/** + * Thrown when any error occurs during {@link TckRepository#createAll(Collection)} + * or {@link TckRepository#removeAll()} invocation. Usually wraps exceptions + * occurred during the storing/removing. + * + * @author Yevhenii Voevodin + */ +public class TckRepositoryException extends Exception { + + public TckRepositoryException(String message) { + super(message); + } + + public TckRepositoryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/DBServerListener.java b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/DBServerListener.java new file mode 100644 index 0000000000..fa4f585f74 --- /dev/null +++ b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/DBServerListener.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck; + +import org.testng.ITestContext; +import org.testng.ITestListener; +import org.testng.ITestResult; + +/** + * Listener representing fake db server url injection for testing "attributes sharing" + * using {@link ITestContext} test suite instance. + * + * @author Yevhenii Voevodin + */ +public class DBServerListener implements ITestListener { + + public static final String DB_SERVER_URL_ATTRIBUTE_NAME = "db_server_url"; + public static final String DB_SERVER_URL = "localhost:12345"; + + @Override + public void onStart(ITestContext context) { + context.setAttribute(DB_SERVER_URL_ATTRIBUTE_NAME, DB_SERVER_URL); + } + + @Override + public void onFinish(ITestContext context) {} + + @Override + public void onTestStart(ITestResult result) {} + + @Override + public void onTestSuccess(ITestResult result) {} + + @Override + public void onTestFailure(ITestResult result) {} + + @Override + public void onTestSkipped(ITestResult result) {} + + @Override + public void onTestFailedButWithinSuccessPercentage(ITestResult result) {} +} diff --git a/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TckComponentsTest.java b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TckComponentsTest.java new file mode 100644 index 0000000000..706ac50b0e --- /dev/null +++ b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TckComponentsTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck; + +import org.eclipse.che.commons.test.tck.repository.TckRepository; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * Tests for {@code org.eclipse.che.commons.test.tck.*} package. + * + * @author Yevhenii Voevodin + */ +@Guice(moduleFactory = TckModuleFactory.class) +public class TckComponentsTest { + + @Inject + private TckRepository tckRepository; + + @Inject + private DBUrlProvider dbUrlProvider; + + @Test + public void testComponentsAreInjected() { + assertNotNull(tckRepository, "TckRepository is not injected"); + assertNotNull(dbUrlProvider, "DBUrlProvider is not injected"); + assertEquals(dbUrlProvider.getUrl(), DBServerListener.DB_SERVER_URL, "Value is set to ITestContext"); + } + + public interface Entity {} + + public interface DBUrlProvider { + String getUrl(); + } +} diff --git a/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule1.java b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule1.java new file mode 100644 index 0000000000..f7e26295c4 --- /dev/null +++ b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule1.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.commons.test.tck; + +import com.google.inject.TypeLiteral; + +import org.eclipse.che.commons.test.tck.TckComponentsTest.Entity; +import org.eclipse.che.commons.test.tck.repository.TckRepository; +import org.eclipse.che.commons.test.tck.repository.TckRepositoryException; + +import java.util.Collection; + +/** + * @author Yevhenii Voevodin + */ +public class TestModule1 extends TckModule { + + @Override + public void configure() { + bind(new TypeLiteral>() {}).toInstance(new TckRepository() { + @Override + public void createAll(Collection entities) throws TckRepositoryException {} + + @Override + public void removeAll() throws TckRepositoryException {} + }); + } +} diff --git a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/model/Membership.java b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule2.java similarity index 53% rename from wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/model/Membership.java rename to core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule2.java index 4060dccd11..666867836c 100644 --- a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/model/Membership.java +++ b/core/commons/che-core-commons-test/src/test/java/org/eclipse/che/commons/test/tck/TestModule2.java @@ -8,25 +8,19 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.user.shared.model; +package org.eclipse.che.commons.test.tck; -import java.util.List; -import java.util.Map; +import org.eclipse.che.commons.test.tck.TckComponentsTest.DBUrlProvider; + +import static org.eclipse.che.commons.test.tck.DBServerListener.DB_SERVER_URL_ATTRIBUTE_NAME; /** - * @author gazarenkov + * @author Yevhenii Voevodin */ -public interface Membership { +public class TestModule2 extends TckModule { - String getScope(); - - List getRoles(); - - String getUserId(); - - String getUserName(); - - String getSubjectId(); - - Map getSubjectProperties(); + @Override + public void configure() { + bind(DBUrlProvider.class).toInstance(() -> getTestContext().getAttribute(DB_SERVER_URL_ATTRIBUTE_NAME).toString()); + } } diff --git a/core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule b/core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule new file mode 100644 index 0000000000..5c9171f461 --- /dev/null +++ b/core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule @@ -0,0 +1,2 @@ +org.eclipse.che.commons.test.tck.TestModule1 +org.eclipse.che.commons.test.tck.TestModule2 diff --git a/core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.testng.ITestNGListener b/core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.testng.ITestNGListener new file mode 100644 index 0000000000..3fea5967cb --- /dev/null +++ b/core/commons/che-core-commons-test/src/test/resources/META-INF/services/org.testng.ITestNGListener @@ -0,0 +1 @@ +org.eclipse.che.commons.test.tck.DBServerListener diff --git a/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java new file mode 100644 index 0000000000..7176bd7057 --- /dev/null +++ b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.user; + +import org.eclipse.che.api.promises.client.Promise; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * GWT client for preferences service; + * + * @author Yevhenii Voevodin + */ +public interface PreferencesServiceClient { + + /** + * Updates user's preferences using the merge strategy. + * + * @param prefsToUpdate + * preferences update + * @return a promise that resolves all the user's preferences, or rejects with an error + */ + Promise> updatePreferences(@NotNull Map prefsToUpdate); + + /** + * Gets user preferences. + * + * @return a promise that resolves preferences, or rejects with an error + */ + Promise> getPreferences(); +} diff --git a/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java new file mode 100644 index 0000000000..30c44c3c02 --- /dev/null +++ b/core/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.user; + +import com.google.inject.Inject; + +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.json.JsonHelper; +import org.eclipse.che.ide.rest.AsyncRequestFactory; +import org.eclipse.che.ide.rest.RestContext; +import org.eclipse.che.ide.rest.StringMapUnmarshaller; +import org.eclipse.che.ide.ui.loaders.request.LoaderFactory; + +import java.util.Map; + +import static org.eclipse.che.ide.MimeType.APPLICATION_JSON; +import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; +import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE; + +/** + * Default implementation of {@link PreferencesServiceClient}. + * + * @author Yevhenii Voevodin + */ +public class PreferencesServiceClientImpl implements PreferencesServiceClient { + + private final String PREFERENCES_PATH; + private final LoaderFactory loaderFactory; + private final AsyncRequestFactory asyncRequestFactory; + + @Inject + protected PreferencesServiceClientImpl(@RestContext String restContext, + LoaderFactory loaderFactory, + AsyncRequestFactory asyncRequestFactory) { + this.loaderFactory = loaderFactory; + this.asyncRequestFactory = asyncRequestFactory; + PREFERENCES_PATH = restContext + "/preferences"; + } + + @Override + public Promise> getPreferences() { + return asyncRequestFactory.createGetRequest(PREFERENCES_PATH) + .header(ACCEPT, APPLICATION_JSON) + .header(CONTENT_TYPE, APPLICATION_JSON) + .loader(loaderFactory.newLoader("Getting user's preferences...")) + .send(new StringMapUnmarshaller()); + } + + @Override + public Promise> updatePreferences(Map update) { + final String data = JsonHelper.toJson(update); + return asyncRequestFactory.createPutRequest(PREFERENCES_PATH, null) + .header(ACCEPT, APPLICATION_JSON) + .header(CONTENT_TYPE, APPLICATION_JSON) + .data(data) + .loader(loaderFactory.newLoader("Updating user's preferences...")) + .send(new StringMapUnmarshaller()); + } +} diff --git a/dashboard/src/app/administration/docker-registry/docker-registry-list/add-registry/add-registry.controller.js b/dashboard/src/app/administration/docker-registry/docker-registry-list/add-registry/add-registry.controller.js index dbc4892703..fd5debb495 100644 --- a/dashboard/src/app/administration/docker-registry/docker-registry-list/add-registry/add-registry.controller.js +++ b/dashboard/src/app/administration/docker-registry/docker-registry-list/add-registry/add-registry.controller.js @@ -20,9 +20,9 @@ export class AddRegistryController { * Default constructor. * @ngInject for Dependency injection */ - constructor($mdDialog, cheProfile, cheNotification) { + constructor($mdDialog, chePreferences, cheNotification) { this.$mdDialog = $mdDialog; - this.cheProfile = cheProfile; + this.chePreferences = chePreferences; this.cheNotification = cheNotification; } @@ -41,7 +41,7 @@ export class AddRegistryController { return; } - let promise = this.cheProfile.addRegistry(this.registryUrl, this.registryUserName, this.registryUserPassword); + let promise = this.chePreferences.addRegistry(this.registryUrl, this.registryUserName, this.registryUserPassword); promise.then(() => { this.$mdDialog.hide(); diff --git a/dashboard/src/app/administration/docker-registry/docker-registry-list/docker-registry-list.controller.js b/dashboard/src/app/administration/docker-registry/docker-registry-list/docker-registry-list.controller.js index 14e598639f..f2ca629382 100644 --- a/dashboard/src/app/administration/docker-registry/docker-registry-list/docker-registry-list.controller.js +++ b/dashboard/src/app/administration/docker-registry/docker-registry-list/docker-registry-list.controller.js @@ -22,16 +22,16 @@ export class DockerRegistryListController { * Default constructor that is using resource * @ngInject for Dependency injection */ - constructor($mdDialog, $document, cheProfile, cheNotification) { + constructor($mdDialog, $document, chePreferences, cheNotification) { this.$mdDialog = $mdDialog; this.$document = $document; - this.cheProfile = cheProfile; + this.chePreferences = chePreferences; this.cheNotification = cheNotification; - this.registries = cheProfile.getRegistries(); + this.registries = chePreferences.getRegistries(); this.isLoading = true; - let promise = cheProfile.fetchPreferences(); + let promise = chePreferences.fetchPreferences(); promise.then(() => { this.isLoading = false; }, (error) => { @@ -91,7 +91,7 @@ export class DockerRegistryListController { .targetEvent(event); this.$mdDialog.show(confirm).then(() => { this.isLoading = true; - let promise = this.cheProfile.removeRegistry(registry.url); + let promise = this.chePreferences.removeRegistry(registry.url); promise.then(() => { this.isLoading = false; }, (error) => { diff --git a/dashboard/src/app/index.module.js b/dashboard/src/app/index.module.js index d94e145b67..a8fe0e9cfb 100644 --- a/dashboard/src/app/index.module.js +++ b/dashboard/src/app/index.module.js @@ -40,13 +40,13 @@ let initModule = angular.module('userDashboard', ['ngAnimate', 'ngCookies', 'ngT initModule.config(['$routeProvider', ($routeProvider) => { $routeProvider.accessWhen = (path, route) => { route.resolve || (route.resolve = {}); - route.resolve.app = ['cheBranding', '$q', 'cheProfile', (cheBranding, $q, cheProfile) => { + route.resolve.app = ['cheBranding', '$q', 'chePreferences', (cheBranding, $q, chePreferences) => { var deferred = $q.defer(); - let profilePreferences = cheProfile.getPreferences(); - if (profilePreferences && profilePreferences.$resolved) { + let preferences = chePreferences.getPreferences(); + if (preferences && preferences.$resolved) { deferred.resolve(); } else { - profilePreferences.$promise.then(() => { + preferences.$promise.then(() => { deferred.resolve(); }, (error) => { deferred.reject(error); @@ -61,13 +61,13 @@ initModule.config(['$routeProvider', ($routeProvider) => { $routeProvider.accessOtherWise = (route) => { route.resolve || (route.resolve = {}); - route.resolve.app = ['$q', 'cheProfile', ($q, cheProfile) => { + route.resolve.app = ['$q', 'chePreferences', ($q, chePreferences) => { var deferred = $q.defer(); - let profilePreferences = cheProfile.getPreferences(); - if (profilePreferences && profilePreferences.$resolved) { + let preferences = chePreferences.getPreferences(); + if (preferences && preferences.$resolved) { deferred.resolve(); } else { - profilePreferences.$promise.then(() => { + preferences.$promise.then(() => { deferred.resolve(); }, (error) => { deferred.reject(error); diff --git a/dashboard/src/app/navbar/navbar.controller.js b/dashboard/src/app/navbar/navbar.controller.js index a60aa4a4a6..e6a89f40de 100644 --- a/dashboard/src/app/navbar/navbar.controller.js +++ b/dashboard/src/app/navbar/navbar.controller.js @@ -26,11 +26,11 @@ export class CheNavBarCtrl { this.links = [{href: '#/create-workspace', name: 'New Workspace'}]; this.profile = cheAPI.getProfile().getProfile(); - if (this.profile.attributes) { - this.email = this.profile.attributes.email; + if (this.profile.email) { + this.email = this.profile.email; } else { this.profile.$promise.then(() => { - this.email = this.profile.attributes.email ? this.profile.attributes.email : 'N/A '; + this.email = this.profile.email ? this.profile.email : 'N/A '; }, () => { this.email = 'N/A '; }); diff --git a/dashboard/src/app/projects/list-projects/list-projects.controller.js b/dashboard/src/app/projects/list-projects/list-projects.controller.js index 96defdd984..d9e1ddde0e 100644 --- a/dashboard/src/app/projects/list-projects/list-projects.controller.js +++ b/dashboard/src/app/projects/list-projects/list-projects.controller.js @@ -49,9 +49,9 @@ export class ListProjectsCtrl { } }); - let profilePreferences = cheAPI.getProfile().getPreferences(); + let preferences = cheAPI.getPreferences().getPreferences(); - this.profileCreationDate = profilePreferences['che:created']; + this.profileCreationDate = preferences['che:created']; this.menuOptions = [ { diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.js b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.js index 47419670ef..228666a2c8 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.js +++ b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.js @@ -28,9 +28,9 @@ export class WorkspaceDetailsProjectsCtrl { this.namespace = $route.current.params.namespace; this.workspaceName = $route.current.params.workspaceName; - let profilePreferences = cheAPI.getProfile().getPreferences(); + let preferences = cheAPI.getPreferences().getPreferences(); - this.profileCreationDate = profilePreferences['che:created']; + this.profileCreationDate = preferences['che:created']; if (!this.cheWorkspace.getWorkspacesById().get(this.workspaceId)) { let promise = this.cheWorkspace.fetchWorkspaceDetails(this.workspaceId); diff --git a/dashboard/src/components/api/builder/che-profile-builder.js b/dashboard/src/components/api/builder/che-profile-builder.js index dc07d28d84..41556b6219 100644 --- a/dashboard/src/components/api/builder/che-profile-builder.js +++ b/dashboard/src/components/api/builder/che-profile-builder.js @@ -32,7 +32,8 @@ export class CheProfileBuilder { * @returns {CheProfileBuilder} */ withEmail(email) { - return this.withAttribute('email', email); + this.profile.email = email; + return this; } /** diff --git a/dashboard/src/components/api/che-api-config.js b/dashboard/src/components/api/che-api-config.js index 65d212c717..974f3bb860 100644 --- a/dashboard/src/components/api/che-api-config.js +++ b/dashboard/src/components/api/che-api-config.js @@ -18,6 +18,7 @@ import {CheRecipeTemplate} from './che-recipe-template.factory'; import {CheStack} from './che-stack.factory'; import {CheWebsocket} from './che-websocket.factory'; import {CheProfile} from './che-profile.factory'; +import {ChePreferences} from './che-preferences.factory'; import {CheService} from './che-service.factory'; import {CheSvn} from './che-svn.factory'; import {CheHttpBackend} from './test/che-http-backend'; @@ -35,6 +36,7 @@ export class ApiConfig { register.factory('cheWorkspace', CheWorkspace); register.factory('cheProjectTemplate', CheProjectTemplate); register.factory('cheProfile', CheProfile); + register.factory('chePreferences', ChePreferences); register.factory('cheWebsocket', CheWebsocket); register.factory('cheRecipe', CheRecipe); register.factory('cheRecipeTemplate', CheRecipeTemplate); diff --git a/dashboard/src/components/api/che-api.factory.js b/dashboard/src/components/api/che-api.factory.js index bdf16639f5..c813d63536 100644 --- a/dashboard/src/components/api/che-api.factory.js +++ b/dashboard/src/components/api/che-api.factory.js @@ -22,10 +22,11 @@ export class CheAPI { * Default constructor that is using resource * @ngInject for Dependency injection */ - constructor(cheWorkspace, cheProfile, cheProjectTemplate, cheWebsocket, cheSvn, cheService, + constructor(cheWorkspace, cheProfile, chePreferences, cheProjectTemplate, cheWebsocket, cheSvn, cheService, cheAdminPlugins, cheAdminService, cheRecipe, cheRecipeTemplate, cheStack, cheOAuthProvider) { this.cheWorkspace = cheWorkspace; this.cheProfile = cheProfile; + this.chePreferences = chePreferences; this.cheProjectTemplate = cheProjectTemplate; this.cheWebsocket = cheWebsocket; this.cheSvn = cheSvn; @@ -63,6 +64,14 @@ export class CheAPI { return this.cheProfile; } + /** + * The Che Preferences API + * @returns {ChePreferences|*} + */ + getPreferences() { + return this.chePreferences; + } + /** * The Che Project Template API * @returns {CheProjectTemplate|*} diff --git a/dashboard/src/components/api/che-preferences.factory.js b/dashboard/src/components/api/che-preferences.factory.js new file mode 100644 index 0000000000..7196ce9130 --- /dev/null +++ b/dashboard/src/components/api/che-preferences.factory.js @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2015-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + */ +'use strict'; + +/** + * This class is handling the preferences API retrieval + * @author Yevhenii Voevodin + */ +export class ChePreferences { + + /** + * Default constructor that is using resource + * @ngInject for Dependency injection + */ + constructor($resource, $http, $window) { + this.$window = $window; + + // keep resource + this.$resource = $resource; + + // http is used for sending data with DELETE method (angular is not sending any data by default with DELETE) + this.$http = $http; + + // remote call + this.remotePreferencesAPI = this.$resource('/api/preferences', {}, { + getPreferences: {method: 'GET', url: '/api/preferences'}, + updatePreferences: {method: 'POST', url: '/api/preferences'} + }); + + // fetch the preferences when we're initialized + this.fetchPreferences(); + + + //registry array + this.registries = []; + } + + /** + * Gets the preferences + * @return preferences + */ + getPreferences() { + return this.preferences; + } + + /** + * Update the preferences + * @param properties + */ + updatePreferences(properties) { + angular.extend(this.preferences, properties); + return this.preferences.$save(); + } + + /** + * Remove preferences properties + * @param properties (list of keys) + */ + removePreferences(properties) { + // delete method doesn't send body when it is defined in $resources + // that's why direct $http call is used. + this.$http({ + url: '/api/preferences', + method: 'DELETE', + headers: {'Content-Type': 'application/json;charset=utf-8'}, + data: properties}).then(resp => { + this.fetchPreferences(); + }) + } + + /** + * Gets the preferences data + */ + fetchPreferences() { + let preferences = this.remotePreferencesAPI.getPreferences(); + // if we don't yet have data + if (!this.preferences) { + // set preferences for using promise in controllers during first request + this.preferences = preferences; + } + + let preferencesPromise = this.preferences.$promise; + + preferencesPromise.then((preferences) => { + // update preferences data if we have new value + this.preferences = preferences; + }); + + return preferencesPromise; + } + + + /** + * Gets the registries + * @return registries + */ + getRegistries() { + return this.registries; + } + + /** + * Add a registry + * @param registryUrl + * @param userName + * @param userEmail + * @param userPassword + * @returns {*} the promise + */ + addRegistry(registryUrl, userName, userPassword) { + let credentials = {}; + credentials[registryUrl] = { + username: userName, + password: userPassword + }; + + if (this.preferences.dockerCredentials) { + let remoteCredentialsJson = this.$window.atob(this.preferences.dockerCredentials); + let remoteCredentials = angular.fromJson(remoteCredentialsJson); + if (remoteCredentials[registryUrl]) { + delete remoteCredentials[registryUrl]; + } + angular.extend(credentials, remoteCredentials); + } + + let credentialsBase64 = this.$window.btoa(angular.toJson(credentials)); + let preferences = {dockerCredentials: credentialsBase64}; + let promise = this.updatePreferences(preferences); + + promise.then((preferences) => { + this.preferences = preferences; + this._updateRegistries(preferences); + }); + + return promise; + } + + + /** + * Remove the registry by its URL + * @param registryUrl + * @returns {*} the promise + */ + removeRegistry(registryUrl) { + let credentialsJson = this.$window.atob(this.preferences.dockerCredentials); + let credentials = angular.fromJson(credentialsJson); + + delete credentials[registryUrl]; + + let credentialsBase64 = this.$window.btoa(angular.toJson(credentials)); + let preferences = {dockerCredentials: credentialsBase64}; + + let promise = this.updatePreferences(preferences); + + promise.then((preferences) => { + this.preferences = preferences; + this._updateRegistries(preferences); + }); + + return promise; + } + + /** + * Update registry array from preferences + * @param preferences + */ + _updateRegistries(preferences) { + this.registries.length = 0; + if (preferences.dockerCredentials) { + let credentialsJson = this.$window.atob(preferences.dockerCredentials); + let credentials = angular.fromJson(credentialsJson); + + for (var key in credentials) { + let credential = { + url: key, + username: credentials[key].username, + password: credentials[key].password + }; + this.registries.push(credential); + } + } + } +} diff --git a/dashboard/src/components/api/che-preferences.spec.js b/dashboard/src/components/api/che-preferences.spec.js new file mode 100644 index 0000000000..c578334ecd --- /dev/null +++ b/dashboard/src/components/api/che-preferences.spec.js @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + */ +'use strict'; + +/** + * Test of the ChePreferences + */ +describe('ChePreferences', function () { + + /** + * Preferences Factory for the test + */ + var factory; + + /** + * API builder. + */ + var apiBuilder; + + /** + * Backend for handling http operations + */ + var httpBackend; + + /** + * che backend + */ + var cheBackend; + + /** + * setup module + */ + beforeEach(angular.mock.module('userDashboard')); + + /** + * Inject factory and http backend + */ + beforeEach(inject(function (chePreferences, cheAPIBuilder, cheHttpBackend) { + factory = chePreferences; + apiBuilder = cheAPIBuilder; + cheBackend = cheHttpBackend; + httpBackend = cheHttpBackend.getHttpBackend(); + })); + + /** + * Check assertion after the test + */ + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + httpBackend.verifyNoOutstandingRequest(); + }); + + /** + * Check that we're able to fetch preferences + */ + it('Fetch preferences', function () { + // providing request + // add default preferences on Http backend + let defaultPreferences = { pref1 : "value1", pref2 : "value2"}; + cheBackend.addDefaultPreferences(defaultPreferences); + + // setup backend + cheBackend.setup(); + + // fetch preferences + factory.fetchPreferences(); + + // expecting GETs + httpBackend.expectGET('/api/preferences'); + + // flush command + httpBackend.flush(); + + // now, check preferences + let preferences = factory.getPreferences(); + + expect(preferences["pref1"]).toEqual("value1"); + expect(preferences["pref2"]).toEqual("value2"); + } + ); + + /** + * Check that we're able to update preferences + */ + it('Update preferences', function () { + let defaultPreferences = { pref1 : "value1" }; + + // setup backend + cheBackend.setup(); + cheBackend.setPreferences(defaultPreferences); + + // fetch preferences + factory.updatePreferences(defaultPreferences); + + // expecting POST + httpBackend.expectPOST('/api/preferences'); + + // flush command + httpBackend.flush(); + + // now, check preferences + let preferences = factory.getPreferences(); + + expect(preferences["pref1"]).toEqual("value1"); + } + ); + + /** + * Check that we're able to delete preferences + */ + it('Remove preferences', function () { + + let defaultPreferences = { pref1 : "value1", pref2 : "value2" }; + cheBackend.addDefaultPreferences(defaultPreferences); + + // setup backend + cheBackend.setup(); + + // r + factory.removePreferences(["pref1"]); + + // expecting POST + httpBackend.expectDELETE('/api/preferences'); + + // flush command + httpBackend.flush(); + } + ); +}); diff --git a/dashboard/src/components/api/che-profile.factory.js b/dashboard/src/components/api/che-profile.factory.js index ea81b3f362..8113c9d57a 100644 --- a/dashboard/src/components/api/che-profile.factory.js +++ b/dashboard/src/components/api/che-profile.factory.js @@ -21,9 +21,8 @@ export class CheProfile { * Default constructor that is using resource * @ngInject for Dependency injection */ - constructor($resource, $http, $window) { + constructor($resource, $http) { this.$resource = $resource; - this.$window = $window; // http is used for sending data with DELETE method (angular is not sending any data by default with DELETE) this.$http = $http; @@ -31,22 +30,13 @@ export class CheProfile { // remote call this.remoteProfileAPI = this.$resource('/api/profile', {}, { getById: {method: 'GET', url: '/api/profile/:userId'}, - setAttributes: {method: 'POST', url: '/api/profile'} + setAttributes: {method: 'PUT', url: '/api/profile/attributes'} }); - // remote call for preferences - this.remoteProfilePreferencesAPI = this.$resource('/api/profile/prefs'); - this.profileIdMap = new Map(); // fetch the profile when we're initialized this.fetchProfile(); - - // fetch the profilePreferences when we're initialized - this.fetchPreferences(); - - //registry array - this.registries = []; } @@ -58,129 +48,6 @@ export class CheProfile { return this.profile; } - /** - * Gets the preferences - * @return preferences - */ - getPreferences() { - return this.profilePreferences; - } - - - /** - * Update the preferences - * @param properties - * @returns {*} the promise - */ - updatePreferences(properties) { - angular.extend(this.profilePreferences, properties); - return this.profilePreferences.$save(); - } - - /** - * Gets the registries - * @return registries - */ - getRegistries() { - return this.registries; - } - - /** - * Add a registry - * @param registryUrl - * @param userName - * @param userEmail - * @param userPassword - * @returns {*} the promise - */ - addRegistry(registryUrl, userName, userPassword) { - let credentials = {}; - credentials[registryUrl] = { - username: userName, - password: userPassword - }; - - if (this.profilePreferences.dockerCredentials) { - let remoteCredentialsJson = this.$window.atob(this.profilePreferences.dockerCredentials); - let remoteCredentials = angular.fromJson(remoteCredentialsJson); - if (remoteCredentials[registryUrl]) { - delete remoteCredentials[registryUrl]; - } - angular.extend(credentials, remoteCredentials); - } - - let credentialsBase64 = this.$window.btoa(angular.toJson(credentials)); - let preferences = {dockerCredentials: credentialsBase64}; - let promise = this.updatePreferences(preferences); - - promise.then((profilePreferences) => { - this.profilePreferences = profilePreferences; - this._updateRegistries(profilePreferences); - }); - - return promise; - } - - - /** - * Remove the registry by its URL - * @param registryUrl - * @returns {*} the promise - */ - removeRegistry(registryUrl) { - let credentialsJson = this.$window.atob(this.profilePreferences.dockerCredentials); - let credentials = angular.fromJson(credentialsJson); - - delete credentials[registryUrl]; - - let credentialsBase64 = this.$window.btoa(angular.toJson(credentials)); - let preferences = {dockerCredentials: credentialsBase64}; - - let promise = this.updatePreferences(preferences); - - promise.then((profilePreferences) => { - this.profilePreferences = profilePreferences; - this._updateRegistries(profilePreferences); - }); - - return promise; - } - - /** - * Update registry array from profile preferences - * @param profilePreferences - */ - _updateRegistries(profilePreferences) { - this.registries.length = 0; - if (profilePreferences.dockerCredentials) { - let credentialsJson = this.$window.atob(profilePreferences.dockerCredentials); - let credentials = angular.fromJson(credentialsJson); - - for (var key in credentials) { - let credential = { - url: key, - username: credentials[key].username, - password: credentials[key].password - }; - this.registries.push(credential); - } - } - } - - /** - * Remove preferences properties - * @param properties (list of keys) - */ - removePreferences(properties) { - this.$http({ - url: '/api/profile/prefs', - method: 'DELETE', - headers: {'Content-Type': 'application/json;charset=utf-8'}, - data: properties - }); - this.fetchPreferences(); - } - /** * Gets the full name if it possible * @returns {string} full name @@ -223,28 +90,6 @@ export class CheProfile { return profilePromise; } - /** - * Gets the preferences data - */ - fetchPreferences() { - let profilePreferences = this.remoteProfilePreferencesAPI.get(); - // if we don't yet have data - if (!this.profilePreferences) { - // set profilePreferences for using promise in controllers during first request - this.profilePreferences = profilePreferences; - } - - let profilePrefsPromise = this.profilePreferences.$promise; - - profilePrefsPromise.then((profilePreferences) => { - // update profilePreferences data if we have new value - this.profilePreferences = profilePreferences; - this._updateRegistries(profilePreferences); - }); - - return profilePrefsPromise; - } - /** * Set the profile attributes data * @param attributes diff --git a/dashboard/src/components/api/che-profile.spec.js b/dashboard/src/components/api/che-profile.spec.js index 9eee677255..7f185bdb80 100644 --- a/dashboard/src/components/api/che-profile.spec.js +++ b/dashboard/src/components/api/che-profile.spec.js @@ -82,8 +82,6 @@ describe('CheProfile', function () { // expecting GETs httpBackend.expectGET('/api/profile'); - httpBackend.expectGET('/api/profile/prefs'); - // flush command httpBackend.flush(); @@ -92,7 +90,7 @@ describe('CheProfile', function () { // check id, email, firstName and lastName in profile attributes expect(profile.id).toEqual(profileId); - expect(profile.attributes.email).toEqual(email); + expect(profile.email).toEqual(email); expect(profile.attributes.firstName).toEqual(firstName); expect(profile.attributes.lastName).toEqual(lastName); } @@ -112,8 +110,8 @@ describe('CheProfile', function () { // fetch profile factory.setAttributes(testAttributes); - // expecting a POST - httpBackend.expectPOST('/api/profile'); + // expecting a PUT + httpBackend.expectPUT('/api/profile/attributes'); // flush command httpBackend.flush(); diff --git a/dashboard/src/components/api/test/che-http-backend.js b/dashboard/src/components/api/test/che-http-backend.js index 66b211c310..c5a4779dce 100644 --- a/dashboard/src/components/api/test/che-http-backend.js +++ b/dashboard/src/components/api/test/che-http-backend.js @@ -61,12 +61,15 @@ export class CheHttpBackend { //profiles this.httpBackend.when('GET', '/api/profile').respond(this.defaultProfile); - this.httpBackend.when('GET', '/api/profile/prefs').respond(this.defaultProfilePrefs); var profileKeys = this.profilesMap.keys(); for (let key of profileKeys) { this.httpBackend.when('GET', '/api/profile/' + key).respond(this.profilesMap.get(key)); } + //preferences + this.httpBackend.when('GET', '/api/preferences').respond(this.defaultPreferences); + this.httpBackend.when('DELETE', '/api/preferences').respond(); + /// project details var projectDetailsKeys = this.projectDetailsMap.keys(); for (let projectKey of projectDetailsKeys) { @@ -177,6 +180,23 @@ export class CheHttpBackend { this.defaultProfile = profile; } + /** + * Add the given preferences + * @param preferences + */ + addDefaultPreferences(preferences) { + this.defaultPreferences = preferences; + } + + /** + * Add the given preferences + * @param preferences + */ + setPreferences(preferences) { + this.httpBackend.when('POST', '/api/preferences').respond(preferences); + this.defaultPreferences = preferences; + } + /** * Add the given profile * @param profile @@ -191,7 +211,7 @@ export class CheHttpBackend { * @param attributes */ setAttributes(attributes) { - this.httpBackend.when('POST', '/api/profile').respond(attributes); + this.httpBackend.when('PUT', '/api/profile/attributes').respond(attributes); this.defaultProfile.attributes = attributes; } @@ -312,4 +332,3 @@ export class CheHttpBackend { } } - diff --git a/dashboard/src/components/widget/learn-more/che-learn-more.controller.js b/dashboard/src/components/widget/learn-more/che-learn-more.controller.js index 403ca1da4a..e33524d506 100644 --- a/dashboard/src/components/widget/learn-more/che-learn-more.controller.js +++ b/dashboard/src/components/widget/learn-more/che-learn-more.controller.js @@ -23,18 +23,18 @@ export class CheLearnMoreCtrl { * Default constructor that is using resource * @ngInject for Dependency injection */ - constructor($scope, $element, $attrs, $compile, cheProfile) { + constructor($scope, $element, $attrs, $compile, chePreferences) { this.items = []; this.WIDGET_PREFERENCES_PREFIX = 'learn-widget-'; - this.cheProfile = cheProfile; + this.chePreferences = chePreferences; // current index is first one this.currentIndex = 0; - let preferences = this.cheProfile.getPreferences(); + let preferences = this.chePreferences.getPreferences(); let promise = preferences.$promise; promise.then(() => { @@ -87,7 +87,7 @@ export class CheLearnMoreCtrl { let checkKey = this.WIDGET_PREFERENCES_PREFIX + key; var properties = {}; properties[checkKey] = value; - this.cheProfile.updatePreferences(properties); + this.chePreferences.updatePreferences(properties); // also update icon state this.stateIcons[key] = value; @@ -104,7 +104,7 @@ export class CheLearnMoreCtrl { // check if key is stored in preferences // if there, if (key) { - let preferences = this.cheProfile.getPreferences(); + let preferences = this.chePreferences.getPreferences(); let promise = preferences.$promise; promise.then(() => { diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/app/CurrentUser.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/app/CurrentUser.java index 2f30fe534c..2dd2e95f57 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/app/CurrentUser.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/app/CurrentUser.java @@ -10,7 +10,8 @@ *******************************************************************************/ package org.eclipse.che.ide.api.app; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; +import org.eclipse.che.api.user.shared.dto.ProfileDto; + import com.google.inject.Singleton; import java.util.Map; @@ -22,17 +23,17 @@ import java.util.Map; @Singleton public class CurrentUser { - private ProfileDescriptor profileDescriptor; + private ProfileDto profileDescriptor; private Map preferences; public CurrentUser() { } - public CurrentUser(ProfileDescriptor profileDescriptor) { + public CurrentUser(ProfileDto profileDescriptor) { this(profileDescriptor, null); } - public CurrentUser(ProfileDescriptor profileDescriptor, Map preferences) { + public CurrentUser(ProfileDto profileDescriptor, Map preferences) { this.profileDescriptor = profileDescriptor; this.preferences = preferences; } @@ -42,11 +43,11 @@ public class CurrentUser { * * @return */ - public ProfileDescriptor getProfile() { + public ProfileDto getProfile() { return profileDescriptor; } - public void setProfile(ProfileDescriptor profileDescriptor) { + public void setProfile(ProfileDto profileDescriptor) { this.profileDescriptor = profileDescriptor; } diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java new file mode 100644 index 0000000000..7176bd7057 --- /dev/null +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClient.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.user; + +import org.eclipse.che.api.promises.client.Promise; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * GWT client for preferences service; + * + * @author Yevhenii Voevodin + */ +public interface PreferencesServiceClient { + + /** + * Updates user's preferences using the merge strategy. + * + * @param prefsToUpdate + * preferences update + * @return a promise that resolves all the user's preferences, or rejects with an error + */ + Promise> updatePreferences(@NotNull Map prefsToUpdate); + + /** + * Gets user preferences. + * + * @return a promise that resolves preferences, or rejects with an error + */ + Promise> getPreferences(); +} diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java new file mode 100644 index 0000000000..30c44c3c02 --- /dev/null +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/PreferencesServiceClientImpl.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.user; + +import com.google.inject.Inject; + +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.json.JsonHelper; +import org.eclipse.che.ide.rest.AsyncRequestFactory; +import org.eclipse.che.ide.rest.RestContext; +import org.eclipse.che.ide.rest.StringMapUnmarshaller; +import org.eclipse.che.ide.ui.loaders.request.LoaderFactory; + +import java.util.Map; + +import static org.eclipse.che.ide.MimeType.APPLICATION_JSON; +import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; +import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE; + +/** + * Default implementation of {@link PreferencesServiceClient}. + * + * @author Yevhenii Voevodin + */ +public class PreferencesServiceClientImpl implements PreferencesServiceClient { + + private final String PREFERENCES_PATH; + private final LoaderFactory loaderFactory; + private final AsyncRequestFactory asyncRequestFactory; + + @Inject + protected PreferencesServiceClientImpl(@RestContext String restContext, + LoaderFactory loaderFactory, + AsyncRequestFactory asyncRequestFactory) { + this.loaderFactory = loaderFactory; + this.asyncRequestFactory = asyncRequestFactory; + PREFERENCES_PATH = restContext + "/preferences"; + } + + @Override + public Promise> getPreferences() { + return asyncRequestFactory.createGetRequest(PREFERENCES_PATH) + .header(ACCEPT, APPLICATION_JSON) + .header(CONTENT_TYPE, APPLICATION_JSON) + .loader(loaderFactory.newLoader("Getting user's preferences...")) + .send(new StringMapUnmarshaller()); + } + + @Override + public Promise> updatePreferences(Map update) { + final String data = JsonHelper.toJson(update); + return asyncRequestFactory.createPutRequest(PREFERENCES_PATH, null) + .header(ACCEPT, APPLICATION_JSON) + .header(CONTENT_TYPE, APPLICATION_JSON) + .data(data) + .loader(loaderFactory.newLoader("Updating user's preferences...")) + .send(new StringMapUnmarshaller()); + } +} diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClient.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClient.java index f1bbad020b..8a331b38c2 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClient.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClient.java @@ -10,8 +10,7 @@ *******************************************************************************/ package org.eclipse.che.ide.api.user; -import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; +import org.eclipse.che.api.user.shared.dto.ProfileDto; import org.eclipse.che.ide.rest.AsyncRequestCallback; import javax.validation.constraints.NotNull; @@ -29,7 +28,7 @@ public interface UserProfileServiceClient { * * @param callback */ - void getCurrentProfile(AsyncRequestCallback callback); + void getCurrentProfile(AsyncRequestCallback callback); /** * Update current user's profile. @@ -38,7 +37,7 @@ public interface UserProfileServiceClient { * attributes to update * @param callback */ - void updateCurrentProfile(@NotNull Map updates, AsyncRequestCallback callback); + void updateCurrentProfile(@NotNull Map updates, AsyncRequestCallback callback); /** * Get profile by id. @@ -47,26 +46,8 @@ public interface UserProfileServiceClient { * profile's id * @param callback */ - void getProfileById(@NotNull String id, AsyncRequestCallback callback); + void getProfileById(@NotNull String id, AsyncRequestCallback callback); - /** - * Get user preferences - * - * @param callback - * which contains some action with user preferences - * @deprecated use {@link #getPreferences()} - */ - @Deprecated - void getPreferences(AsyncRequestCallback> callback); - - /** - * Get user preferences - * - * @return the promise which either uses preferences for some actions or rejects with an error - */ - Promise> getPreferences(); - - /** /** * Update profile. @@ -77,25 +58,5 @@ public interface UserProfileServiceClient { * attributes to update * @param callback */ - void updateProfile(@NotNull String id, Map updates, AsyncRequestCallback callback); - - /** - * Update preferences. - * - * @param prefsToUpdate - * preferences to update - * @param callback - * which contains some action with user preferences - * @deprecated use {@link #updatePreferences(Map)} - */ - @Deprecated - void updatePreferences(@NotNull Map prefsToUpdate, AsyncRequestCallback> callback); - - /** - * Update preferences. - * - * @param prefsToUpdate - * @return promise which either uses preferences for some actions or rejects with an error - */ - Promise> updatePreferences(@NotNull Map prefsToUpdate); + void updateProfile(@NotNull String id, Map updates, AsyncRequestCallback callback); } diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClientImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClientImpl.java index c6d7b6d19b..264f9242b7 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClientImpl.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserProfileServiceClientImpl.java @@ -13,7 +13,7 @@ package org.eclipse.che.ide.api.user; import com.google.inject.Inject; import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; +import org.eclipse.che.api.user.shared.dto.ProfileDto; import org.eclipse.che.ide.json.JsonHelper; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.AsyncRequestFactory; @@ -35,7 +35,6 @@ import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE; */ public class UserProfileServiceClientImpl implements UserProfileServiceClient { private final String PROFILE; - private final String PREFS; private final LoaderFactory loaderFactory; private final AsyncRequestFactory asyncRequestFactory; @@ -46,12 +45,11 @@ public class UserProfileServiceClientImpl implements UserProfileServiceClient { this.loaderFactory = loaderFactory; this.asyncRequestFactory = asyncRequestFactory; PROFILE = restContext + "/profile/"; - PREFS = PROFILE + "prefs"; } /** {@inheritDoc} */ @Override - public void getCurrentProfile(AsyncRequestCallback callback) { + public void getCurrentProfile(AsyncRequestCallback callback) { asyncRequestFactory.createGetRequest(PROFILE) .header(ACCEPT, APPLICATION_JSON) .loader(loaderFactory.newLoader("Retrieving current user's profile...")) @@ -60,7 +58,7 @@ public class UserProfileServiceClientImpl implements UserProfileServiceClient { /** {@inheritDoc} */ @Override - public void updateCurrentProfile(@NotNull Map updates, AsyncRequestCallback callback) { + public void updateCurrentProfile(@NotNull Map updates, AsyncRequestCallback callback) { asyncRequestFactory.createPostRequest(PROFILE, null) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) @@ -71,7 +69,7 @@ public class UserProfileServiceClientImpl implements UserProfileServiceClient { /** {@inheritDoc} */ @Override - public void getProfileById(@NotNull String id, AsyncRequestCallback callback) { + public void getProfileById(@NotNull String id, AsyncRequestCallback callback) { String requestUrl = PROFILE + id; asyncRequestFactory.createGetRequest(requestUrl) @@ -80,27 +78,9 @@ public class UserProfileServiceClientImpl implements UserProfileServiceClient { .send(callback); } - @Override - public void getPreferences(AsyncRequestCallback> callback) { - asyncRequestFactory.createGetRequest(PREFS) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .loader(loaderFactory.newLoader("Getting user's preferences...")) - .send(callback); - } - - @Override - public Promise> getPreferences() { - return asyncRequestFactory.createGetRequest(PREFS) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .loader(loaderFactory.newLoader("Getting user's preferences...")) - .send(new StringMapUnmarshaller()); - } - /** {@inheritDoc} */ @Override - public void updateProfile(@NotNull String id, Map updates, AsyncRequestCallback callback) { + public void updateProfile(@NotNull String id, Map updates, AsyncRequestCallback callback) { String requestUrl = PROFILE + id; asyncRequestFactory.createPostRequest(requestUrl, null) @@ -110,28 +90,4 @@ public class UserProfileServiceClientImpl implements UserProfileServiceClient { .loader(loaderFactory.newLoader("Updating user's profile...")) .send(callback); } - - /** {@inheritDoc} */ - @Override - public void updatePreferences(@NotNull Map update, AsyncRequestCallback> callback) { - final String data = JsonHelper.toJson(update); - asyncRequestFactory.createPostRequest(PREFS, null) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .data(data) - .loader(loaderFactory.newLoader("Updating user's preferences...")) - .send(callback); - } - - /** {@inheritDoc} */ - @Override - public Promise> updatePreferences(@NotNull Map update) { - final String data = JsonHelper.toJson(update); - return asyncRequestFactory.createPostRequest(PREFS, null) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .data(data) - .loader(loaderFactory.newLoader("Updating user's preferences...")) - .send(new StringMapUnmarshaller()); - } } diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClient.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClient.java index 5d84a46e81..19d612de8c 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClient.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClient.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.che.ide.api.user; -import org.eclipse.che.api.user.shared.dto.UserDescriptor; +import org.eclipse.che.api.user.shared.dto.UserDto; import org.eclipse.che.ide.rest.AsyncRequestCallback; import javax.validation.constraints.NotNull; @@ -31,14 +31,14 @@ public interface UserServiceClient { * if true - is temporary user * @param callback */ - void createUser(@NotNull String token, boolean isTemporary, AsyncRequestCallback callback); + void createUser(@NotNull String token, boolean isTemporary, AsyncRequestCallback callback); /** * Get current user's information. * * @param callback */ - void getCurrentUser(AsyncRequestCallback callback); + void getCurrentUser(AsyncRequestCallback callback); /** * Update user's password. @@ -56,7 +56,7 @@ public interface UserServiceClient { * user's id * @param callback */ - void getUserById(@NotNull String id, AsyncRequestCallback callback); + void getUserById(@NotNull String id, AsyncRequestCallback callback); /** * Get user's information by its alias. @@ -65,7 +65,7 @@ public interface UserServiceClient { * user's alias * @param callback */ - void getUserByAlias(@NotNull String alias, AsyncRequestCallback callback); + void getUserByAlias(@NotNull String alias, AsyncRequestCallback callback); /** * Remove user. diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClientImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClientImpl.java index 804b094055..b2b11f588f 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClientImpl.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/user/UserServiceClientImpl.java @@ -12,7 +12,7 @@ package org.eclipse.che.ide.api.user; import com.google.inject.Inject; -import org.eclipse.che.api.user.shared.dto.UserDescriptor; +import org.eclipse.che.api.user.shared.dto.UserDto; import org.eclipse.che.ide.MimeType; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.AsyncRequestFactory; @@ -52,7 +52,7 @@ public class UserServiceClientImpl implements UserServiceClient { /** {@inheritDoc} */ @Override - public void createUser(@NotNull String token, boolean isTemporary, AsyncRequestCallback callback) { + public void createUser(@NotNull String token, boolean isTemporary, AsyncRequestCallback callback) { StringBuilder requestUrl = new StringBuilder(CREATE); requestUrl.append("?token=").append(token).append("&temporary=").append(isTemporary); @@ -64,7 +64,7 @@ public class UserServiceClientImpl implements UserServiceClient { /** {@inheritDoc} */ @Override - public void getCurrentUser(AsyncRequestCallback callback) { + public void getCurrentUser(AsyncRequestCallback callback) { asyncRequestFactory.createGetRequest(USER) .header(ACCEPT, MimeType.APPLICATION_JSON) @@ -86,7 +86,7 @@ public class UserServiceClientImpl implements UserServiceClient { /** {@inheritDoc} */ @Override - public void getUserById(@NotNull String id, AsyncRequestCallback callback) { + public void getUserById(@NotNull String id, AsyncRequestCallback callback) { String requestUrl = USER + id; asyncRequestFactory.createGetRequest(requestUrl) @@ -97,7 +97,7 @@ public class UserServiceClientImpl implements UserServiceClient { /** {@inheritDoc} */ @Override - public void getUserByAlias(@NotNull String alias, AsyncRequestCallback callback) { + public void getUserByAlias(@NotNull String alias, AsyncRequestCallback callback) { String requestUrl = FIND + "?alias=" + alias; asyncRequestFactory.createGetRequest(requestUrl) diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java index db7f11f6e8..903ed9fd20 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/CoreGinModule.java @@ -41,6 +41,8 @@ import org.eclipse.che.ide.api.project.ProjectTypeServiceClient; import org.eclipse.che.ide.api.project.ProjectTypeServiceClientImpl; import org.eclipse.che.ide.api.ssh.SshServiceClient; import org.eclipse.che.ide.api.ssh.SshServiceClientImpl; +import org.eclipse.che.ide.api.user.PreferencesServiceClient; +import org.eclipse.che.ide.api.user.PreferencesServiceClientImpl; import org.eclipse.che.ide.api.user.UserProfileServiceClient; import org.eclipse.che.ide.api.user.UserProfileServiceClientImpl; import org.eclipse.che.ide.api.user.UserServiceClient; @@ -333,6 +335,7 @@ public class CoreGinModule extends AbstractGinModule { private void configurePlatformApiGwtClients() { bind(UserServiceClient.class).to(UserServiceClientImpl.class).in(Singleton.class); bind(UserProfileServiceClient.class).to(UserProfileServiceClientImpl.class).in(Singleton.class); + bind(PreferencesServiceClient.class).to(PreferencesServiceClientImpl.class).in(Singleton.class); bind(GitServiceClient.class).to(GitServiceClientImpl.class).in(Singleton.class); bind(OAuthServiceClient.class).to(OAuthServiceClientImpl.class).in(Singleton.class); bind(FactoryServiceClient.class).to(FactoryServiceClientImpl.class).in(Singleton.class); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/ProfileComponent.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/ProfileComponent.java index c37127aff5..b55fd2c3d1 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/ProfileComponent.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/ProfileComponent.java @@ -10,8 +10,8 @@ *******************************************************************************/ package org.eclipse.che.ide.core; +import org.eclipse.che.api.user.shared.dto.ProfileDto; import org.eclipse.che.ide.api.user.UserProfileServiceClient; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; import org.eclipse.che.ide.api.app.CurrentUser; import org.eclipse.che.ide.api.component.Component; import org.eclipse.che.ide.rest.AsyncRequestCallback; @@ -40,10 +40,10 @@ public class ProfileComponent implements Component { @Override public void start(final Callback callback) { - AsyncRequestCallback asyncRequestCallback = new AsyncRequestCallback( - dtoUnmarshallerFactory.newUnmarshaller(ProfileDescriptor.class)) { + AsyncRequestCallback asyncRequestCallback = new AsyncRequestCallback( + dtoUnmarshallerFactory.newUnmarshaller(ProfileDto.class)) { @Override - protected void onSuccess(final ProfileDescriptor profile) { + protected void onSuccess(final ProfileDto profile) { currentUser.setProfile(profile); callback.onSuccess(ProfileComponent.this); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/preferences/PreferencesManagerImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/preferences/PreferencesManagerImpl.java index 5c73c8ee24..ebddb5b45b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/preferences/PreferencesManagerImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/preferences/PreferencesManagerImpl.java @@ -17,7 +17,7 @@ import org.eclipse.che.api.promises.client.Function; import org.eclipse.che.api.promises.client.FunctionException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.js.Promises; -import org.eclipse.che.ide.api.user.UserProfileServiceClient; +import org.eclipse.che.ide.api.user.PreferencesServiceClient; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.ide.api.preferences.PreferencesManager; @@ -31,22 +31,22 @@ import java.util.Map; */ @Singleton public class PreferencesManagerImpl implements PreferencesManager { - private final UserProfileServiceClient userProfileService; - private final Map changedPreferences; + private final Map changedPreferences; + private final PreferencesServiceClient preferencesService; private Map persistedPreferences; /** * Create preferences manager * - * @param userProfileService + * @param preferencesService * user preference service client */ @Inject - protected PreferencesManagerImpl(UserProfileServiceClient userProfileService) { + protected PreferencesManagerImpl(PreferencesServiceClient preferencesService) { this.persistedPreferences = new HashMap<>(); this.changedPreferences = new HashMap<>(); - this.userProfileService = userProfileService; + this.preferencesService = preferencesService; } /** {@inheritDoc} */ @@ -72,9 +72,9 @@ public class PreferencesManagerImpl implements PreferencesManager { return Promises.resolve(null); } - return userProfileService.updatePreferences(changedPreferences).thenPromise(new Function, Promise>() { + return preferencesService.updatePreferences(changedPreferences).thenPromise(new Function, Promise>() { @Override - public Promise apply(Map result) throws FunctionException { + public Promise apply(Map result) throws FunctionException { persistedPreferences.putAll(changedPreferences); changedPreferences.clear(); return Promises.resolve(null); @@ -85,7 +85,7 @@ public class PreferencesManagerImpl implements PreferencesManager { /** {@inheritDoc} */ @Override public Promise> loadPreferences() { - return userProfileService.getPreferences().then(new Function, Map>() { + return preferencesService.getPreferences().then(new Function, Map>() { @Override public Map apply(Map preferences) throws FunctionException { persistedPreferences = preferences; diff --git a/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProvider.java b/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProvider.java index 9ce739728f..42aaadbc08 100644 --- a/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProvider.java +++ b/plugins/plugin-docker/che-plugin-docker-client/src/main/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProvider.java @@ -13,7 +13,7 @@ package org.eclipse.che.plugin.docker.client; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.PreferenceManager; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.dto.server.DtoFactory; @@ -35,11 +35,11 @@ public class UserSpecificDockerRegistryCredentialsProvider { private static final Logger LOG = LoggerFactory.getLogger(UserSpecificDockerRegistryCredentialsProvider.class); - private PreferenceDao preferenceDao; + private PreferenceManager preferenceManager; @Inject - public UserSpecificDockerRegistryCredentialsProvider(PreferenceDao preferenceDao) { - this.preferenceDao = preferenceDao; + public UserSpecificDockerRegistryCredentialsProvider(PreferenceManager preferenceManager) { + this.preferenceManager = preferenceManager; } /** @@ -52,11 +52,11 @@ public class UserSpecificDockerRegistryCredentialsProvider { @Nullable public AuthConfigs getCredentials() { try { - String encodedCredentials = preferenceDao.getPreferences(EnvironmentContext.getCurrent() - .getSubject() - .getUserId(), - DOCKER_REGISTRY_CREDENTIALS_KEY) - .get(DOCKER_REGISTRY_CREDENTIALS_KEY); + String encodedCredentials = preferenceManager.find(EnvironmentContext.getCurrent() + .getSubject() + .getUserId(), + DOCKER_REGISTRY_CREDENTIALS_KEY) + .get(DOCKER_REGISTRY_CREDENTIALS_KEY); String credentials = encodedCredentials != null ? new String(Base64.getDecoder().decode(encodedCredentials), "UTF-8") : "{}"; return DtoFactory.newDto(AuthConfigs.class).withConfigs( diff --git a/plugins/plugin-docker/che-plugin-docker-client/src/test/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProviderTest.java b/plugins/plugin-docker/che-plugin-docker-client/src/test/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProviderTest.java index bd645ba019..8e5c7e1f5d 100644 --- a/plugins/plugin-docker/che-plugin-docker-client/src/test/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProviderTest.java +++ b/plugins/plugin-docker/che-plugin-docker-client/src/test/java/org/eclipse/che/plugin/docker/client/UserSpecificDockerRegistryCredentialsProviderTest.java @@ -11,7 +11,7 @@ package org.eclipse.che.plugin.docker.client; import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.PreferenceManager; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.dto.server.DtoFactory; @@ -41,13 +41,13 @@ public class UserSpecificDockerRegistryCredentialsProviderTest { private static final String DOCKER_REGISTRY_CREDENTIALS_KEY = "dockerCredentials"; @Mock - private PreferenceDao preferenceDao; + private PreferenceManager preferenceManager; private UserSpecificDockerRegistryCredentialsProvider dockerCredentials; @BeforeClass private void before() { - dockerCredentials = new UserSpecificDockerRegistryCredentialsProvider(preferenceDao); + dockerCredentials = new UserSpecificDockerRegistryCredentialsProvider(preferenceManager); } @Test @@ -101,7 +101,7 @@ public class UserSpecificDockerRegistryCredentialsProviderTest { preferences.put(DOCKER_REGISTRY_CREDENTIALS_KEY, base64encodedCredentials); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("name", "id", "token1234", false)); - when(preferenceDao.getPreferences(anyObject(), anyObject())).thenReturn(preferences); + when(preferenceManager.find(anyObject(), anyObject())).thenReturn(preferences); } } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/integration/ServiceTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/integration/ServiceTest.java index f38d7ccd26..8a4ac374a2 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/integration/ServiceTest.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/integration/ServiceTest.java @@ -19,7 +19,7 @@ import org.eclipse.che.api.machine.server.MachineInstanceProviders; import org.eclipse.che.api.machine.server.MachineManager; import org.eclipse.che.api.machine.server.MachineRegistry; import org.eclipse.che.api.machine.server.MachineService; -import org.eclipse.che.api.machine.server.dao.SnapshotDao; +import org.eclipse.che.api.machine.server.spi.SnapshotDao; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.spi.impl.SnapshotImpl; import org.eclipse.che.api.machine.server.model.impl.MachineImpl; @@ -128,7 +128,7 @@ public class ServiceTest { .withHostConfig(new HostConfig().withPortBindings( singletonMap("5000/tcp", new PortBinding[]{new PortBinding().withHostPort("5000")}))); - registryContainerId = docker.createContainer(containerConfig, null).getId(); + registryContainerId = docker.createContainer(containerConfig, null).getUserId(); docker.startContainer(registryContainerId, null); } @@ -192,7 +192,7 @@ public class ServiceTest { } @Override - public String getId() { + public String getUserId() { return USER; } @@ -208,7 +208,7 @@ public class ServiceTest { @AfterMethod public void tearDown() throws Exception { for (MachineStateImpl machine : new ArrayList<>(machineManager.getMachinesStates())) { - machineManager.destroy(machine.getId(), false); + machineManager.destroy(machine.getUserId(), false); } EnvironmentContext.reset(); } @@ -224,7 +224,7 @@ public class ServiceTest { .withType("Dockerfile") .withScript("FROM ubuntu\nCMD tail -f /dev/null\n"))); - waitMachineIsRunning(machine.getId()); + waitMachineIsRunning(machine.getUserId()); } @Test(dependsOnMethods = "saveSnapshotTest", enabled = false) @@ -242,27 +242,27 @@ public class ServiceTest { final MachineStateDescriptor machine = machineService .createMachineFromSnapshot(newDto(SnapshotMachineCreationMetadata.class).withSnapshotId(SNAPSHOT_ID)); - waitMachineIsRunning(machine.getId()); + waitMachineIsRunning(machine.getUserId()); } @Test public void getMachineTest() throws Exception { final MachineImpl machine = createMachineAndWaitRunningState(); - final MachineDescriptor machineById = machineService.getMachineById(machine.getId()); + final MachineDescriptor machineById = machineService.getMachineById(machine.getUserId()); - assertEquals(machineById.getId(), machine.getId()); + assertEquals(machineById.getUserId(), machine.getUserId()); } @Test public void getMachinesTest() throws Exception { Set expected = new HashSet<>(); - expected.add(createMachineAndWaitRunningState().getId()); - expected.add(createMachineAndWaitRunningState().getId()); + expected.add(createMachineAndWaitRunningState().getUserId()); + expected.add(createMachineAndWaitRunningState().getUserId()); Set actual = machineManager.getMachinesStates() .stream() - .map(MachineImpl::getId) + .map(MachineImpl::getUserId) .collect(Collectors.toSet()); assertEquals(actual, expected); } @@ -271,14 +271,14 @@ public class ServiceTest { public void destroyMachineTest() throws Exception { final MachineImpl machine = createMachineAndWaitRunningState(); - machineService.destroyMachine(machine.getId()); + machineService.destroyMachine(machine.getUserId()); - assertEquals(machineService.getMachineStateById(machine.getId()).getStatus(), MachineStatus.DESTROYING); + assertEquals(machineService.getMachineStateById(machine.getUserId()).getStatus(), MachineStatus.DESTROYING); int counter = 0; while (++counter < 1000) { try { - machineManager.getMachine(machine.getId()); + machineManager.getMachine(machine.getUserId()); } catch (NotFoundException e) { return; } @@ -293,7 +293,7 @@ public class ServiceTest { // use machine manager instead of machine service because it returns future with snapshot // that allows check operation result - final SnapshotImpl snapshot = machineManager.save(machine.getId(), USER, "test description"); + final SnapshotImpl snapshot = machineManager.save(machine.getUserId(), USER, "test description"); for (int i = 0; snapshot.getInstanceKey() == null && i < 10; ++i) { Thread.sleep(500); @@ -337,13 +337,13 @@ public class ServiceTest { final MachineImpl machine = createMachineAndWaitRunningState(); String commandInMachine = "echo \"command in machine\" && tail -f /dev/null"; - machineService.executeCommandInMachine(machine.getId(), + machineService.executeCommandInMachine(machine.getUserId(), DtoFactory.newDto(CommandDto.class).withCommandLine(commandInMachine), null); Thread.sleep(500); - final List processes = machineService.getProcesses(machine.getId()); + final List processes = machineService.getProcesses(machine.getUserId()); assertEquals(processes.size(), 1); assertEquals(processes.get(0).getCommandLine(), commandInMachine); } @@ -357,12 +357,12 @@ public class ServiceTest { commands.add("sleep 10000"); for (String command : commands) { - machineService.executeCommandInMachine(machine.getId(), DtoFactory.newDto(CommandDto.class).withCommandLine(command), null); + machineService.executeCommandInMachine(machine.getUserId(), DtoFactory.newDto(CommandDto.class).withCommandLine(command), null); } Thread.sleep(500); - final List processes = machineService.getProcesses(machine.getId()); + final List processes = machineService.getProcesses(machine.getUserId()); assertEquals(processes.size(), 2); Set actualCommandLines = new HashSet<>(2); for (MachineProcessDto process : processes) { @@ -377,19 +377,19 @@ public class ServiceTest { final MachineImpl machine = createMachineAndWaitRunningState(); String commandInMachine = "echo \"command in machine\" && tail -f /dev/null"; - machineService.executeCommandInMachine(machine.getId(), + machineService.executeCommandInMachine(machine.getUserId(), DtoFactory.newDto(CommandDto.class).withCommandLine(commandInMachine), null); Thread.sleep(500); - final List processes = machineService.getProcesses(machine.getId()); + final List processes = machineService.getProcesses(machine.getUserId()); assertEquals(processes.size(), 1); assertEquals(processes.get(0).getCommandLine(), commandInMachine); - machineService.stopProcess(machine.getId(), processes.get(0).getPid()); + machineService.stopProcess(machine.getUserId(), processes.get(0).getPid()); - assertTrue(machineService.getProcesses(machine.getId()).isEmpty()); + assertTrue(machineService.getProcesses(machine.getUserId()).isEmpty()); } @Test(expectedExceptions = NotFoundException.class, expectedExceptionsMessageRegExp = "Process with pid .* not found") @@ -397,17 +397,17 @@ public class ServiceTest { final MachineImpl machine = createMachineAndWaitRunningState(); String commandInMachine = "echo \"command in machine\" && tail -f /dev/null"; - machineService.executeCommandInMachine(machine.getId(), + machineService.executeCommandInMachine(machine.getUserId(), DtoFactory.newDto(CommandDto.class).withCommandLine(commandInMachine), null); Thread.sleep(500); - final List processes = machineService.getProcesses(machine.getId()); + final List processes = machineService.getProcesses(machine.getUserId()); assertEquals(processes.size(), 1); assertEquals(processes.get(0).getCommandLine(), commandInMachine); - machineService.stopProcess(machine.getId(), processes.get(0).getPid() + 100); + machineService.stopProcess(machine.getUserId(), processes.get(0).getPid() + 100); } private MachineImpl createMachineAndWaitRunningState() throws Exception { @@ -423,7 +423,7 @@ public class ServiceTest { .withDev(false) .withDisplayName("displayName" + System.currentTimeMillis()) , false); - waitMachineIsRunning(machine.getId()); + waitMachineIsRunning(machine.getUserId()); return machine; } diff --git a/plugins/plugin-github/che-plugin-github-ide/src/main/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImpl.java b/plugins/plugin-github/che-plugin-github-ide/src/main/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImpl.java index 2fdbff90b6..9ff12bd033 100644 --- a/plugins/plugin-github/che-plugin-github-ide/src/main/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImpl.java +++ b/plugins/plugin-github/che-plugin-github-ide/src/main/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImpl.java @@ -142,7 +142,7 @@ public class GitHubAuthenticatorImpl implements OAuth2Authenticator, OAuthCallba private void generateSshKeys(final OAuthStatus authStatus) { final SshKeyUploader githubKeyUploader = registry.getUploader(GITHUB_HOST); if (githubKeyUploader != null) { - String userId = appContext.getCurrentUser().getProfile().getId(); + String userId = appContext.getCurrentUser().getProfile().getUserId(); githubKeyUploader.uploadKey(userId, new AsyncCallback() { @Override public void onSuccess(Void result) { diff --git a/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImplTest.java b/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImplTest.java index 06cc2fb719..690f4c370e 100644 --- a/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImplTest.java +++ b/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/authenticator/GitHubAuthenticatorImplTest.java @@ -16,7 +16,8 @@ import com.google.gwtmockito.GwtMockitoTestRunner; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.ssh.shared.dto.SshPairDto; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; +import org.eclipse.che.api.user.shared.dto.ProfileDto; +import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.app.CurrentUser; import org.eclipse.che.ide.api.dialogs.ConfirmCallback; @@ -141,14 +142,14 @@ public class GitHubAuthenticatorImplTest { SshKeyUploader sshKeyUploader = mock(SshKeyUploader.class); CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); when(view.isGenerateKeysSelected()).thenReturn(true); when(registry.getUploader(GITHUB_HOST)).thenReturn(sshKeyUploader); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); gitHubAuthenticator.onAuthenticated(authStatus); @@ -164,11 +165,11 @@ public class GitHubAuthenticatorImplTest { OAuthStatus authStatus = mock(OAuthStatus.class); CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); when(view.isGenerateKeysSelected()).thenReturn(false); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); gitHubAuthenticator.authenticate(null, getCallBack()); gitHubAuthenticator.onAuthenticated(authStatus); @@ -184,13 +185,13 @@ public class GitHubAuthenticatorImplTest { SshKeyUploader keyProvider = mock(SshKeyUploader.class); CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); when(view.isGenerateKeysSelected()).thenReturn(true); when(registry.getUploader(GITHUB_HOST)).thenReturn(keyProvider); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); gitHubAuthenticator.authenticate(null, getCallBack()); gitHubAuthenticator.onAuthenticated(authStatus); @@ -213,14 +214,14 @@ public class GitHubAuthenticatorImplTest { SshKeyUploader keyProvider = mock(SshKeyUploader.class); CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); MessageDialog messageDialog = mock(MessageDialog.class); when(view.isGenerateKeysSelected()).thenReturn(true); when(registry.getUploader(GITHUB_HOST)).thenReturn(keyProvider); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); when(dialogFactory.createMessageDialog(anyString(), anyString(), Matchers.anyObject())).thenReturn(messageDialog); gitHubAuthenticator.authenticate(null, getCallBack()); @@ -248,14 +249,14 @@ public class GitHubAuthenticatorImplTest { SshKeyUploader keyUploader = mock(SshKeyUploader.class); CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); MessageDialog messageDialog = mock(MessageDialog.class); when(view.isGenerateKeysSelected()).thenReturn(true); when(registry.getUploader(GITHUB_HOST)).thenReturn(keyUploader); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); when(dialogFactory.createMessageDialog(anyString(), anyString(), Matchers.anyObject())).thenReturn(messageDialog); when(pair.getName()).thenReturn(GITHUB_HOST); when(pair.getService()).thenReturn(SshKeyManagerPresenter.VCS_SSH_SERVICE); diff --git a/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/importer/page/GithubImporterPagePresenterTest.java b/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/importer/page/GithubImporterPagePresenterTest.java index 4d9aa2b651..807b13fb58 100644 --- a/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/importer/page/GithubImporterPagePresenterTest.java +++ b/plugins/plugin-github/che-plugin-github-ide/src/test/java/org/eclipse/che/plugin/github/ide/importer/page/GithubImporterPagePresenterTest.java @@ -20,7 +20,9 @@ import org.eclipse.che.api.project.shared.dto.ProjectImporterDescriptor; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.PromiseError; import org.eclipse.che.ide.api.user.UserServiceClient; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; +import org.eclipse.che.api.user.shared.dto.ProfileDto; +import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; +import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.app.CurrentUser; import org.eclipse.che.ide.api.notification.NotificationManager; @@ -500,11 +502,11 @@ public class GithubImporterPagePresenterTest { public void onLoadRepoClickedWhenAuthorizeIsFailed() throws Exception { String userId = "userId"; CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); final Throwable exception = mock(UnauthorizedException.class); @@ -553,12 +555,12 @@ public class GithubImporterPagePresenterTest { final Throwable exception = mock(UnauthorizedException.class); String userId = "userId"; CurrentUser user = mock(CurrentUser.class); - ProfileDescriptor profile = mock(ProfileDescriptor.class); + ProfileDto profile = mock(ProfileDto.class); doReturn(exception).when(promiseError).getCause(); when(appContext.getCurrentUser()).thenReturn(user); when(user.getProfile()).thenReturn(profile); - when(profile.getId()).thenReturn(userId); + when(profile.getUserId()).thenReturn(userId); presenter.onLoadRepoClicked(); diff --git a/plugins/plugin-java/che-plugin-java-ext-jdt/org-eclipse-ui-ide/src/main/java/org/eclipse/ui/IWorkingSet.java b/plugins/plugin-java/che-plugin-java-ext-jdt/org-eclipse-ui-ide/src/main/java/org/eclipse/ui/IWorkingSet.java index 00b1556499..b0be467f4e 100644 --- a/plugins/plugin-java/che-plugin-java-ext-jdt/org-eclipse-ui-ide/src/main/java/org/eclipse/ui/IWorkingSet.java +++ b/plugins/plugin-java/che-plugin-java-ext-jdt/org-eclipse-ui-ide/src/main/java/org/eclipse/ui/IWorkingSet.java @@ -53,9 +53,9 @@ public interface IWorkingSet extends /*IPersistableElement,*/ IAdaptable { * Currently, this is one of the icons specified in the extensions * of the org.eclipse.ui.workingSets extension point. * The extension is identified using the value returned by - * getId(). + * getUserId(). * Returns null if no icon has been specified in the - * extension or if getId() returns null. + * extension or if getUserId() returns null. * * @return the working set icon or null. * @since 2.1 @@ -69,9 +69,9 @@ public interface IWorkingSet extends /*IPersistableElement,*/ IAdaptable { * Currently, this is one of the icons specified in the extensions * of the org.eclipse.ui.workingSets extension point. * The extension is identified using the value returned by - * getId(). + * getUserId(). * Returns null if no icon has been specified in the - * extension or if getId() returns null. + * extension or if getUserId() returns null. * * @return the working set icon or null. * @since 3.3 diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenter.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenter.java index 287a373223..19b4a160b4 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenter.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenter.java @@ -17,8 +17,8 @@ import com.google.inject.Inject; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.PromiseError; +import org.eclipse.che.api.user.shared.dto.ProfileDto; import org.eclipse.che.ide.api.user.UserProfileServiceClient; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient; import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; import org.eclipse.che.ide.extension.machine.client.machine.Machine; @@ -66,11 +66,11 @@ public class MachineInfoPresenter implements TabPresenter { */ public void update(@NotNull Machine machine) { - Unmarshallable profileUnMarshaller = unmarshallerFactory.newUnmarshaller(ProfileDescriptor.class); + Unmarshallable profileUnMarshaller = unmarshallerFactory.newUnmarshaller(ProfileDto.class); - userProfile.getCurrentProfile(new AsyncRequestCallback(profileUnMarshaller) { + userProfile.getCurrentProfile(new AsyncRequestCallback(profileUnMarshaller) { @Override - protected void onSuccess(ProfileDescriptor result) { + protected void onSuccess(ProfileDto result) { Map attributes = result.getAttributes(); String firstName = attributes.get(FIRST_NAME_KEY); diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenterTest.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenterTest.java index aa4bd503cd..40270ba6f6 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenterTest.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/sufficientinfo/MachineInfoPresenterTest.java @@ -13,8 +13,8 @@ package org.eclipse.che.ide.extension.machine.client.perspective.widgets.machine import com.google.gwt.user.client.ui.AcceptsOneWidget; import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.api.user.shared.dto.ProfileDto; import org.eclipse.che.ide.api.user.UserProfileServiceClient; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient; import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto; import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; @@ -62,22 +62,22 @@ public class MachineInfoPresenterTest { //additional mocks @Mock - private Machine machine; + private Machine machine; @Mock - private AcceptsOneWidget container; + private AcceptsOneWidget container; @Mock - private Unmarshallable profileUnmarshaller; + private Unmarshallable profileUnmarshaller; @Mock - private Unmarshallable wsUnmarshaller; + private Unmarshallable wsUnmarshaller; @Mock - private ProfileDescriptor profileDescriptor; + private ProfileDto profileDescriptor; @Mock private WorkspaceDto wsDescriptor; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Promise promise; @Captor - private ArgumentCaptor> profileCaptor; + private ArgumentCaptor> profileCaptor; @Captor private ArgumentCaptor> wsCaptor; @@ -88,7 +88,7 @@ public class MachineInfoPresenterTest { public void setUp() { when(machine.getWorkspaceId()).thenReturn(SOME_TEXT); - when(unmarshallerFactory.newUnmarshaller(ProfileDescriptor.class)).thenReturn(profileUnmarshaller); + when(unmarshallerFactory.newUnmarshaller(ProfileDto.class)).thenReturn(profileUnmarshaller); when(unmarshallerFactory.newUnmarshaller(WorkspaceDto.class)).thenReturn(wsUnmarshaller); } @@ -98,9 +98,9 @@ public class MachineInfoPresenterTest { presenter.update(machine); - verify(unmarshallerFactory).newUnmarshaller(ProfileDescriptor.class); + verify(unmarshallerFactory).newUnmarshaller(ProfileDto.class); - verify(userProfile).getCurrentProfile(Matchers.>anyObject()); + verify(userProfile).getCurrentProfile(Matchers.>anyObject()); verify(machine).getWorkspaceId(); verify(wsService).getWorkspace(eq(SOME_TEXT)); @@ -119,7 +119,7 @@ public class MachineInfoPresenterTest { presenter.update(machine); verify(userProfile).getCurrentProfile(profileCaptor.capture()); - AsyncRequestCallback callback = profileCaptor.getValue(); + AsyncRequestCallback callback = profileCaptor.getValue(); //noinspection NonJREEmulationClassesInClientCode Method method = callback.getClass().getDeclaredMethod("onSuccess", Object.class); @@ -144,7 +144,7 @@ public class MachineInfoPresenterTest { presenter.update(machine); verify(userProfile).getCurrentProfile(profileCaptor.capture()); - AsyncRequestCallback callback = profileCaptor.getValue(); + AsyncRequestCallback callback = profileCaptor.getValue(); //noinspection NonJREEmulationClassesInClientCode Method method = callback.getClass().getDeclaredMethod("onSuccess", Object.class); diff --git a/plugins/plugin-ssh-key/che-plugin-ssh-key-ide/src/main/java/org/eclipse/che/plugin/ssh/key/client/manage/SshKeyManagerPresenter.java b/plugins/plugin-ssh-key/che-plugin-ssh-key-ide/src/main/java/org/eclipse/che/plugin/ssh/key/client/manage/SshKeyManagerPresenter.java index 3ca65b0e1b..dc90af0c2d 100644 --- a/plugins/plugin-ssh-key/che-plugin-ssh-key-ide/src/main/java/org/eclipse/che/plugin/ssh/key/client/manage/SshKeyManagerPresenter.java +++ b/plugins/plugin-ssh-key/che-plugin-ssh-key-ide/src/main/java/org/eclipse/che/plugin/ssh/key/client/manage/SshKeyManagerPresenter.java @@ -178,7 +178,7 @@ public class SshKeyManagerPresenter extends AbstractPreferencePagePresenter impl CurrentUser user = appContext.getCurrentUser(); final SshKeyUploader githubUploader = registry.getUploaders().get(GITHUB_HOST); if (user != null && githubUploader != null) { - githubUploader.uploadKey(user.getProfile().getId(), new AsyncCallback() { + githubUploader.uploadKey(user.getProfile().getUserId(), new AsyncCallback() { @Override public void onSuccess(Void result) { refreshKeys(); diff --git a/plugins/plugin-svn/che-plugin-svn-ext-server/src/main/java/org/eclipse/che/plugin/svn/server/credentials/CurrentUserPreferencesAccessImpl.java b/plugins/plugin-svn/che-plugin-svn-ext-server/src/main/java/org/eclipse/che/plugin/svn/server/credentials/CurrentUserPreferencesAccessImpl.java index 43f3302c9c..71db6d5177 100644 --- a/plugins/plugin-svn/che-plugin-svn-ext-server/src/main/java/org/eclipse/che/plugin/svn/server/credentials/CurrentUserPreferencesAccessImpl.java +++ b/plugins/plugin-svn/che-plugin-svn-ext-server/src/main/java/org/eclipse/che/plugin/svn/server/credentials/CurrentUserPreferencesAccessImpl.java @@ -10,9 +10,8 @@ *******************************************************************************/ package org.eclipse.che.plugin.svn.server.credentials; -import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.subject.Subject; @@ -45,7 +44,7 @@ public class CurrentUserPreferencesAccessImpl implements CurrentUserPreferencesA content.put(key, value); try { this.preferencesDao.setPreferences(currentSubject.getUserId(), content); - } catch (final ServerException | NotFoundException e) { + } catch (final ServerException e) { throw new PreferencesAccessException(e); } } diff --git a/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/SubversionProjectImporterTest.java b/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/SubversionProjectImporterTest.java index efca68365a..4b18ad594a 100644 --- a/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/SubversionProjectImporterTest.java +++ b/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/SubversionProjectImporterTest.java @@ -20,7 +20,7 @@ import org.eclipse.che.api.project.server.FolderEntry; import org.eclipse.che.api.project.server.importer.ProjectImporter; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.api.project.server.type.ValueProviderFactory; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.user.server.spi.ProfileDao; import org.eclipse.che.api.vfs.VirtualFile; import org.eclipse.che.api.vfs.VirtualFileSystem; import org.eclipse.che.commons.lang.NameGenerator; @@ -47,7 +47,8 @@ import static org.mockito.Mockito.when; public class SubversionProjectImporterTest { @Mock - private UserProfileDao userProfileDao; + private ProfileDao userProfileDao; + @Mock private CredentialsProvider credentialsProvider; @Mock @@ -73,7 +74,7 @@ public class SubversionProjectImporterTest { .to(SubversionValueProviderFactory.class); bind(SshKeyProvider.class).toInstance(sshKeyProvider); - bind(UserProfileDao.class).toInstance(userProfileDao); + bind(ProfileDao.class).toInstance(userProfileDao); bind(CredentialsProvider.class).toInstance(credentialsProvider); bind(RepositoryUrlProvider.class).toInstance(repositoryUrlProvider); } diff --git a/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/utils/TestUtils.java b/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/utils/TestUtils.java index 9cec8bc957..1eed582ac9 100644 --- a/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/utils/TestUtils.java +++ b/plugins/plugin-svn/che-plugin-svn-ext-server/src/test/java/org/eclipse/che/plugin/svn/server/utils/TestUtils.java @@ -15,8 +15,8 @@ import com.google.common.io.Files; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.core.util.LineConsumerFactory; -import org.eclipse.che.api.user.server.dao.Profile; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.ProfileDao; import org.eclipse.che.api.vfs.VirtualFileSystem; import org.eclipse.che.api.vfs.VirtualFileSystemProvider; import org.eclipse.che.api.vfs.impl.file.LocalVirtualFileSystemProvider; @@ -163,7 +163,7 @@ public class TestUtils { * @throws Exception * if anything goes wrong */ - public static void createTestUser(final UserProfileDao userProfileDao) throws Exception { + public static void createTestUser(final ProfileDao userProfileDao) throws Exception { // set current user EnvironmentContext.getCurrent().setSubject(new SubjectImpl("codenvy", "codenvy", null, false)); @@ -175,7 +175,7 @@ public class TestUtils { profileAttributes.put("email", "che@eclipse.org"); Mockito.when(userProfileDao.getById("codenvy")) - .thenReturn(new Profile().withId("codenvy").withUserId("codenvy").withAttributes(profileAttributes)); + .thenReturn(new ProfileImpl("codenvy", profileAttributes)); } /** diff --git a/pom.xml b/pom.xml index 3b0149288e..e7570de986 100644 --- a/pom.xml +++ b/pom.xml @@ -186,6 +186,12 @@ che-core-api-ssh-shared ${che.version} + + org.eclipse.che.core + che-core-api-user + ${project.version} + tests + org.eclipse.che.core che-core-api-user diff --git a/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/LocalGitUserResolver.java b/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/LocalGitUserResolver.java index 62173c7bad..d4ab4dd4ee 100644 --- a/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/LocalGitUserResolver.java +++ b/wsagent/che-core-api-git/src/main/java/org/eclipse/che/api/git/LocalGitUserResolver.java @@ -12,7 +12,7 @@ package org.eclipse.che.api.git; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.git.shared.GitUser; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.commons.env.EnvironmentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java index a757539d9a..d2bb1e393f 100644 --- a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java +++ b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java @@ -40,7 +40,7 @@ import org.eclipse.che.api.project.shared.dto.ItemReference; import org.eclipse.che.api.project.shared.dto.MoveOptions; import org.eclipse.che.api.project.shared.dto.SourceEstimation; import org.eclipse.che.api.project.shared.dto.TreeElement; -import org.eclipse.che.api.user.server.dao.UserDao; +import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.vfs.VirtualFile; import org.eclipse.che.api.vfs.VirtualFileSystem; import org.eclipse.che.api.vfs.impl.file.DefaultFileWatcherNotificationHandler; diff --git a/wsagent/wsagent-local/src/main/java/org/eclipse/che/RemotePreferenceDao.java b/wsagent/wsagent-local/src/main/java/org/eclipse/che/RemotePreferenceDao.java index 6817bb84fa..06762de4d4 100644 --- a/wsagent/wsagent-local/src/main/java/org/eclipse/che/RemotePreferenceDao.java +++ b/wsagent/wsagent-local/src/main/java/org/eclipse/che/RemotePreferenceDao.java @@ -17,8 +17,7 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.UnauthorizedException; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; -import org.eclipse.che.api.user.server.UserProfileService; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.commons.env.EnvironmentContext; import javax.inject.Inject; @@ -44,12 +43,12 @@ public class RemotePreferenceDao implements PreferenceDao { @Inject public RemotePreferenceDao(@Named("api.endpoint") String apiUrl, HttpJsonRequestFactory requestFactory) { - this.prefsUrl = apiUrl + "/profile/prefs"; + this.prefsUrl = apiUrl + "/preferences"; this.requestFactory = requestFactory; } @Override - public void setPreferences(String userId, Map preferences) throws ServerException, NotFoundException { + public void setPreferences(String userId, Map preferences) throws ServerException { requireNonNull(preferences, "Required non-null preferences"); checkUserId(requireNonNull(userId, "Required non-null user id")); try { diff --git a/wsagent/wsagent-local/src/test/java/org/eclipse/che/RemotePreferenceDaoCompatibilityTest.java b/wsagent/wsagent-local/src/test/java/org/eclipse/che/RemotePreferenceDaoCompatibilityTest.java index 3b7b29aaf0..592936d4f7 100644 --- a/wsagent/wsagent-local/src/test/java/org/eclipse/che/RemotePreferenceDaoCompatibilityTest.java +++ b/wsagent/wsagent-local/src/test/java/org/eclipse/che/RemotePreferenceDaoCompatibilityTest.java @@ -11,10 +11,9 @@ package org.eclipse.che; import org.eclipse.che.api.core.rest.DefaultHttpJsonRequestFactory; -import org.eclipse.che.api.user.server.UserProfileService; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.UserDao; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.user.server.PreferenceManager; +import org.eclipse.che.api.user.server.PreferencesService; +import org.eclipse.che.api.user.server.ProfileService; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.commons.subject.SubjectImpl; @@ -41,7 +40,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * Tests that {@link RemotePreferenceDao} uses correct methods of {@link UserProfileService}. + * Tests that {@link RemotePreferenceDao} uses correct methods of {@link ProfileService}. * * @author Yevhenii Voevodin */ @@ -53,16 +52,10 @@ public class RemotePreferenceDaoCompatibilityTest { private static final Subject TEST_SUBJECT = new SubjectImpl("name", "id", "token", false); @Mock - private PreferenceDao preferenceDaoMock; - - @Mock - private UserDao userDao; - - @Mock - private UserProfileDao userProfileDao; + private PreferenceManager preferenceManager; @InjectMocks - private UserProfileService profileService; + private PreferencesService preferenceService; @BeforeMethod private void setUp() { @@ -77,7 +70,7 @@ public class RemotePreferenceDaoCompatibilityTest { remoteDao.getPreferences(TEST_SUBJECT.getUserId()); - verify(preferenceDaoMock).getPreferences(TEST_SUBJECT.getUserId()); + verify(preferenceManager).find(TEST_SUBJECT.getUserId()); } @Test @@ -86,7 +79,7 @@ public class RemotePreferenceDaoCompatibilityTest { remoteDao.getPreferences(TEST_SUBJECT.getUserId(), "filter"); - verify(preferenceDaoMock).getPreferences(TEST_SUBJECT.getUserId(), "filter"); + verify(preferenceManager).find(TEST_SUBJECT.getUserId(), "filter"); } @Test @@ -96,7 +89,7 @@ public class RemotePreferenceDaoCompatibilityTest { remoteDao.setPreferences(TEST_SUBJECT.getUserId(), prefs); - verify(preferenceDaoMock).setPreferences(TEST_SUBJECT.getUserId(), prefs); + verify(preferenceManager).save(TEST_SUBJECT.getUserId(), prefs); } @Test @@ -105,7 +98,7 @@ public class RemotePreferenceDaoCompatibilityTest { remoteDao.remove(TEST_SUBJECT.getUserId()); - verify(preferenceDaoMock).remove(TEST_SUBJECT.getUserId()); + verify(preferenceManager).remove(TEST_SUBJECT.getUserId()); } @Filter diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java index d6a623ed1c..7a42236b7c 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java @@ -31,7 +31,7 @@ import org.eclipse.che.api.factory.server.builder.FactoryBuilder; import org.eclipse.che.api.factory.server.snippet.SnippetGenerator; import org.eclipse.che.api.factory.shared.dto.Author; import org.eclipse.che.api.factory.shared.dto.Factory; -import org.eclipse.che.api.user.server.dao.UserDao; +import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.workspace.server.WorkspaceManager; import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryAcceptValidatorImpl.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryAcceptValidatorImpl.java index 69d52f3162..dacb658c6b 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryAcceptValidatorImpl.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryAcceptValidatorImpl.java @@ -13,7 +13,7 @@ package org.eclipse.che.api.factory.server.impl; import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.factory.server.FactoryAcceptValidator; import org.eclipse.che.api.factory.shared.dto.Factory; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidator.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidator.java index cceb5cbbbf..73feb7a8ff 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidator.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidator.java @@ -20,7 +20,7 @@ import org.eclipse.che.api.factory.shared.dto.Ide; import org.eclipse.che.api.factory.shared.dto.OnAppLoaded; import org.eclipse.che.api.factory.shared.dto.OnProjectsLoaded; import org.eclipse.che.api.factory.shared.dto.Policies; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import java.io.UnsupportedEncodingException; @@ -30,7 +30,6 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; -import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.Boolean.parseBoolean; import static java.lang.String.format; diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryCreateValidatorImpl.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryCreateValidatorImpl.java index 999550fbc3..168e768fb6 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryCreateValidatorImpl.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/impl/FactoryCreateValidatorImpl.java @@ -15,7 +15,7 @@ import org.eclipse.che.api.core.ForbiddenException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.factory.server.FactoryCreateValidator; import org.eclipse.che.api.factory.shared.dto.Factory; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.api.workspace.server.WorkspaceValidator; import javax.inject.Inject; diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/FactoryServiceTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/FactoryServiceTest.java index 9891e0c01e..138aa8e238 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/FactoryServiceTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/FactoryServiceTest.java @@ -26,8 +26,8 @@ import org.eclipse.che.api.factory.shared.dto.Button; import org.eclipse.che.api.factory.shared.dto.ButtonAttributes; import org.eclipse.che.api.factory.shared.dto.Factory; import org.eclipse.che.api.machine.shared.dto.CommandDto; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.workspace.server.WorkspaceManager; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; @@ -161,7 +161,11 @@ public class FactoryServiceTest { factoryBuilder = spy(new FactoryBuilder(new SourceStorageParametersValidator())); doNothing().when(factoryBuilder).checkValid(any(Factory.class)); when(factoryParametersResolverHolder.getFactoryParametersResolvers()).thenReturn(factoryParametersResolvers); - when(userDao.getById(anyString())).thenReturn(new User().withName(JettyHttpServer.ADMIN_USER_NAME)); + when(userDao.getById(anyString())).thenReturn(new UserImpl(null, + null, + JettyHttpServer.ADMIN_USER_NAME, + null, + null)); factoryService = new FactoryService(factoryStore, createValidator, acceptValidator, diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidatorTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidatorTest.java index d67d83bf3f..37dd3c3369 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidatorTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryBaseValidatorTest.java @@ -26,9 +26,9 @@ import org.eclipse.che.api.factory.shared.dto.OnAppClosed; import org.eclipse.che.api.factory.shared.dto.OnAppLoaded; import org.eclipse.che.api.factory.shared.dto.OnProjectsLoaded; import org.eclipse.che.api.factory.shared.dto.Policies; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.server.spi.PreferenceDao; +import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto; @@ -86,7 +86,7 @@ public class FactoryBaseValidatorTest { .withCreator(newDto(Author.class) .withUserId("userid")); - User user = new User().withId("userid"); + UserImpl user = new UserImpl("userid"); when(userDao.getById("userid")).thenReturn(user); validator = new TesterFactoryBaseValidator(preferenceDao); diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryCreateAndAcceptValidatorsImplsTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryCreateAndAcceptValidatorsImplsTest.java index a60644563a..0df79abda2 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryCreateAndAcceptValidatorsImplsTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/FactoryCreateAndAcceptValidatorsImplsTest.java @@ -13,8 +13,8 @@ package org.eclipse.che.api.factory.server.impl; import org.eclipse.che.api.core.ApiException; import org.eclipse.che.api.core.model.workspace.WorkspaceConfig; import org.eclipse.che.api.factory.shared.dto.Factory; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.UserDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; +import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.workspace.server.WorkspaceValidator; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/TesterFactoryBaseValidator.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/TesterFactoryBaseValidator.java index ecaac39056..5c5080b6df 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/TesterFactoryBaseValidator.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/impl/TesterFactoryBaseValidator.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.che.api.factory.server.impl; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; /** * @author Sergii Kabashniuk diff --git a/wsmaster/che-core-api-user-shared/pom.xml b/wsmaster/che-core-api-user-shared/pom.xml index aa6e1404d6..239d2c61b2 100644 --- a/wsmaster/che-core-api-user-shared/pom.xml +++ b/wsmaster/che-core-api-user-shared/pom.xml @@ -41,6 +41,10 @@ org.eclipse.che.core che-core-api-dto + + org.eclipse.che.core + che-core-api-model + diff --git a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/MembershipDto.java b/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/MembershipDto.java deleted file mode 100644 index 7c754e2ed3..0000000000 --- a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/MembershipDto.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.user.shared.dto; - -import org.eclipse.che.api.user.shared.model.Membership; -import org.eclipse.che.dto.shared.DTO; - -import java.util.List; -import java.util.Map; - -/** - * - * - * @author andrew00x - */ -@DTO -public interface MembershipDto extends Membership { - - @Override - String getUserId(); - - void setUserId(String id); - - MembershipDto withUserId(String id); - - @Override - String getScope(); - - @Override - String getUserName(); - - @Override - String getSubjectId(); - - @Override - Map getSubjectProperties(); - - @Override - List getRoles(); - - void setRoles(List roles); - - MembershipDto withRoles(List roles); -} diff --git a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/ProfileDescriptor.java b/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/ProfileDto.java similarity index 68% rename from wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/ProfileDescriptor.java rename to wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/ProfileDto.java index 076c23f189..df91663179 100644 --- a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/ProfileDescriptor.java +++ b/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/ProfileDto.java @@ -10,43 +10,47 @@ *******************************************************************************/ package org.eclipse.che.api.user.shared.dto; +import org.eclipse.che.api.core.model.user.Profile; import org.eclipse.che.api.core.rest.shared.dto.Link; +import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.dto.shared.DTO; + import io.swagger.annotations.ApiModelProperty; import java.util.List; import java.util.Map; /** - * @author andrew00x + * This object used for transporting profile data to/from client. + * + * @author Yevhenii Voevodin + * @see Profile + * @see DtoFactory */ @DTO -public interface ProfileDescriptor { - - void setId(String id); - - @ApiModelProperty("Profile ID") - String getId(); - - ProfileDescriptor withId(String id); - - @ApiModelProperty("User ID") - String getUserId(); +public interface ProfileDto extends Profile { void setUserId(String id); - ProfileDescriptor withUserId(String id); + @ApiModelProperty("Profile ID") + String getUserId(); + + ProfileDto withUserId(String id); @ApiModelProperty("Profile attributes") Map getAttributes(); void setAttributes(Map attributes); - ProfileDescriptor withAttributes(Map attributes); + ProfileDto withAttributes(Map attributes); List getLinks(); void setLinks(List links); - ProfileDescriptor withLinks(List links); + ProfileDto withLinks(List links); + + String getEmail(); + + ProfileDto withEmail(String email); } diff --git a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/UserDescriptor.java b/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/UserDto.java similarity index 72% rename from wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/UserDescriptor.java rename to wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/UserDto.java index 09681e0af7..5013273967 100644 --- a/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/UserDescriptor.java +++ b/wsmaster/che-core-api-user-shared/src/main/java/org/eclipse/che/api/user/shared/dto/UserDto.java @@ -10,56 +10,62 @@ *******************************************************************************/ package org.eclipse.che.api.user.shared.dto; +import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.core.rest.shared.dto.Link; +import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.dto.shared.DTO; + import io.swagger.annotations.ApiModelProperty; import java.util.List; /** - * @author andrew00x + * This object used for transporting user data to/from client. + * + * @author Yevhenii Voevodin + * @see User + * @see DtoFactory */ @DTO -public interface UserDescriptor { +public interface UserDto extends User { @ApiModelProperty("User ID") String getId(); void setId(String id); - UserDescriptor withId(String id); - + UserDto withId(String id); @ApiModelProperty("User alias which is used for oAuth") List getAliases(); void setAliases(List aliases); - UserDescriptor withAliases(List aliases); + UserDto withAliases(List aliases); @ApiModelProperty("User email") String getEmail(); void setEmail(String email); - UserDescriptor withEmail(String email); + UserDto withEmail(String email); @ApiModelProperty("User name") String getName(); void setName(String name); - UserDescriptor withName(String name); + UserDto withName(String name); @ApiModelProperty("User password") String getPassword(); void setPassword(String password); - UserDescriptor withPassword(String password); + UserDto withPassword(String password); List getLinks(); void setLinks(List links); - UserDescriptor withLinks(List links); + UserDto withLinks(List links); } diff --git a/wsmaster/che-core-api-user/pom.xml b/wsmaster/che-core-api-user/pom.xml index 11db5d149a..774fe5d09a 100644 --- a/wsmaster/che-core-api-user/pom.xml +++ b/wsmaster/che-core-api-user/pom.xml @@ -48,6 +48,10 @@ org.eclipse.che.core che-core-api-dto + + org.eclipse.che.core + che-core-api-model + org.eclipse.che.core che-core-api-user-shared @@ -56,10 +60,19 @@ org.eclipse.che.core che-core-commons-lang + + org.eclipse.che.core + che-core-commons-test + org.slf4j slf4j-api + + com.google.code.gson + gson + test + com.jayway.restassured rest-assured @@ -99,4 +112,35 @@ test + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/spi/tck/*.* + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + **/spi/tck/*.* + + + + + + + diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/Constants.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/Constants.java index 3fb6d2501d..3181cce577 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/Constants.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/Constants.java @@ -11,30 +11,32 @@ package org.eclipse.che.api.user.server; /** - * Constants for User API and UserProfile API + * Constants for User/Profile/Preferences API. * - * @author Eugene Voevodin + * @author Yevhenii Voevodin * @author Max Shaposhnik */ public final class Constants { - public static final String LINK_REL_GET_CURRENT_USER_PROFILE = "current user profile"; - public static final String LINK_REL_UPDATE_CURRENT_USER_PROFILE = "update current user profile"; - public static final String LINK_REL_GET_USER_PROFILE_BY_ID = "user profile by id"; - public static final String LINK_REL_UPDATE_USER_PROFILE_BY_ID = "update user profile by id"; - public static final String LINK_REL_INROLE = "in role"; - public static final String LINK_REL_CREATE_USER = "create user"; - public static final String LINK_REL_GET_CURRENT_USER = "get current"; - public static final String LINK_REL_UPDATE_PASSWORD = "update password"; - public static final String LINK_REL_REMOVE_PREFERENCES = "remove preferences"; - public static final String LINK_REL_REMOVE_ATTRIBUTES = "remove attributes"; - public static final String LINK_REL_GET_USER_BY_ID = "get user by id"; - public static final String LINK_REL_GET_USER_BY_EMAIL = "get user by email"; - public static final String LINK_REL_REMOVE_USER_BY_ID = "remove user by id"; - public static final String LINK_REL_UPDATE_PREFERENCES = "update prefs"; - public static final int ID_LENGTH = 16; - public static final int PASSWORD_LENGTH = 10; + /** Profile link relationships. */ + public static final String LINK_REL_CURRENT_PROFILE = "current_profile"; + public static final String LINK_REL_CURRENT_PROFILE_ATTRIBUTES = "current_profile.attributes"; + public static final String LINK_REL_PROFILE = "profile"; + public static final String LINK_REL_PROFILE_ATTRIBUTES = "profile.attributes"; - private Constants() { - } + /** User links relationships. */ + public static final String LINK_REL_USER = "user"; + public static final String LINK_REL_CURRENT_USER = "current_user"; + public static final String LINK_REL_CURRENT_USER_PASSWORD = "current_user.password"; + public static final String LINK_REL_CURRENT_USER_SETTINGS = "current_user.settings"; + + /** Preferences links relationships. */ + public static final String LINK_REL_PREFERENCES = "preferences"; + + public static final String LINK_REL_SELF = "self"; + + public static final int ID_LENGTH = 16; + public static final int PASSWORD_LENGTH = 10; + + private Constants() {} } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/DtoConverter.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/DtoConverter.java index cd9a5806f0..c12a424387 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/DtoConverter.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/DtoConverter.java @@ -10,8 +10,8 @@ *******************************************************************************/ package org.eclipse.che.api.user.server; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.shared.dto.UserDescriptor; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.user.shared.dto.UserDto; import org.eclipse.che.dto.server.DtoFactory; /** @@ -22,10 +22,10 @@ import org.eclipse.che.dto.server.DtoFactory; public final class DtoConverter { /** - * Converts {@link User} to {@link UserDescriptor}. + * Converts {@link User} to {@link UserDto}. */ - public static UserDescriptor toDescriptor(User user) { - return DtoFactory.getInstance().createDto(UserDescriptor.class) + public static UserDto asDto(User user) { + return DtoFactory.getInstance().createDto(UserDto.class) .withId(user.getId()) .withEmail(user.getEmail()) .withName(user.getName()) diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferenceManager.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferenceManager.java new file mode 100644 index 0000000000..2a97b1fca5 --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferenceManager.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import com.google.common.util.concurrent.Striped; + +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.user.server.spi.PreferenceDao; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; + +import static java.util.Objects.requireNonNull; + +/** + * Preferences manager layer, simplifies preferences service by + * taking all the business logic out from the service and making that logic easily + * reusable throughout the system. + * + *

The manager doesn't perform any bean validations and it + * is expected that all the incoming objects are valid, nevertheless + * this exactly the right place for performing business validations. + * + * @author Yevhenii Voevodin + */ +@Singleton +public class PreferenceManager { + + private static final Striped UPDATE_REENTRANT_LOCKS = Striped.lazyWeakLock(32); + + @Inject + private PreferenceDao preferenceDao; + + /** + * Associates the given {@code preferences} with the given {@code userId}. + * + *

Note that this method will override all the existing properties + * for the user with id {@code userId}. + * + * @param userId + * the user id whom the {@code preferences} belong to + * @param preferences + * the preferences to associate with the {@code userId} + * @throws NullPointerException + * when either {@code userId} or {@code preferences} is null + * @throws ServerException + * when any error occurs + */ + public void save(String userId, Map preferences) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + requireNonNull(preferences, "Required non-null preferences"); + preferenceDao.setPreferences(userId, preferences); + } + + /** + * Updates the preferences of the user by merging given {@code preferences} + * with the existing preferences. If user doesn't have any preferences + * then the given {@code preferences} will be associated with the user. + * + * @param userId + * the user whose preferences should be updated + * @param preferences + * preferences update + * @return all the user's preferences including the update + * @throws NullPointerException + * when either {@code userId} or {@code preferences} is null + * @throws ServerException + * when any error occurs + */ + public Map update(String userId, Map preferences) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + requireNonNull(preferences, "Required non-null preferences"); + // Holding reference to prevent garbage collection + // this reentrantLock helps to avoid race-conditions when parallel updates are applied + final Lock reentrantLock = UPDATE_REENTRANT_LOCKS.get(userId); + reentrantLock.lock(); + try { + final Map found = preferenceDao.getPreferences(userId); + found.putAll(preferences); + preferenceDao.setPreferences(userId, found); + return found; + } finally { + reentrantLock.unlock(); + } + } + + /** + * Finds user's preferences. + * + * @param userId + * user id to find preferences + * @return found preferences or empty map, if there are no preferences related to user + * @throws NullPointerException + * when {@code userId} is null + * @throws ServerException + * when any error occurs + */ + public Map find(String userId) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + return preferenceDao.getPreferences(userId); + } + + /** + * Finds user's preferences. + * + * @param userId + * user id to find preferences + * @param keyFilter + * regex which is used to filter preferences by keys, so + * result contains only the user's preferences that match {@code keyFilter} regex + * @return found preferences filtered by {@code keyFilter} or an empty map + * if there are no preferences related to user + * @throws NullPointerException + * when {@code userId} is null + * @throws ServerException + * when any error occurs + */ + public Map find(String userId, String keyFilter) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + return preferenceDao.getPreferences(userId, keyFilter); + } + + /** + * Removes(clears) user's preferences. + * + * @param userId + * the id of the user to remove preferences + * @throws NullPointerException + * when {@code userId} is null + * @throws ServerException + * when any error occurs + */ + public void remove(String userId) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + preferenceDao.remove(userId); + } + + /** + * Removes the preferences with the given {@code names}. + * + * @param userId + * the id of the user to remove preferences + * @param names + * the names to remove + * @throws NullPointerException + * when either {@code userId} or {@code names} is null + * @throws ServerException + * when any error occurs + */ + public void remove(String userId, List names) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + requireNonNull(names, "Required non-null preference names"); + // Holding reference to prevent garbage collection + // this reentrantLock helps to avoid race-conditions when parallel updates are applied + final Lock reentrantLock = UPDATE_REENTRANT_LOCKS.get(userId); + reentrantLock.lock(); + try { + final Map preferences = preferenceDao.getPreferences(userId); + names.forEach(preferences::remove); + preferenceDao.setPreferences(userId, preferences); + } finally { + reentrantLock.unlock(); + } + } +} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferencesService.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferencesService.java new file mode 100644 index 0000000000..9ca9d74e30 --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/PreferencesService.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import org.eclipse.che.api.core.BadRequestException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.rest.Service; +import org.eclipse.che.api.core.rest.annotations.GenerateLink; +import org.eclipse.che.commons.env.EnvironmentContext; + +import javax.annotation.security.RolesAllowed; +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import java.util.List; +import java.util.Map; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_PREFERENCES; + +/** + * Preferences REST API. + * + * @author Yevhenii Voevodin + */ +@Path("/preferences") +@Api(value = "/preferences", description = "Preferences REST API") +public class PreferencesService extends Service { + + @Inject + private PreferenceManager preferenceManager; + + @GET + @Produces(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_PREFERENCES) + @ApiOperation(value = "Gets preferences of logged in user", + notes = "If not all the preferences needed then 'filter' may be used, " + + "basically it is regex for filtering preferences by names") + @ApiResponses({@ApiResponse(code = 200, message = "Preferences successfully fetched"), + @ApiResponse(code = 500, message = "Internal Server Error")}) + public Map find(@ApiParam("Regex for filtering preferences by names, e.g. '.*github.*' " + + "will return all the preferences which name contains github") + @QueryParam("filter") + String filter) throws ServerException { + if (filter == null) { + return preferenceManager.find(userId()); + } + return preferenceManager.find(userId(), filter); + } + + @POST + @Consumes(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_PREFERENCES) + @ApiOperation(value = "Saves preferences of logged in user", + notes = "All the existing user's preferences will be override by this method") + @ApiResponses({@ApiResponse(code = 204, message = "Preferences successfully saved"), + @ApiResponse(code = 400, message = "Request doesn't contain new preferences"), + @ApiResponse(code = 500, message = "Couldn't save preferences due to internal server error")}) + public void save(Map preferences) throws BadRequestException, ServerException { + if (preferences == null) { + throw new BadRequestException("Required non-null new preferences"); + } + preferenceManager.save(userId(), preferences); + } + + @PUT + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_PREFERENCES) + @ApiOperation(value = "Updates preferences of logged in user", + notes = "The merge strategy is used for update, which means that " + + "existing preferences with keys equal to update preference keys will " + + "be replaces with new values, and new preferences will be added") + @ApiResponses({@ApiResponse(code = 200, message = "Preferences successfully updated, response contains " + + "all the user preferences"), + @ApiResponse(code = 400, message = "Request doesn't contain preferences update"), + @ApiResponse(code = 500, message = "Couldn't update preferences due to internal server error")}) + public Map update(Map preferences) throws ServerException, BadRequestException { + if (preferences == null) { + throw new BadRequestException("Required non-null preferences update"); + } + return preferenceManager.update(userId(), preferences); + } + + @DELETE + @Consumes(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_PREFERENCES) + @ApiOperation(value = "Remove preferences of logged in user.", + notes = "If names are not specified, then all the user's preferences will be removed, " + + "otherwise only the preferences which names are listed") + @ApiResponses({@ApiResponse(code = 204, message = "Preferences successfully removed"), + @ApiResponse(code = 500, message = "Couldn't remove preferences due to internal server error")}) + public void removePreferences(@ApiParam("Preferences to remove") List names) throws ServerException { + if (names == null || names.isEmpty()) { + preferenceManager.remove(userId()); + } else { + preferenceManager.remove(userId(), names); + } + } + + private static String userId() { + return EnvironmentContext.getCurrent().getSubject().getUserId(); + } +} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileLinksInjector.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileLinksInjector.java new file mode 100644 index 0000000000..752ce15556 --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileLinksInjector.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import org.eclipse.che.api.core.rest.ServiceContext; +import org.eclipse.che.api.core.rest.shared.dto.Link; +import org.eclipse.che.api.user.shared.dto.ProfileDto; + +import javax.inject.Singleton; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.UriBuilder; +import java.util.ArrayList; +import java.util.List; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.eclipse.che.api.core.util.LinksHelper.createLink; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_PROFILE; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_PROFILE_ATTRIBUTES; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_PROFILE_ATTRIBUTES; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_SELF; + +/** + * Creates and injects links to the {@link ProfileDto} object. + * + * @author Yevhenii Voevodin + */ +@Singleton +public class ProfileLinksInjector { + + public ProfileDto injectLinks(ProfileDto profileDto, ServiceContext serviceContext) { + final UriBuilder uriBuilder = serviceContext.getServiceUriBuilder(); + final List links = new ArrayList<>(5); + links.add(createLink(HttpMethod.GET, + uriBuilder.clone() + .path(ProfileService.class, "getCurrent") + .build() + .toString(), + null, + APPLICATION_JSON, + LINK_REL_CURRENT_PROFILE)); + links.add(createLink(HttpMethod.GET, + uriBuilder.clone() + .path(ProfileService.class, "getById") + .build(profileDto.getUserId()) + .toString(), + null, + APPLICATION_JSON, + LINK_REL_SELF)); + links.add(createLink(HttpMethod.PUT, + uriBuilder.clone() + .path(ProfileService.class, "updateAttributes") + .build() + .toString(), + APPLICATION_JSON, + APPLICATION_JSON, + LINK_REL_CURRENT_PROFILE_ATTRIBUTES)); + links.add(createLink(HttpMethod.DELETE, + uriBuilder.clone() + .path(ProfileService.class, "removeAttributes") + .build(profileDto.getUserId()) + .toString(), + APPLICATION_JSON, + APPLICATION_JSON, + LINK_REL_CURRENT_PROFILE_ATTRIBUTES)); + links.add(createLink(HttpMethod.PUT, + uriBuilder.clone() + .path(ProfileService.class, "updateAttributesById") + .build(profileDto.getUserId()) + .toString(), + APPLICATION_JSON, + APPLICATION_JSON, + LINK_REL_PROFILE_ATTRIBUTES)); + return profileDto.withLinks(links); + } +} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileManager.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileManager.java new file mode 100644 index 0000000000..ba72708941 --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileManager.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.Profile; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.ProfileDao; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import static java.util.Objects.requireNonNull; + +/** + * Preferences manager layer, simplifies prefernces service by + * taking all the business logic out from the service and making that logic + * easily reusable throughout the system. + * + *

The manager doesn't perform any bean validations and it + * is expected that all the incoming objects are valid, nevertheless + * this exactly the right place for performing business validations. + * + * @author Yevhenii Voevodin + */ +@Singleton +public class ProfileManager { + + @Inject + private ProfileDao profileDao; + + /** + * Finds the profile related to the user with given {@code userId}. + * + * @param userId + * the id to search the user's profile + * @return found profile + * @throws NullPointerException + * when {@code userId} is null + * @throws NotFoundException + * when there is no profile for the user with the id {@code userId} + * @throws ServerException + * when any other error occurs + */ + public Profile getById(String userId) throws NotFoundException, ServerException { + requireNonNull(userId, "Required non-null user id"); + return profileDao.getById(userId); + } + + /** + * Creates a new user's profile . + * + * @param profile + * new profile + * @throws NullPointerException + * when profile is null + * @throws ConflictException + * when profile for the user {@code profile.getUserId()} already exists + * @throws ServerException + * when any other error occurs + */ + public void create(Profile profile) throws ServerException, ConflictException { + requireNonNull(profile, "Required non-null profile"); + profileDao.create(new ProfileImpl(profile)); + } + + /** + * Updates current profile using replace strategy. + * + *

Note that {@link Profile#getEmail()} can't be updated using this method + * as it is mirrored from the {@link User#getEmail()}. + * + * @param profile + * profile update + * @throws NullPointerException + * when {@code profile} is null + * @throws NotFoundException + * when there is no profile for the user with the id {@code profile.getUserId()} + * @throws ServerException + * when any other error occurs + */ + public void update(Profile profile) throws NotFoundException, ServerException { + requireNonNull(profile, "Required non-null profile"); + profileDao.update(new ProfileImpl(profile)); + } + + /** + * Removes the user's profile. + * + *

Note that this method won't throw any exception when + * user doesn't have the corresponding profile. + * + * @param userId + * the id of the user, whose profile should be removed + * @throws NullPointerException + * when {@code id} is null + * @throws ServerException + * when any other error occurs + */ + public void remove(String userId) throws ServerException { + requireNonNull(userId, "Required non-null user id"); + profileDao.remove(userId); + } +} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileService.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileService.java new file mode 100644 index 0000000000..aef2115761 --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/ProfileService.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import org.eclipse.che.api.core.BadRequestException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.Profile; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.core.rest.Service; +import org.eclipse.che.api.core.rest.annotations.GenerateLink; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.shared.dto.ProfileDto; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.dto.server.DtoFactory; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.SecurityContext; + +import java.util.List; +import java.util.Map; + +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_PROFILE; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_PROFILE_ATTRIBUTES; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +/** + * Profile REST API. + * + * @author Yevhenii Voevodin + */ +@Api(value = "/profile", description = "Profile REST API") +@Path("/profile") +public class ProfileService extends Service { + + @Inject + private ProfileManager profileManager; + @Inject + private UserManager userManager; + @Inject + private ProfileLinksInjector linksInjector; + @Context + private SecurityContext context; + + @GET + @Produces(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_CURRENT_PROFILE) + @ApiOperation("Get profile of the logged in user") + @ApiResponses({@ApiResponse(code = 200, message = "The response contains requested profile entity"), + @ApiResponse(code = 404, message = "Currently logged in user doesn't have profile"), + @ApiResponse(code = 500, message = "Couldn't retrieve profile due to internal server error")}) + public ProfileDto getCurrent() throws ServerException, NotFoundException { + final ProfileImpl profile = new ProfileImpl(profileManager.getById(userId())); + return linksInjector.injectLinks(asDto(profile, userManager.getById(profile.getUserId())), getServiceContext()); + } + + @GET + @Path("/{id}") + @Produces(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_CURRENT_PROFILE) + @ApiOperation("Get profile by user's id") + @ApiResponses({@ApiResponse(code = 200, message = "The response contains requested profile entity"), + @ApiResponse(code = 404, message = "Profile for the user with requested identifier doesn't exist"), + @ApiResponse(code = 500, message = "Couldn't retrieve profile due to internal server error")}) + public ProfileDto getById(@ApiParam("User identifier") + @PathParam("id") + String userId) throws NotFoundException, ServerException { + return linksInjector.injectLinks(asDto(profileManager.getById(userId), userManager.getById(userId)), getServiceContext()); + } + + @PUT + @Path("/{id}/attributes") + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_JSON) + @ApiOperation(value = "Update the profile attributes of the user with requested identifier", + notes = "The replace strategy is used for the update, so all the existing profile " + + "attributes will be override by the profile update") + @ApiResponses({@ApiResponse(code = 200, message = "The profile successfully updated and the response contains " + + "newly updated profile entity"), + @ApiResponse(code = 404, message = "When profile for the user with requested identifier doesn't exist"), + @ApiResponse(code = 500, message = "Couldn't retrieve profile due to internal server error")}) + public ProfileDto updateAttributesById(@ApiParam("Id of the user") + @PathParam("id") + String userId, + @ApiParam("New profile attributes") + Map updates) throws NotFoundException, + ServerException, + BadRequestException { + if (updates == null) { + throw new BadRequestException("Update attributes required"); + } + final ProfileImpl profile = new ProfileImpl(profileManager.getById(userId)); + profile.setAttributes(updates); + profileManager.update(profile); + return linksInjector.injectLinks(asDto(profile, userManager.getById(userId)), getServiceContext()); + } + + @PUT + @Path("/attributes") + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_JSON) + @GenerateLink(rel = LINK_REL_CURRENT_PROFILE_ATTRIBUTES) + @ApiOperation(value = "Update the profile attributes of the currently logged in user", + notes = "The replace strategy is used for the update, so all the existing profile " + + "attributes will be override with incoming values") + public ProfileDto updateAttributes(@ApiParam("New profile attributes") + Map updates) throws NotFoundException, + ServerException, + BadRequestException { + if (updates == null) { + throw new BadRequestException("Update attributes required"); + } + final ProfileImpl profile = new ProfileImpl(profileManager.getById(userId())); + profile.setAttributes(updates); + profileManager.update(profile); + return linksInjector.injectLinks(asDto(profile, userManager.getById(profile.getUserId())), getServiceContext()); + } + + @DELETE + @Path("/attributes") + @GenerateLink(rel = LINK_REL_CURRENT_PROFILE_ATTRIBUTES) + @Consumes(APPLICATION_JSON) + @ApiOperation(value = "Remove profile attributes which names are equal to given", + notes = "If names list is not send, all the attributes will be removed, " + + "if there are no attributes which names equal to some of the given names, " + + "then those names are skipped.") + @ApiResponses({@ApiResponse(code = 204, message = "Attributes successfully removed"), + @ApiResponse(code = 500, message = "Couldn't remove attributes due to internal server error")}) + public void removeAttributes(@ApiParam("The names of the profile attributes to remove") + List names) throws NotFoundException, ServerException { + final Profile profile = profileManager.getById(userId()); + final Map attributes = profile.getAttributes(); + if (names == null) { + attributes.clear(); + } else { + names.forEach(attributes::remove); + } + profileManager.update(profile); + } + + private static ProfileDto asDto(Profile profile, User user) { + return DtoFactory.newDto(ProfileDto.class) + .withUserId(profile.getUserId()) + .withEmail(user.getEmail()) + .withAttributes(profile.getAttributes()); + } + + private static String userId() { + return EnvironmentContext.getCurrent().getSubject().getUserId(); + } +} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/TokenValidator.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/TokenValidator.java index e5be2caf94..cc890f7807 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/TokenValidator.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/TokenValidator.java @@ -11,7 +11,7 @@ package org.eclipse.che.api.user.server; import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.user.server.dao.User; +import org.eclipse.che.api.core.model.user.User; /** * Validates token. @@ -22,7 +22,7 @@ import org.eclipse.che.api.user.server.dao.User; public interface TokenValidator { /** - * Validates {@param token} + * Validates {@code token}. * * @return user email * @throws ConflictException diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/LinksInjector.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserLinksInjector.java similarity index 54% rename from wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/LinksInjector.java rename to wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserLinksInjector.java index 9eaa226df4..ba823c5d75 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/LinksInjector.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserLinksInjector.java @@ -13,96 +13,88 @@ package org.eclipse.che.api.user.server; import org.eclipse.che.api.core.rest.ServiceContext; import org.eclipse.che.api.core.rest.shared.dto.Link; import org.eclipse.che.api.core.util.LinksHelper; -import org.eclipse.che.api.user.shared.dto.UserDescriptor; +import org.eclipse.che.api.user.shared.dto.UserDto; +import javax.inject.Singleton; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.UriBuilder; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_CURRENT_USER; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_CURRENT_USER_PROFILE; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_BY_EMAIL; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_PROFILE_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_REMOVE_USER_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_PASSWORD; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_USER; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_USER_PASSWORD; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_USER_SETTINGS; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_PREFERENCES; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_PROFILE; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_SELF; /** * Helps to inject {@link UserService} related links. * * @author Anatoliy Bazko + * @author Yevhenii Voevodin */ -public final class LinksInjector { +@Singleton +public class UserLinksInjector { - public static UserDescriptor injectLinks(UserDescriptor userDescriptor, ServiceContext serviceContext) { + public UserDto injectLinks(UserDto userDto, ServiceContext serviceContext) { final UriBuilder uriBuilder = serviceContext.getBaseUriBuilder(); - final List links = new LinkedList<>(); + final List links = new ArrayList<>(6); links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone().path(UserProfileService.class) - .path(UserProfileService.class, "getCurrent") - .build() + uriBuilder.clone() + .path(UserService.class) + .path(UserService.class, "getById") + .build(userDto.getId()) .toString(), null, APPLICATION_JSON, - LINK_REL_GET_CURRENT_USER_PROFILE)); + LINK_REL_SELF)); links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone().path(UserService.class) + uriBuilder.clone() + .path(UserService.class) .path(UserService.class, "getCurrent") .build() .toString(), null, APPLICATION_JSON, - LINK_REL_GET_CURRENT_USER)); + LINK_REL_CURRENT_USER)); links.add(LinksHelper.createLink(HttpMethod.POST, - uriBuilder.clone().path(UserService.class) + uriBuilder.clone() + .path(UserService.class) .path(UserService.class, "updatePassword") .build() .toString(), APPLICATION_FORM_URLENCODED, null, - LINK_REL_UPDATE_PASSWORD)); - + LINK_REL_CURRENT_USER_PASSWORD)); links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone().path(UserService.class) - .path(UserService.class, "getById") - .build(userDescriptor.getId()) + uriBuilder.clone() + .path(ProfileService.class) + .path(ProfileService.class, "getById") + .build(userDto.getId()) .toString(), null, APPLICATION_JSON, - LINK_REL_GET_USER_BY_ID)); + LINK_REL_PROFILE)); links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone().path(UserProfileService.class) - .path(UserProfileService.class, "getById") - .build(userDescriptor.getId()) + uriBuilder.clone() + .path(UserService.class) + .path(UserService.class, "getSettings") + .build() .toString(), null, APPLICATION_JSON, - LINK_REL_GET_USER_PROFILE_BY_ID)); - if (userDescriptor.getEmail() != null) { - links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone().path(UserService.class) - .path(UserService.class, "getByAlias") - .queryParam("email", userDescriptor.getEmail()) - .build() - .toString(), - null, - APPLICATION_JSON, - LINK_REL_GET_USER_BY_EMAIL)); - } - links.add(LinksHelper.createLink(HttpMethod.DELETE, - uriBuilder.clone().path(UserService.class) - .path(UserService.class, "remove") - .build(userDescriptor.getId()) + LINK_REL_CURRENT_USER_SETTINGS)); + links.add(LinksHelper.createLink(HttpMethod.GET, + uriBuilder.clone().path(PreferencesService.class) + .path(PreferencesService.class, "find") + .build() .toString(), null, - null, - LINK_REL_REMOVE_USER_BY_ID)); - - return userDescriptor.withLinks(links); + APPLICATION_JSON, + LINK_REL_PREFERENCES)); + return userDto.withLinks(links); } - - private LinksInjector() {} } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserManager.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserManager.java index 6f1da2d75b..3cd30c8ab9 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserManager.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserManager.java @@ -10,167 +10,218 @@ *******************************************************************************/ package org.eclipse.che.api.user.server; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import org.eclipse.che.api.core.ConflictException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.Profile; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.core.model.user.Profile; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.PreferenceDao; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.eclipse.che.api.user.server.spi.UserDao; +import org.eclipse.che.api.user.server.model.impl.UserImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import static com.google.common.base.MoreObjects.firstNonNull; import static java.lang.String.format; +import static java.lang.System.currentTimeMillis; +import static java.util.Objects.requireNonNull; import static org.eclipse.che.api.user.server.Constants.ID_LENGTH; import static org.eclipse.che.api.user.server.Constants.PASSWORD_LENGTH; import static org.eclipse.che.commons.lang.NameGenerator.generate; /** - * Facade for User related operations. + * Facade for {@link User} and {@link Profile} related operations. * * @author Max Shaposhnik (mshaposhnik@codenvy.com) + * @author Yevhenii Voevodin */ +@Singleton public class UserManager { - private static final Logger LOG = LoggerFactory.getLogger(UserManager.class); - private final UserDao userDao; - private final UserProfileDao profileDao; - private final PreferenceDao preferenceDao; - private final Set reservedNames; + private final UserDao userDao; + private final ProfileDao profileDao; + private final PreferenceDao preferencesDao; + private final Set reservedNames; @Inject public UserManager(UserDao userDao, - UserProfileDao profileDao, - PreferenceDao preferenceDao, + ProfileDao profileDao, + PreferenceDao preferencesDao, @Named("user.reserved_names") String[] reservedNames) { this.userDao = userDao; this.profileDao = profileDao; - this.preferenceDao = preferenceDao; + this.preferencesDao = preferencesDao; this.reservedNames = Sets.newHashSet(reservedNames); } - /** - * Creates new user. + * Creates new user and his profile. * - * @param user - * POJO representation of user entity + * @param newUser + * created user + * @throws NullPointerException + * when {@code newUser} is null * @throws ConflictException - * when given user cannot be created + * when user with such name/email/alias already exists * @throws ServerException * when any other error occurs */ - public void create(User user, boolean isTemporary) throws ConflictException, ServerException { - if (reservedNames.contains(user.getName().toLowerCase())) { - throw new ConflictException(String.format("Username \"%s\" is reserved", user.getName())); + public User create(User newUser, boolean isTemporary) throws ConflictException, ServerException { + requireNonNull(newUser, "Required non-null user"); + if (reservedNames.contains(newUser.getName().toLowerCase())) { + throw new ConflictException(String.format("Username '%s' is reserved", newUser.getName())); } - user.withId(generate("user", ID_LENGTH)) - .withPassword(firstNonNull(user.getPassword(), generate("", PASSWORD_LENGTH))); - userDao.create(user); - - profileDao.create(new Profile(user.getId())); - - final Map preferences = new HashMap<>(4); - preferences.put("temporary", Boolean.toString(isTemporary)); - preferences.put("codenvy:created", Long.toString(System.currentTimeMillis())); + final UserImpl user = new UserImpl(generate("user", ID_LENGTH), + newUser.getEmail(), + newUser.getName(), + firstNonNull(newUser.getPassword(), generate("", PASSWORD_LENGTH)), + newUser.getAliases()); try { - preferenceDao.setPreferences(user.getId(), preferences); - } catch (NotFoundException e) { - LOG.warn(format("Cannot set creation time preferences for user %s.", user.getId()), e); + userDao.create(user); + profileDao.create(new ProfileImpl(user.getId())); + preferencesDao.setPreferences(user.getId(), ImmutableMap.of("temporary", Boolean.toString(isTemporary), + "codenvy:created", Long.toString(currentTimeMillis()))); + } catch (ConflictException | ServerException x) { + // optimistic rollback(won't remove profile if userDao.remove failed) + // remove operation is not-found-safe so if any exception + // during the either user or profile creation occurs remove all entities + // NOTE: this logic must be replaced with transaction management + try { + userDao.remove(user.getId()); + profileDao.remove(user.getId()); + preferencesDao.remove(user.getId()); + } catch (ConflictException | ServerException rollbackEx) { + LOG.error(format("An attempt to clean up resources due to user creation failure was unsuccessful." + + "Now the system may be in inconsistent state. " + + "User with id '%s' must not exist", + user.getId()), + rollbackEx); + } + throw x; } + return user; } + /** + * Updates user by replacing an existing user entity with a new one. + * + * @param user + * user update + * @throws NullPointerException + * when {@code user} is null + * @throws NotFoundException + * when user with id {@code user.getId()} is not found + * @throws ConflictException + * when user's new alias/email/name is not unique + * @throws ServerException + * when any other error occurs + */ + public void update(User user) throws NotFoundException, ServerException, ConflictException { + requireNonNull(user, "Required non-null user"); + userDao.update(new UserImpl(user)); + } /** - * Gets user from persistent layer by his identifier + * Finds user by given {@code id}. * * @param id * user identifier - * @return user POJO + * @return user instance + * @throws NullPointerException + * when {@code id} is null * @throws NotFoundException * when user doesn't exist * @throws ServerException * when any other error occurs */ public User getById(String id) throws NotFoundException, ServerException { + requireNonNull(id, "Required non-null id"); return userDao.getById(id); } - /** - * Updates already present in persistent layer user. - * - * @param user - * POJO representation of user entity - * @throws NotFoundException - * when user is not found - * @throws ConflictException - * when given user cannot be updated - * @throws ServerException - * when any other error occurs - * - */ - public void update(User user) throws NotFoundException, ServerException, ConflictException { - userDao.update(user); - } - - - /** - * Gets user from persistent layer by any of his aliases + * Finds user by given {@code alias}. * * @param alias - * user name or alias - * @return user POJO + * user alias + * @return user instance + * @throws NullPointerException + * when {@code alias} is null * @throws NotFoundException * when user doesn't exist * @throws ServerException * when any other error occurs */ public User getByAlias(String alias) throws NotFoundException, ServerException { + requireNonNull(alias, "Required non-null alias"); return userDao.getByAlias(alias); } - /** - * Gets user from persistent layer by his username + * Finds user by given {@code name}. * - * @param userName + * @param name * user name - * @return user POJO + * @return user instance + * @throws NullPointerException + * when {@code name} is null * @throws NotFoundException * when user doesn't exist * @throws ServerException * when any other error occurs */ - public User getByName(String userName) throws NotFoundException, ServerException { - return userDao.getByName(userName); + public User getByName(String name) throws NotFoundException, ServerException { + requireNonNull(name, "Required non-null name"); + return userDao.getByName(name); } + /** + * Finds user by given {@code email}. + * + * @param email + * user email + * @return user instance + * @throws NullPointerException + * when {@code email} is null + * @throws NotFoundException + * when user doesn't exist + * @throws ServerException + * when any other error occurs + */ + public User getByEmail(String email) throws NotFoundException, ServerException { + requireNonNull(email, "Required non-null email"); + return userDao.getByEmail(email); + } /** - * Removes user from persistent layer by his identifier. + * Removes user and his dependencies by given {@code id}. * * @param id * user identifier + * @throws NullPointerException + * when {@code id} is null * @throws ConflictException * when given user cannot be deleted * @throws ServerException * when any other error occurs */ - public void remove(String id) throws NotFoundException, ServerException, ConflictException { + public void remove(String id) throws ServerException, ConflictException { + requireNonNull(id, "Required non-null id"); + profileDao.remove(id); + preferencesDao.remove(id); userDao.remove(id); } } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserProfileService.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserProfileService.java deleted file mode 100644 index 23d8ccd355..0000000000 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserProfileService.java +++ /dev/null @@ -1,442 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.user.server; - - -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -import com.google.common.util.concurrent.Striped; - -import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.core.NotFoundException; -import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.core.rest.Service; -import org.eclipse.che.api.core.rest.annotations.Description; -import org.eclipse.che.api.core.rest.annotations.GenerateLink; -import org.eclipse.che.api.core.rest.annotations.Required; -import org.eclipse.che.api.core.rest.shared.dto.Link; -import org.eclipse.che.api.core.util.LinksHelper; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.Profile; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; -import org.eclipse.che.api.user.server.dao.UserProfileDao; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; -import org.eclipse.che.commons.env.EnvironmentContext; -import org.eclipse.che.commons.subject.Subject; -import org.eclipse.che.dto.server.DtoFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.UriBuilder; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.Lock; - -import static com.google.common.base.Strings.nullToEmpty; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_CURRENT_USER_PROFILE; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_PROFILE_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_REMOVE_ATTRIBUTES; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_REMOVE_PREFERENCES; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_CURRENT_USER_PROFILE; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_PREFERENCES; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_USER_PROFILE_BY_ID; - -/** - * User Profile API - * - * @author Eugene Voevodin - * @author Max Shaposhnik - */ -@Api(value = "/profile", - description = "User profile manager") -@Path("/profile") -public class UserProfileService extends Service { - - private static final Logger LOG = LoggerFactory.getLogger(UserProfileService.class); - - // Assuming 1000 concurrent users at most trying to update their preferences (if more they will wait for another user to finish). - // Using the lazy weak version of Striped so the locks will be created on demand and not eagerly, and garbage collected when not needed anymore. - private static final Striped preferencesUpdateLocksByUser = Striped.lazyWeakLock(1000); - - private final UserProfileDao profileDao; - private final UserDao userDao; - private final PreferenceDao preferenceDao; - - @Inject - public UserProfileService(UserProfileDao profileDao, PreferenceDao preferenceDao, UserDao userDao) { - this.profileDao = profileDao; - this.userDao = userDao; - this.preferenceDao = preferenceDao; - } - - /** - *

Returns {@link ProfileDescriptor} for current user profile.

- *

By default user email will be added to attributes with key 'email'.

- * - * @return descriptor of profile - * @throws ServerException - * when some error occurred while retrieving/updating profile - * @see ProfileDescriptor - * @see #updateCurrent(Map) - */ - @ApiOperation(value = "Get user profile", - notes = "Get user profile details.", - response = ProfileDescriptor.class) - @ApiResponses(value = { - @ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - @GET - @GenerateLink(rel = LINK_REL_GET_CURRENT_USER_PROFILE) - @Produces(APPLICATION_JSON) - public ProfileDescriptor getCurrent() throws NotFoundException, ServerException { - final User user = userDao.getById(currentUser().getUserId()); - final Profile profile = profileDao.getById(user.getId()); - profile.getAttributes().put("email", user.getEmail()); - return toDescriptor(profile); - } - - /** - * Returns preferences for current user - */ - @ApiOperation(value = "Get user preferences", - notes = "Get user preferences, like SSH keys, recently opened project and files. It is possible " + - "to use a filter, e.g. CodenvyAppState or ssh.key.public.github.com to get the last opened project " + - "or a public part of GitHub SSH key (if any)", - response = ProfileDescriptor.class) - @ApiResponses(value = { - @ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - @GET - @Path("/prefs") - @Produces(APPLICATION_JSON) - public Map getPreferences(@ApiParam(value = "Filer") - @QueryParam("filter") String filter) throws ServerException { - if (filter != null) { - return preferenceDao.getPreferences(currentUser().getUserId(), filter); - } - return preferenceDao.getPreferences(currentUser().getUserId()); - } - - /** - * Updates attributes of current user profile. - * - * @param updates - * attributes to update - * @return descriptor of updated profile - * @throws ServerException - * when some error occurred while retrieving/persisting profile - * @see ProfileDescriptor - */ - - @POST - @GenerateLink(rel = LINK_REL_UPDATE_CURRENT_USER_PROFILE) - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - public ProfileDescriptor updateCurrent(@Description("attributes to update") Map updates) - throws NotFoundException, ServerException, ConflictException { - if (updates == null || updates.isEmpty()) { - throw new ConflictException("Attributes to update required"); - } - final User user = userDao.getById(currentUser().getUserId()); - final Profile profile = profileDao.getById(user.getId()); - profile.getAttributes().putAll(updates); - profileDao.update(profile); - logEventUserUpdateProfile(user, profile.getAttributes()); - return toDescriptor(profile); - } - - - /** - * Updates attributes of certain profile. - * - * @param profileId - * profile identifier - * @param updates - * attributes to update - * @return descriptor of updated profile - * @throws NotFoundException - * when profile with given identifier doesn't exist - * @throws ServerException - * when some error occurred while retrieving/updating profile - * @see ProfileDescriptor - * @see #getById(String) - */ - - @POST - @Path("/{id}") - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - public ProfileDescriptor update(@PathParam("id") String profileId, - Map updates) throws NotFoundException, ServerException, ConflictException { - if (updates == null || updates.isEmpty()) { - throw new ConflictException("Attributes to update required"); - } - final Profile profile = profileDao.getById(profileId); - profile.getAttributes().putAll(updates); - profileDao.update(profile); - final User user = userDao.getById(profile.getUserId()); - logEventUserUpdateProfile(user, profile.getAttributes()); - return toDescriptor(profile); - } - - /** - * Searches for profile with given identifier and {@link ProfileDescriptor} if found. - * - * @param profileId - * profile identifier - * @return descriptor of found profile - * @throws NotFoundException - * when profile with given identifier doesn't exist - * @throws ServerException - * when some error occurred while retrieving user or profile - * @see ProfileDescriptor - * @see #getById(String) - */ - @ApiOperation(value = "Get profile of a specific user", - notes = "Get profile of a specific user", - response = ProfileDescriptor.class) - @ApiResponses(value = { - @ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - @GET - @Path("/{id}") - @Produces(APPLICATION_JSON) - public ProfileDescriptor getById(@ApiParam(value = " ID") - @PathParam("id") - String profileId) throws NotFoundException, ServerException { - final Profile profile = profileDao.getById(profileId); - final User user = userDao.getById(profile.getUserId()); - profile.getAttributes().put("email", user.getEmail()); - return toDescriptor(profile); - } - - /** - *

Updates preferences of current user profile.

- * - * @param update - * update preferences - * @return descriptor of updated profile - * @throws ServerException - * when some error occurred while retrieving/updating profile - * @throws ConflictException - * when update is {@code null} or empty - * @see ProfileDescriptor - * @see #updateCurrent(Map) - */ - @POST - @Path("/prefs") - @GenerateLink(rel = LINK_REL_UPDATE_PREFERENCES) - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - public Map updatePreferences(@Required Map update) throws NotFoundException, - ServerException, - ConflictException { - if (update == null || update.isEmpty()) { - throw new ConflictException("Preferences to update required"); - } - - String userId = currentUser().getUserId(); - // Keep the lock in a variable so it isn't garbage collected while in use - Lock lock = preferencesUpdateLocksByUser.get(userId); - lock.lock(); - try { - final Map preferences = preferenceDao.getPreferences(userId); - preferences.putAll(update); - preferenceDao.setPreferences(currentUser().getUserId(), preferences); - return preferences; - } finally { - lock.unlock(); - } - } - - /** - * Removes attributes with given names from current user profile. - * If names are {@code null} - all attributes will be removed - * - * @param attrNames - * attributes names to remove - * @throws ConflictException - * when given list of attributes names is {@code null} - * @throws ServerException - * when some error occurred while retrieving/updating profile - */ - @ApiOperation(value = "Remove attributes of a current user", - notes = "Remove attributes of a current user", - position = 6) - @ApiResponses(value = { - @ApiResponse(code = 204, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 409, message = "Attributes names required"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - @DELETE - @Path("/attributes") - @GenerateLink(rel = LINK_REL_REMOVE_ATTRIBUTES) - @Consumes(APPLICATION_JSON) - public void removeAttributes(@ApiParam(value = "Attributes", required = true) - @Required - @Description("Attributes names to remove") - List attrNames) throws NotFoundException, ServerException, ConflictException { - final Profile currentProfile = profileDao.getById(currentUser().getUserId()); - if (attrNames == null) { - currentProfile.getAttributes().clear(); - } else { - for (String attributeName : attrNames) { - currentProfile.getAttributes().remove(attributeName); - } - } - profileDao.update(currentProfile); - } - - /** - * Removes preferences with given name from current user profile. - * If names are {@code null} - all preferences will be removed - * - * @param names - * preferences names to remove - * @throws ServerException - * when some error occurred while retrieving/updating profile - * @see #removeAttributes(List) - */ - @ApiOperation(value = "Remove profile references of a current user", - notes = "Remove profile references of a current user", - position = 7) - @ApiResponses(value = { - @ApiResponse(code = 204, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 409, message = "Preferences names required"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - @DELETE - @Path("/prefs") - @GenerateLink(rel = LINK_REL_REMOVE_PREFERENCES) - @Consumes(APPLICATION_JSON) - public void removePreferences(@ApiParam(value = "Preferences to remove", required = true) - @Required - List names) throws ServerException, NotFoundException { - String userId = currentUser().getUserId(); - if (names == null) { - preferenceDao.remove(userId); - } else { - // Keep the lock in a variable so it isn't garbage collected while in use - Lock lock = preferencesUpdateLocksByUser.get(userId); - lock.lock(); - try { - final Map preferences = preferenceDao.getPreferences(userId); - for (String name : names) { - preferences.remove(name); - } - preferenceDao.setPreferences(userId, preferences); - } finally { - lock.unlock(); - } - } - } - - /** - * Converts {@link Profile} to {@link ProfileDescriptor} - */ - /* package-private used in tests*/ProfileDescriptor toDescriptor(Profile profile) { - final UriBuilder uriBuilder = getServiceContext().getServiceUriBuilder(); - final List links = new LinkedList<>(); - links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone() - .path(getClass(), "getCurrent") - .build() - .toString(), - null, - APPLICATION_JSON, - LINK_REL_GET_CURRENT_USER_PROFILE)); - links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone() - .path(getClass(), "getById") - .build(profile.getId()) - .toString(), - null, - APPLICATION_JSON, - LINK_REL_GET_USER_PROFILE_BY_ID)); - links.add(LinksHelper.createLink(HttpMethod.POST, - uriBuilder.clone() - .path(getClass(), "updateCurrent") - .build() - .toString(), - APPLICATION_JSON, - APPLICATION_JSON, - LINK_REL_UPDATE_CURRENT_USER_PROFILE)); - links.add(LinksHelper.createLink(HttpMethod.POST, - uriBuilder.clone() - .path(getClass(), "updatePreferences") - .build() - .toString(), - APPLICATION_JSON, - APPLICATION_JSON, - LINK_REL_UPDATE_PREFERENCES)); - links.add(LinksHelper.createLink(HttpMethod.GET, - uriBuilder.clone() - .path(getClass(), "getById") - .build(profile.getId()) - .toString(), - null, - APPLICATION_JSON, - LINK_REL_GET_USER_PROFILE_BY_ID)); - links.add(LinksHelper.createLink(HttpMethod.POST, - uriBuilder.clone() - .path(getClass(), "update") - .build(profile.getId()) - .toString(), - APPLICATION_JSON, - APPLICATION_JSON, - LINK_REL_UPDATE_USER_PROFILE_BY_ID)); - return DtoFactory.newDto(ProfileDescriptor.class) - .withId(profile.getId()) - .withUserId(profile.getUserId()) - .withAttributes(profile.getAttributes()) - .withLinks(links); - } - - private Subject currentUser() { - return EnvironmentContext.getCurrent().getSubject(); - } - - private void logEventUserUpdateProfile(User user, Map attributes) { - final Set emails = new HashSet<>(user.getAliases()); - emails.add(user.getEmail()); - - LOG.info("EVENT#user-update-profile# USER#{}# FIRSTNAME#{}# LASTNAME#{}# COMPANY#{}# PHONE#{}# JOBTITLE#{}# EMAILS#{}# USER-ID#{}#", - user.getEmail(), - nullToEmpty(attributes.get("firstName")), - nullToEmpty(attributes.get("lastName")), - nullToEmpty(attributes.get("employer")), - nullToEmpty(attributes.get("phone")), - nullToEmpty(attributes.get("jobtitle")), - user.getAliases(), - user.getId()); - } -} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserService.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserService.java index 6ba7f67405..a47fff2dd7 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserService.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserService.java @@ -20,15 +20,14 @@ import com.google.common.collect.ImmutableMap; import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.core.ForbiddenException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.UnauthorizedException; +import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.core.rest.Service; import org.eclipse.che.api.core.rest.annotations.GenerateLink; -import org.eclipse.che.api.core.rest.annotations.Required; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.shared.dto.UserDescriptor; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.shared.dto.UserDto; import org.eclipse.che.commons.env.EnvironmentContext; import javax.inject.Inject; @@ -46,301 +45,162 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.util.Map; -import static com.google.common.base.Strings.isNullOrEmpty; import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.Response.Status.CREATED; -import static javax.ws.rs.core.Response.status; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_CREATE_USER; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_CURRENT_USER; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_BY_EMAIL; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_REMOVE_USER_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_PASSWORD; -import static org.eclipse.che.api.user.server.DtoConverter.toDescriptor; -import static org.eclipse.che.api.user.server.LinksInjector.injectLinks; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_USER; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_CURRENT_USER_PASSWORD; +import static org.eclipse.che.api.user.server.Constants.LINK_REL_USER; +import static org.eclipse.che.api.user.server.DtoConverter.asDto; /** - * Provides REST API for user management + * User REST API. * - * @author Eugene Voevodin + * @author Yevhenii Voevodin * @author Anton Korneta */ -@Api(value = "/user", description = "User manager") @Path("/user") +@Api(value = "/user", description = "User REST API") public class UserService extends Service { public static final String USER_SELF_CREATION_ALLOWED = "user.self.creation.allowed"; private final UserManager userManager; private final TokenValidator tokenValidator; - private final UserNameValidator userNameValidator; + private final UserLinksInjector linksInjector; + private final UserValidator userValidator; private final boolean userSelfCreationAllowed; @Inject public UserService(UserManager userManager, TokenValidator tokenValidator, - UserNameValidator userNameValidator, + UserValidator userNameValidator, + UserLinksInjector linksInjector, @Named(USER_SELF_CREATION_ALLOWED) boolean userSelfCreationAllowed) { this.userManager = userManager; + this.linksInjector = linksInjector; this.tokenValidator = tokenValidator; - this.userNameValidator = userNameValidator; + this.userValidator = userNameValidator; this.userSelfCreationAllowed = userSelfCreationAllowed; } - /** - * Creates new user and profile. - * - *

User will be created from {@code token} parameter or from {@code userDescriptor} - * when {@code token} is null - * - * @param token - * authentication token - * @param isTemporary - * if it is {@code true} creates temporary user - * @return entity of created user - * @throws ForbiddenException - * when the user is not the system admin, or self creation is disabled - * @throws BadRequestException - * when {@code userDescriptor} is invalid - * @throws UnauthorizedException - * when token is null - * @throws ConflictException - * when token is not valid - * @throws ServerException - * when some error occurred while persisting user or user profile - * @see UserDescriptor - * @see #getCurrent() - * @see #updatePassword(String) - * @see #getById(String) - * @see #getByAlias(String) - * @see #remove(String) - */ @POST - @Path("/create") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) - @GenerateLink(rel = LINK_REL_CREATE_USER) - @ApiOperation(value = "Create a new user", - notes = "Create a new user in the system. There are two ways to create a user: " + - "through a regular registration workflow when auth token is sent to user's mailbox" + - "and directly with predefined name and password. ", - response = UserDescriptor.class) - @ApiResponses({@ApiResponse(code = 201, message = "Created"), + @GenerateLink(rel = LINK_REL_USER) + @ApiOperation(value = "Create a new user", response = UserDto.class) + @ApiResponses({@ApiResponse(code = 201, message = "User successfully created, response contains created entity"), @ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"), @ApiResponse(code = 401, message = "Missed token parameter"), - @ApiResponse(code = 403, message = "Invalid or missing request parameters"), - @ApiResponse(code = 409, message = "Invalid token"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - public Response create(@ApiParam(value = "New user") - UserDescriptor userDescriptor, - @ApiParam(value = "Authentication token") + @ApiResponse(code = 500, message = "Couldn't create user due to internal server error")}) + public Response create(@ApiParam("New user") + UserDto userDto, + @ApiParam("Authentication token") @QueryParam("token") String token, - @ApiParam(value = "User type") + @ApiParam("User type") @QueryParam("temporary") @DefaultValue("false") - Boolean isTemporary) throws ForbiddenException, - BadRequestException, + Boolean isTemporary) throws BadRequestException, UnauthorizedException, ConflictException, - ServerException, - NotFoundException { - final User user = isNullOrEmpty(token) ? fromEntity(userDescriptor) : fromToken(token); - if (!userNameValidator.isValidUserName(user.getName())) { - throw new BadRequestException("Username must contain only letters and digits"); - } - userManager.create(user, isTemporary); - return status(CREATED).entity(injectLinks(toDescriptor(user), getServiceContext())).build(); + ServerException { + final User newUser = token == null ? userDto : tokenValidator.validateToken(token); + userValidator.checkUser(newUser); + return Response.status(CREATED) + .entity(linksInjector.injectLinks(asDto(userManager.create(newUser, isTemporary)), getServiceContext())) + .build(); } - /** - * Returns {@link UserDescriptor} of current user. - * - * @return entity of current user. - * @throws NotFoundException - * when current user not found - * @throws ServerException - * when some error occurred while retrieving current user - */ @GET - @GenerateLink(rel = LINK_REL_GET_CURRENT_USER) @Produces(APPLICATION_JSON) - @ApiOperation(value = "Get current user", - notes = "Get user currently logged in the system", - response = UserDescriptor.class, - position = 2) - @ApiResponses({@ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - public UserDescriptor getCurrent() throws NotFoundException, ServerException { - final User user = userManager.getById(currentUserId()); - return injectLinks(toDescriptor(user), getServiceContext()); + @GenerateLink(rel = LINK_REL_CURRENT_USER) + @ApiOperation("Get logged in user") + @ApiResponses({@ApiResponse(code = 200, message = "The response contains currently logged in user entity"), + @ApiResponse(code = 500, message = "Couldn't get user due to internal server error")}) + public UserDto getCurrent() throws NotFoundException, ServerException { + final User user = userManager.getById(userId()); + return linksInjector.injectLinks(asDto(user), getServiceContext()); } - /** - * Updates current user password. - * - * @param password - * new user password - * @throws NotFoundException - * when current user not found - * @throws BadRequestException - * when given password is invalid - * @throws ServerException - * when some error occurred while updating profile - * @see UserDescriptor - */ @POST @Path("/password") - @GenerateLink(rel = LINK_REL_UPDATE_PASSWORD) @Consumes(APPLICATION_FORM_URLENCODED) - @ApiOperation(value = "Update password", - notes = "Update current password") - @ApiResponses({@ApiResponse(code = 204, message = "OK"), - @ApiResponse(code = 400, message = "Invalid password"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) + @GenerateLink(rel = LINK_REL_CURRENT_USER_PASSWORD) + @ApiOperation(value = "Update password of logged in user", + notes = "Password must contain at least 8 characters, " + + "passport must contain letters and digits") + @ApiResponses({@ApiResponse(code = 204, message = "Password successfully updated"), + @ApiResponse(code = 400, message = "Incoming password is invalid value." + + "Valid password must contain at least 8 character " + + "which are letters and digits"), + @ApiResponse(code = 500, message = "Couldn't update password due to internal server error")}) public void updatePassword(@ApiParam(value = "New password", required = true) @FormParam("password") String password) throws NotFoundException, BadRequestException, ServerException, ConflictException { + userValidator.checkPassword(password); - checkPassword(password); - - final User user = userManager.getById(currentUserId()); + final UserImpl user = new UserImpl(userManager.getById(userId())); user.setPassword(password); userManager.update(user); } - /** - * Returns status 200 and {@link UserDescriptor} built from user with given {@code id} - * or status 404 when user with given {@code id} was not found. - * - * @param id - * identifier to search user - * @return entity of found user - * @throws NotFoundException - * when user with given identifier doesn't exist - * @throws ServerException - * when some error occurred while retrieving user - * @see UserDescriptor - * @see #getByAlias(String) - */ @GET @Path("/{id}") - @GenerateLink(rel = LINK_REL_GET_USER_BY_ID) @Produces(APPLICATION_JSON) - @ApiOperation(value = "Get user by ID", - notes = "Get user by its ID in the system", - response = UserDescriptor.class) - @ApiResponses({@ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - public UserDescriptor getById(@ApiParam(value = "User ID") @PathParam("id") String id) throws NotFoundException, - ServerException { + @GenerateLink(rel = LINK_REL_USER) + @ApiOperation("Get user by identifier") + @ApiResponses({@ApiResponse(code = 200, message = "The response contains requested user entity"), + @ApiResponse(code = 404, message = "User with requested identifier not found"), + @ApiResponse(code = 500, message = "Impossible to get user due to internal server error")}) + public UserDto getById(@ApiParam("User identifier") + @PathParam("id") + String id) throws NotFoundException, ServerException { final User user = userManager.getById(id); - return injectLinks(toDescriptor(user), getServiceContext()); + return linksInjector.injectLinks(asDto(user), getServiceContext()); } - /** - * Returns status 200 and {@link UserDescriptor} built from user with given {@code alias} - * or status 404 when user with given {@code alias} was not found. - * - * @param alias - * alias to search user - * @return entity of found user - * @throws NotFoundException - * when user with given alias doesn't exist - * @throws ServerException - * when some error occurred while retrieving user - * @throws BadRequestException - * when alias parameter is missing - * @see UserDescriptor - * @see #getById(String) - * @see #remove(String) - */ @GET @Path("/find") - @GenerateLink(rel = LINK_REL_GET_USER_BY_EMAIL) @Produces(APPLICATION_JSON) - @ApiOperation(value = "Get user by alias", - notes = "Get user by alias", - response = UserDescriptor.class) - @ApiResponses({@ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 400, message = "Missed alias parameter"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - public UserDescriptor getByAlias(@ApiParam(value = "User alias", required = true) - @QueryParam("alias") - @Required String alias) throws NotFoundException, - ServerException, - BadRequestException { - if (alias == null) { - throw new BadRequestException("Missed parameter alias"); + @GenerateLink(rel = LINK_REL_USER) + @ApiOperation("Get user by email or name") + @ApiResponses({@ApiResponse(code = 200, message = "The response contains requested user entity"), + @ApiResponse(code = 404, message = "User with requested email/name not found"), + @ApiResponse(code = 500, message = "Impossible to get user due to internal server error")}) + public UserDto find(@ApiParam("User email, if it is set then name shouldn't be") + @QueryParam("email") + String email, + @ApiParam("User name, if is is set then email shouldn't be") + @QueryParam("name") + String name) throws NotFoundException, + ServerException, + BadRequestException { + if (email == null && name == null) { + throw new BadRequestException("Missed user's email or name"); } - final User user = userManager.getByAlias(alias); - return injectLinks(toDescriptor(user), getServiceContext()); + if (email != null && name != null) { + throw new BadRequestException("Expected either user's email or name, while both values received"); + } + final User user = name == null ? userManager.getByEmail(email) : userManager.getByName(name); + return linksInjector.injectLinks(asDto(user), getServiceContext()); } - /** - * Removes user with given identifier. - * - * @param id - * identifier to remove user - * @throws NotFoundException - * when user with given identifier doesn't exist - * @throws ServerException - * when some error occurred while removing user - * @throws ConflictException - * when some error occurred while removing user - */ @DELETE @Path("/{id}") - @GenerateLink(rel = LINK_REL_REMOVE_USER_BY_ID) - @ApiOperation(value = "Delete user", - notes = "Delete a user from the system") - @ApiResponses({@ApiResponse(code = 204, message = "Deleted"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 409, message = "Impossible to remove user"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - public void remove(@ApiParam(value = "User ID") @PathParam("id") String id) throws NotFoundException, - ServerException, - ConflictException { + @GenerateLink(rel = LINK_REL_USER) + @ApiOperation("Delete user") + @ApiResponses({@ApiResponse(code = 204, message = "User successfully removed"), + @ApiResponse(code = 409, message = "Couldn't remove user due to conflict(e.g. it has related entities)"), + @ApiResponse(code = 500, message = "Couldn't remove user due to internal server error")}) + public void remove(@ApiParam("User identifier") + @PathParam("id") + String id) throws ServerException, ConflictException { userManager.remove(id); } - /** - * Get user by name. - * - * @param name - * user name - * @return found user - * @throws NotFoundException - * when user with given name doesn't exist - * @throws ServerException - * when some error occurred while retrieving user - */ - @GET - @Path("/name/{name}") - @GenerateLink(rel = "get user by name") - @Produces(APPLICATION_JSON) - @ApiOperation(value = "Get user by name", - notes = "Get user by its name in the system") - @ApiResponses({@ApiResponse(code = 200, message = "OK"), - @ApiResponse(code = 404, message = "Not Found"), - @ApiResponse(code = 500, message = "Internal Server Error")}) - public UserDescriptor getByName(@ApiParam(value = "User email") - @PathParam("name") - String name) throws NotFoundException, ServerException { - final User user = userManager.getByName(name); - return injectLinks(toDescriptor(user), getServiceContext()); - } - - /** - * Get setting of user service - */ @GET @Path("/settings") @Produces(APPLICATION_JSON) @@ -348,64 +208,7 @@ public class UserService extends Service { return ImmutableMap.of(USER_SELF_CREATION_ALLOWED, Boolean.toString(userSelfCreationAllowed)); } - private User fromEntity(UserDescriptor userDescriptor) throws BadRequestException { - if (userDescriptor == null) { - throw new BadRequestException("User Descriptor required"); - } - if (isNullOrEmpty(userDescriptor.getName())) { - throw new BadRequestException("User name required"); - } - if (isNullOrEmpty(userDescriptor.getEmail())) { - throw new BadRequestException("User email required"); - } - final User user = new User().withName(userDescriptor.getName()) - .withEmail(userDescriptor.getEmail()); - if (userDescriptor.getPassword() != null) { - checkPassword(userDescriptor.getPassword()); - user.setPassword(userDescriptor.getPassword()); - } - return user; - } - - private User fromToken(String token) throws UnauthorizedException, ConflictException { - return tokenValidator.validateToken(token); - } - - /** - * Checks user password conforms some rules: - *

    - *
  • Not null - *
  • Must be at least 8 character length - *
  • Must contain at least one letter and one digit - *
- * - * @param password - * user's password - * @throws BadRequestException - * when password violates any rule - */ - private void checkPassword(String password) throws BadRequestException { - if (password == null) { - throw new BadRequestException("Password required"); - } - if (password.length() < 8) { - throw new BadRequestException("Password should contain at least 8 characters"); - } - int numOfLetters = 0; - int numOfDigits = 0; - for (char passwordChar : password.toCharArray()) { - if (Character.isDigit(passwordChar)) { - numOfDigits++; - } else if (Character.isLetter(passwordChar)) { - numOfLetters++; - } - } - if (numOfDigits == 0 || numOfLetters == 0) { - throw new BadRequestException("Password should contain letters and digits"); - } - } - - private String currentUserId() { + private static String userId() { return EnvironmentContext.getCurrent().getSubject().getUserId(); } } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserNameValidator.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserValidator.java similarity index 51% rename from wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserNameValidator.java rename to wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserValidator.java index 871ed552a4..45c132f0f1 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserNameValidator.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/UserValidator.java @@ -10,8 +10,10 @@ *******************************************************************************/ package org.eclipse.che.api.user.server; +import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.commons.lang.NameGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,13 +21,17 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.util.regex.Pattern; +import static com.google.common.base.Strings.isNullOrEmpty; + +// TODO extract normalization code from the validator as it is not related to the validation at all /** * Utils for username validation and normalization. * * @author Mihail Kuznyetsov + * @author Yevhenii Voevodin */ -public class UserNameValidator { - private static final Logger LOG = LoggerFactory.getLogger(UserNameValidator.class); +public class UserValidator { + private static final Logger LOG = LoggerFactory.getLogger(UserValidator.class); private static final Pattern ILLEGAL_USERNAME_CHARACTERS = Pattern.compile("[^a-zA-Z0-9]"); private static final Pattern VALID_USERNAME = Pattern.compile("^[a-zA-Z0-9]*"); @@ -33,19 +39,74 @@ public class UserNameValidator { private final UserManager userManager; @Inject - public UserNameValidator (UserManager userManager) { + public UserValidator(UserManager userManager) { this.userManager = userManager; } + /** + * Checks whether given user is valid. + * + * @param user + * user to check + * @throws BadRequestException + * when user is not valid + */ + public void checkUser(User user) throws BadRequestException { + if (user == null) { + throw new BadRequestException("User required"); + } + if (isNullOrEmpty(user.getName())) { + throw new BadRequestException("User name required"); + } + if (!isValidName(user.getName())) { + throw new BadRequestException("Username must contain only letters and digits"); + } + if (isNullOrEmpty(user.getEmail())) { + throw new BadRequestException("User email required"); + } + if (user.getPassword() != null) { + checkPassword(user.getPassword()); + } + } + + /** + * Checks whether password is ok. + * + * @param password + * password to check + * @throws BadRequestException + * when password is not valid + */ + public void checkPassword(String password) throws BadRequestException { + if (password == null) { + throw new BadRequestException("Password required"); + } + if (password.length() < 8) { + throw new BadRequestException("Password should contain at least 8 characters"); + } + int numOfLetters = 0; + int numOfDigits = 0; + for (char passwordChar : password.toCharArray()) { + if (Character.isDigit(passwordChar)) { + numOfDigits++; + } else if (Character.isLetter(passwordChar)) { + numOfLetters++; + } + } + if (numOfDigits == 0 || numOfLetters == 0) { + throw new BadRequestException("Password should contain letters and digits"); + } + } + /** * Validate name, if it doesn't contain illegal characters * * @param name - * username + * username * @return true if valid name, false otherwise */ - public boolean isValidUserName(String name) { - return VALID_USERNAME.matcher(name).matches(); + public boolean isValidName(String name) { + return name != null && VALID_USERNAME.matcher(name).matches(); } /** @@ -54,7 +115,7 @@ public class UserNameValidator { * Also ensures username is unique, if not, adds digits to it's end. * * @param name - * username + * username * @return username without illegal characters */ public String normalizeUserName(String name) throws ServerException { @@ -67,7 +128,7 @@ public class UserNameValidator { candidate = normalized.isEmpty() ? NameGenerator.generate("username", 4) : normalized + String.valueOf(i++); } } catch (ServerException e) { - LOG.warn("Error occured during username normalization", e); + LOG.warn("Error occurred during username normalization", e); throw e; } return candidate; @@ -78,8 +139,6 @@ public class UserNameValidator { userManager.getByName(username); } catch (NotFoundException e) { return false; - } catch (ServerException e) { - throw e; } return true; } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserDao.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserDao.java deleted file mode 100644 index 19eeea893a..0000000000 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserDao.java +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.user.server.dao; - -import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.core.NotFoundException; -import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.core.UnauthorizedException; - -/** - * DAO interface offers means to perform CRUD operations with {@link User} data. The implementation is not - * required to be responsible for persistent layer data dto integrity. It simply transfers data from one layer to another, so if - * you're going to call any of implemented methods it is considered that all needed verifications are already done.

- * Note: This particularly does not mean that method call will not make any inconsistency, but this - * mean that such kind of inconsistencies are expected by design and may be treated further.

- */ -public interface UserDao { - - /** - * Authenticate user. - * - * @param alias - * user name or alias - * @param password - * password - * @return user id when authentication success - * @throws UnauthorizedException - * when authentication failed or no such user exists - * @throws ServerException - * when any other error occurs - * - */ - String authenticate(String alias, String password) throws UnauthorizedException, ServerException; - - /** - * Adds user to persistent layer. - * - * @param user - * - POJO representation of user entity - * @throws ConflictException - * when given user cannot be created - * @throws ServerException - * when any other error occurs - */ - void create(User user) throws ConflictException, ServerException; - - /** - * Updates already present in persistent layer user. - * - * @param user - * POJO representation of user entity - * @throws NotFoundException - * when user is not found - * @throws ConflictException - * when given user cannot be updated - * @throws ServerException - * when any other error occurs - * - */ - void update(User user) throws NotFoundException, ServerException, ConflictException; - - /** - * Removes user from persistent layer by his identifier. - * - * @param id - * user identifier - * @throws ConflictException - * when given user cannot be deleted - * @throws ServerException - * when any other error occurs - */ - void remove(String id) throws NotFoundException, ServerException, ConflictException; - - /** - * Gets user from persistent layer by any of his aliases - * - * @param alias - * user name or alias - * @return user POJO - * @throws NotFoundException - * when user doesn't exist - * @throws ServerException - * when any other error occurs - */ - User getByAlias(String alias) throws NotFoundException, ServerException; - - /** - * Gets user from persistent layer by his identifier - * - * @param id - * user identifier - * @return user POJO - * @throws NotFoundException - * when user doesn't exist - * @throws ServerException - * when any other error occurs - */ - User getById(String id) throws NotFoundException, ServerException; - - /** - * Gets user from persistent layer by his username - * - * @param userName - * user name - * @return user POJO - * @throws NotFoundException - * when user doesn't exist - * @throws ServerException - * when any other error occurs - */ - User getByName(String userName) throws NotFoundException, ServerException; -} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserProfileDao.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserProfileDao.java deleted file mode 100644 index 2f75d2e571..0000000000 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/UserProfileDao.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.user.server.dao; - - -import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.core.NotFoundException; -import org.eclipse.che.api.core.ServerException; - -/** - * DAO interface offers means to perform CRUD operations with {@link Profile} data. - * - * @author Eugene Voevodin - * @author Max Shaposhnik - */ -public interface UserProfileDao { - - /** - * Adds profile to persistent layer. - * - * @param profile - * profile to setPreferences - */ - void create(Profile profile) throws ConflictException, ServerException; - - /** - * Updates already present in persistent layer profile. - * - * @param profile - * profile to update - */ - void update(Profile profile) throws NotFoundException, ServerException; - - /** - * Removes profile from persistent layer. - * - * @param id - * profile identifier - */ - void remove(String id) throws NotFoundException, ServerException; - - /** - * Gets profile from persistent layer. - * - * @param id - * profile identifier - * @return profile with given {@code id} - * @throws org.eclipse.che.api.core.NotFoundException - * when profile doesn't exist - */ - Profile getById(String id) throws NotFoundException, ServerException; -} \ No newline at end of file diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/Profile.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/model/impl/ProfileImpl.java similarity index 55% rename from wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/Profile.java rename to wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/model/impl/ProfileImpl.java index 428eee8741..a8d5dbd412 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/Profile.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/model/impl/ProfileImpl.java @@ -8,57 +8,48 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.user.server.dao; +package org.eclipse.che.api.user.server.model.impl; + +import org.eclipse.che.api.core.model.user.Profile; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** - * @author Eugene Voevodin + * Data object for the {@link Profile}. + * + * @author Yevhenii Voevodin */ -public class Profile { +public class ProfileImpl implements Profile { private String id; - private String userId; private Map attributes; - public Profile() {} - - public Profile(String id) { + public ProfileImpl(String id) { this.id = id; - this.userId = id; } - public String getId() { + public ProfileImpl(String id, Map attributes) { + this.id = id; + if (attributes != null) { + this.attributes = new HashMap<>(attributes); + } + } + + public ProfileImpl(Profile profile) { + this(profile.getUserId(), profile.getAttributes()); + } + + @Override + public String getUserId() { return id; } - public void setId(String id) { - this.id = id; - } - - public Profile withId(String id) { - this.id = id; - return this; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - public Profile withUserId(String userId) { - this.userId = userId; - return this; - } - + @Override public Map getAttributes() { if (attributes == null) { - attributes = new HashMap<>(); + this.attributes = new HashMap<>(); } return attributes; } @@ -67,31 +58,31 @@ public class Profile { this.attributes = attributes; } - public Profile withAttributes(Map attributes) { - this.attributes = attributes; - return this; - } - @Override public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof Profile)) { + if (!(obj instanceof ProfileImpl)) { return false; } - final Profile other = (Profile)obj; - return Objects.equals(id, other.id) && - Objects.equals(userId, other.userId) && - Objects.equals(getAttributes(), other.getAttributes()); + final ProfileImpl that = (ProfileImpl)obj; + return Objects.equals(id, that.id) && getAttributes().equals(that.getAttributes()); } @Override public int hashCode() { int hash = 7; hash = 31 * hash + Objects.hashCode(id); - hash = 31 * hash + Objects.hashCode(userId); - hash = 31 * hash + Objects.hashCode(attributes); + hash = 31 * hash + getAttributes().hashCode(); return hash; } + + @Override + public String toString() { + return "ProfileImpl{" + + "id='" + id + '\'' + + ", attributes=" + attributes + + '}'; + } } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/User.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/model/impl/UserImpl.java similarity index 58% rename from wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/User.java rename to wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/model/impl/UserImpl.java index bb2f59b7b9..951a822e60 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/User.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/model/impl/UserImpl.java @@ -8,16 +8,21 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.user.server.dao; +package org.eclipse.che.api.user.server.model.impl; + +import org.eclipse.che.api.core.model.user.User; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; /** - * @author Eugene Voevodin + * Data object for the {@link User}. + * + * @author Yevhenii Voevodin */ -public class User { +public class UserImpl implements User { private String id; private String email; @@ -25,19 +30,69 @@ public class User { private String password; private List aliases; + public UserImpl(String id) { + this.id = id; + } + + public UserImpl(String id, String email, String name) { + this.id = id; + this.email = email; + this.name = name; + } + + public UserImpl(String id, + String email, + String name, + String password, + Collection aliases) { + this(id, email, name); + this.password = password; + if (aliases != null) { + this.aliases = new ArrayList<>(aliases); + } + } + + public UserImpl(User user) { + this(user.getId(), + user.getEmail(), + user.getName(), + user.getPassword(), + user.getAliases()); + } + + @Override public String getId() { return id; } - public void setId(String id) { - this.id = id; + @Override + public String getEmail() { + return email; } - public User withId(String id) { - this.id = id; - return this; + public void setEmail(String email) { + this.email = email; } + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override public List getAliases() { if (aliases == null) { aliases = new ArrayList<>(); @@ -49,64 +104,20 @@ public class User { this.aliases = aliases; } - public User withAliases(List aliases) { - this.aliases = aliases; - return this; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public User withEmail(String email) { - this.email = email; - return this; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public User withPassword(String password) { - this.password = password; - return this; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public User withName(String name) { - this.name = name; - return this; - } - @Override public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof User)) { + if (!(obj instanceof UserImpl)) { return false; } - final User other = (User)obj; - return Objects.equals(id, other.id) && - Objects.equals(email, other.email) && - Objects.equals(password, other.password) && - Objects.equals(name, other.name) && - getAliases().equals(other.getAliases()); + final UserImpl that = (UserImpl)obj; + return Objects.equals(id, that.id) + && Objects.equals(email, that.email) + && Objects.equals(name, that.name) + && Objects.equals(password, that.password) + && getAliases().equals(that.getAliases()); } @Override @@ -114,9 +125,20 @@ public class User { int hash = 7; hash = 31 * hash + Objects.hashCode(id); hash = 31 * hash + Objects.hashCode(email); - hash = 31 * hash + Objects.hashCode(password); hash = 31 * hash + Objects.hashCode(name); + hash = 31 * hash + Objects.hashCode(password); hash = 31 * hash + getAliases().hashCode(); return hash; } + + @Override + public String toString() { + return "UserImpl{" + + "id='" + id + '\'' + + ", email='" + email + '\'' + + ", name='" + name + '\'' + + ", password='" + password + '\'' + + ", aliases=" + aliases + + '}'; + } } diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/PreferenceDao.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/PreferenceDao.java similarity index 89% rename from wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/PreferenceDao.java rename to wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/PreferenceDao.java index ea1b39258b..409f9993e5 100644 --- a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/dao/PreferenceDao.java +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/PreferenceDao.java @@ -8,9 +8,8 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.api.user.server.dao; +package org.eclipse.che.api.user.server.spi; -import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import java.util.Map; @@ -31,12 +30,10 @@ public interface PreferenceDao { * new preferences, if preferences are empty - removes user preferences * @throws NullPointerException * when preferences or userId is null - * @throws NotFoundException - * when user with given identifier doesn't exist * @throws ServerException * when any other error occurs */ - void setPreferences(String userId, Map preferences) throws ServerException, NotFoundException; + void setPreferences(String userId, Map preferences) throws ServerException; /** * Gets user preferences. @@ -45,7 +42,7 @@ public interface PreferenceDao { *
{@code
      *      Map prefs = dao.getPreferences("user123");
      *      prefs.put("key", "secret");
-     *      dao.setPreferences("user123", prefs);
+     *      spi.setPreferences("user123", prefs);
      * }
* * @param userId @@ -63,7 +60,7 @@ public interface PreferenceDao { * *

Note that this method must always return upgradable map, thus it may be used as: *

{@code
-     *      Map prefs = dao.getPreferences("user123", ".*key.*");
+     *      Map prefs = spi.getPreferences("user123", ".*key.*");
      *      prefs.put("new-key", "secret");
      *      prefs.setPreferences("user123", prefs);
      * }
diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/ProfileDao.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/ProfileDao.java new file mode 100644 index 0000000000..17b7fb4b7e --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/ProfileDao.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server.spi; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.Profile; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; + +/** + * Data access object contract for {@link Profile}. + * + * @author Yevhenii Voevodin + */ +public interface ProfileDao { + + /** + * Creates user profile. + * + * @param profile + * new profile + * @throws NullPointerException + * when {@code profile} is null + * @throws ServerException + * when any error occurs + * @throws ConflictException + * when profile for user {@code profile.getUserId()} already exists + */ + void create(ProfileImpl profile) throws ServerException, ConflictException; + + /** + * Updates profile by replacing an existing entity with a new one. + * + * @param profile + * profile update + * @throws NullPointerException + * when {@code profile} is null + * @throws NotFoundException + * when profile with such id doesn't exist + * @throws ServerException + * when any other error occurs + */ + void update(ProfileImpl profile) throws NotFoundException, ServerException; + + /** + * Removes profile. + * + * @param id + * profile identifier + * @throws NullPointerException + * when {@code id} is null + * @throws ServerException + * when any other error occurs + */ + void remove(String id) throws ServerException; + + /** + * Finds profile by its id. + * + *

Due to {@link Profile#getEmail()} and {@link Profile#getUserId()} definition + * returned profile must contain profile owner's {@link User#getEmail() email} + * and {@link User#getId()} identifier. + * + * @param id + * profile identifier + * @return profile with given {@code id} + * @throws NullPointerException + * when {@code id} is null + * @throws NotFoundException + * when profile with such {@code id} doesn't exist + * @throws ServerException + * when any other error occurs + */ + ProfileImpl getById(String id) throws NotFoundException, ServerException; +} diff --git a/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/UserDao.java b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/UserDao.java new file mode 100644 index 0000000000..949371d452 --- /dev/null +++ b/wsmaster/che-core-api-user/src/main/java/org/eclipse/che/api/user/server/spi/UserDao.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server.spi; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.UnauthorizedException; +import org.eclipse.che.api.user.server.model.impl.UserImpl; + +/** + * Defines data access object contract for {@link UserImpl}. + * + *

The implementation is not required to be responsible for persistent layer + * data dto integrity. It simply transfers data from one layer to another, + * so if you're going to call any of implemented methods it is considered + * that all needed verifications are already done. + * + *

Note: This particularly does not mean that + * method call will not make any inconsistency, but this mean that + * such kind of inconsistencies are expected by design and may be treated further. + * + * @author Yevhenii Voevodin + */ +public interface UserDao { + + /** + * // TODO remove this method from spi + * Authenticates user. + * + * @param emailOrAliasOrName + * one of the user identifiers such as email/name/alias + * @param password + * password + * @return user identifier + * @throws NullPointerException + * when either {@code emailOrAliasOrName} or {@code password} is null + * @throws UnauthorizedException + * when user with such {@code aliasOrName} and {@code password} doesn't exist + * @throws ServerException + * when any other error occurs + */ + String authenticate(String emailOrAliasOrName, String password) throws UnauthorizedException, ServerException; + + /** + * Creates a new user. + * + * @param user + * user to create + * @throws NullPointerException + * when {@code user} is null + * @throws ConflictException + * when user with such id/alias/email/name already exists + * @throws ServerException + * when any other error occurs + */ + void create(UserImpl user) throws ConflictException, ServerException; + + /** + * Updates user by replacing an existing entity with a new one. + * + * @param user + * user to update + * @throws NullPointerException + * when {@code user} is null + * @throws NotFoundException + * when user with id {@code user.getId()} doesn't exist + * @throws ConflictException + * when any of the id/alias/email/name updated with a value + * which is not unique + * @throws ServerException + * when any other error occurs + */ + void update(UserImpl user) throws NotFoundException, ServerException, ConflictException; + + /** + * Removes user. + * + *

It is up to implementation to do cascade removing of dependent data or + * to forbid removing at all. + * + *

Note that this method doesn't throw any exception when + * user doesn't exist. + * + * @param id + * user identifier + * @throws NullPointerException + * when {@code id} is null + * @throws ConflictException + * when given user cannot be deleted + * @throws ServerException + * when any other error occurs + */ + void remove(String id) throws ServerException, ConflictException; + + /** + * Finds user by his alias. + * + *

This method doesn't work for user's email or name. + * If it is necessary to get user by name use {@link #getByName(String)} method instead. + * + * @param alias + * user name or alias + * @return user instance, never null + * @throws NullPointerException + * when {@code alias} is null + * @throws NotFoundException + * when user with given {@code alias} doesn't exist + * @throws ServerException + * when any other error occurs + */ + UserImpl getByAlias(String alias) throws NotFoundException, ServerException; + + /** + * Finds user by his identifier. + * + * @param id + * user identifier + * @return user instance, never null + * @throws NullPointerException + * when {@code id} is null + * @throws NotFoundException + * when user with given {@code id} doesn't exist + * @throws ServerException + * when any other error occurs + */ + UserImpl getById(String id) throws NotFoundException, ServerException; + + /** + * Finds user by his name. + * + * @param name + * user name + * @return user instance, never null + * @throws NullPointerException + * when {@code name} is null + * @throws NotFoundException + * when user with such {@code name} doesn't exist + * @throws ServerException + * when any other error occurs + */ + UserImpl getByName(String name) throws NotFoundException, ServerException; + + /** + * Finds user by his email. + * + * @param email + * user email + * @return user instance, never null + * @throws NullPointerException + * when {@code email} is null + * @throws NotFoundException + * when user with such {@code email} doesn't exist + * @throws ServerException + * when any other error occurs + */ + UserImpl getByEmail(String email) throws NotFoundException, ServerException; +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferenceManagerTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferenceManagerTest.java new file mode 100644 index 0000000000..97792de800 --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferenceManagerTest.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import com.google.common.collect.ImmutableMap; + +import org.eclipse.che.api.user.server.spi.PreferenceDao; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Arrays.asList; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * Tests for {@link PreferenceManager}. + * + * @author Yevhenii Voevodin. + */ +@Listeners(MockitoTestNGListener.class) +public class PreferenceManagerTest { + + @Mock + private PreferenceDao preferenceDao; + + @InjectMocks + private PreferenceManager preferenceManager; + + @Captor + private ArgumentCaptor> preferencesCaptor; + + @Test + public void shouldUseMergeStrategyForPreferencesUpdate() throws Exception { + // Preparing preferences + final Map existingPreferences = new HashMap<>(); + existingPreferences.put("pKey1", "pValue1"); + existingPreferences.put("pKey2", "pValue2"); + existingPreferences.put("pKey3", "pValue3"); + existingPreferences.put("pKey4", "pValue4"); + when(preferenceDao.getPreferences(any())).thenReturn(existingPreferences); + + // Updating preferences + final Map newPreferences = new HashMap<>(); + newPreferences.put("pKey5", "pValue5"); + newPreferences.put("pKey1", "new-value"); + preferenceManager.update("user123", newPreferences); + + // Checking + verify(preferenceDao).setPreferences(anyString(), preferencesCaptor.capture()); + assertEquals(preferencesCaptor.getValue(), ImmutableMap.of("pKey1", "new-value", + "pKey2", "pValue2", + "pKey3", "pValue3", + "pKey4", "pValue4", + "pKey5", "pValue5")); + + } + + @Test + public void shouldRemoveSpecifiedPreferences() throws Exception { + // Preparing preferences + final Map existingPreferences = new HashMap<>(); + existingPreferences.put("pKey1", "pValue1"); + existingPreferences.put("pKey2", "pValue2"); + existingPreferences.put("pKey3", "pValue3"); + existingPreferences.put("pKey4", "pValue4"); + when(preferenceDao.getPreferences(any())).thenReturn(existingPreferences); + + // Removing + preferenceManager.remove("user123", asList("pKey1", "pKey5", "odd-pref-name")); + + // Checking + verify(preferenceDao).setPreferences(anyString(), preferencesCaptor.capture()); + assertEquals(preferencesCaptor.getValue(), ImmutableMap.of("pKey2", "pValue2", + "pKey3", "pValue3", + "pKey4", "pValue4")); + } + + @Test + public void shouldGetPreferencesByUser() throws Exception { + final Map preferences = ImmutableMap.of("name", "value"); + when(preferenceDao.getPreferences("user123")).thenReturn(preferences); + + assertEquals(preferenceManager.find("user123"), preferences); + } + + @Test + public void shouldGetPreferencesByUserAndFilter() throws Exception { + final Map preferences = ImmutableMap.of("name", "value"); + when(preferenceDao.getPreferences("user123", "name.*")).thenReturn(preferences); + + assertEquals(preferenceManager.find("user123", "name.*"), preferences); + } + + @Test(expectedExceptions = NullPointerException.class) + public void getPreferencesShouldThrowNpeWhenUserIdIsNull() throws Exception { + preferenceManager.find(null); + } + + @Test(expectedExceptions = NullPointerException.class) + public void getPreferencesByUserAndFilterShouldThrowNpeWhenUserIdIsNull() throws Exception { + preferenceManager.find(null, "name.*"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenRemovingPreferencesAndUserIdIsNull() throws Exception { + preferenceManager.remove(null); + } + + @Test + public void shouldRemoveUserPreferences() throws Exception { + preferenceManager.remove("user123"); + + verify(preferenceDao).remove("user123"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenSavePreferencesWithNullUser() throws Exception { + preferenceManager.save(null, Collections.emptyMap()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenSavePreferencesWithNullPreferences() throws Exception { + preferenceManager.save("user123", null); + } + + @Test + public void shouldSavePreferences() throws Exception { + preferenceManager.save("user123", Collections.emptyMap()); + + verify(preferenceDao).setPreferences("user123", Collections.emptyMap()); + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferencesServiceTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferencesServiceTest.java new file mode 100644 index 0000000000..ffa09a5d69 --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/PreferencesServiceTest.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import com.google.common.collect.ImmutableMap; +import com.jayway.restassured.response.Response; + +import org.eclipse.che.api.core.rest.ApiExceptionMapper; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.subject.Subject; +import org.eclipse.che.commons.subject.SubjectImpl; +import org.everrest.assured.EverrestJetty; +import org.everrest.core.Filter; +import org.everrest.core.GenericContainerRequest; +import org.everrest.core.RequestFilter; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.Map; + +import static com.jayway.restassured.RestAssured.given; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME; +import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD; +import static org.everrest.assured.JettyHttpServer.SECURE_PATH; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.assertEquals; + +/** + * Tests for {@link PreferencesService}. + * + * @author Yevhenii Voevodin + */ +@Listeners({EverrestJetty.class, MockitoTestNGListener.class}) +public class PreferencesServiceTest { + + @SuppressWarnings("unused") + private static final ApiExceptionMapper MAPPER = new ApiExceptionMapper(); + @SuppressWarnings("unused") + private static final EnvironmentFilter FILTER = new EnvironmentFilter(); + private static final Subject SUBJECT = new SubjectImpl("user", "user123", "token", false); + + @Mock(answer = Answers.RETURNS_MOCKS) + private PreferenceManager preferenceManager; + + @InjectMocks + private PreferencesService preferencesService; + + @Test + public void shouldFindPreferences() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 200); + verify(preferenceManager).find(SUBJECT.getUserId()); + } + + @Test + public void shouldFindPreferencesAndFilter() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/preferences?filter=.*github.*"); + + assertEquals(response.getStatusCode(), 200); + verify(preferenceManager).find(SUBJECT.getUserId(), ".*github.*"); + } + + @Test + public void shouldSavePreferences() throws Exception { + final Map preferences = ImmutableMap.of("pref1", "value1", + "pref2", "value2", + "pref3", "value3"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/json") + .body(preferences) + .when() + .post(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 204); + verify(preferenceManager).save(SUBJECT.getUserId(), preferences); + } + + @Test + public void shouldNotSavePreferencesWhenNothingSent() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/json") + .when() + .post(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 400); + } + + @Test + public void shouldUpdatePreferences() throws Exception { + final Map preferences = ImmutableMap.of("pref1", "value1", + "pref2", "value2", + "pref3", "value3"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/json") + .body(preferences) + .when() + .put(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 200); + verify(preferenceManager).update(SUBJECT.getUserId(), preferences); + } + + @Test + public void shouldNotUpdatePreferencesWhenNothingSent() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/json") + .when() + .put(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 400); + } + + @Test + public void shouldRemoveSpecifiedPreferences() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/json") + .body(asList("pref1", "pref2")) + .when() + .delete(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 204); + verify(preferenceManager).remove(SUBJECT.getUserId(), asList("pref1", "pref2")); + } + + @Test + public void shouldRemoveAllPreferencesIfNothingSent() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/json") + .when() + .delete(SECURE_PATH + "/preferences"); + + assertEquals(response.getStatusCode(), 204); + verify(preferenceManager).remove(SUBJECT.getUserId()); + } + + @Filter + public static class EnvironmentFilter implements RequestFilter { + public void doFilter(GenericContainerRequest request) { + EnvironmentContext.getCurrent().setSubject(SUBJECT); + } + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileLinksInjectorTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileLinksInjectorTest.java new file mode 100644 index 0000000000..bf536afa56 --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileLinksInjectorTest.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import com.google.common.collect.Sets; + +import org.eclipse.che.api.core.rest.ServiceContext; +import org.eclipse.che.api.user.shared.dto.ProfileDto; +import org.eclipse.che.commons.lang.Pair; +import org.eclipse.che.dto.server.DtoFactory; +import org.everrest.core.impl.uri.UriBuilderImpl; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * Tests for {@link ProfileLinksInjector}. + * + * @author Yevhenii Voevodin + */ +@Listeners(MockitoTestNGListener.class) +public class ProfileLinksInjectorTest { + + @Mock + private ServiceContext serviceContext; + + @InjectMocks + private ProfileLinksInjector linksInjector; + + @BeforeMethod + public void setUpContext() { + final UriBuilderImpl uriBuilder = new UriBuilderImpl(); + uriBuilder.uri("http://localhost:8080"); + when(serviceContext.getServiceUriBuilder()).thenReturn(uriBuilder); + when(serviceContext.getBaseUriBuilder()).thenReturn(uriBuilder); + } + + @Test + public void shouldInjectProfileLinks() throws Exception { + final ProfileDto profileDto = DtoFactory.newDto(ProfileDto.class) + .withUserId("user123") + .withEmail("user@codenvy.com"); + + linksInjector.injectLinks(profileDto, serviceContext); + + // [rel, method] pairs links + final Set> links = profileDto.getLinks() + .stream() + .map(link -> Pair.of(link.getMethod(), link.getRel())) + .collect(Collectors.toSet()); + final Set> expectedLinks + = new HashSet<>(asList(Pair.of("GET", Constants.LINK_REL_SELF), + Pair.of("GET", Constants.LINK_REL_CURRENT_PROFILE), + Pair.of("PUT", Constants.LINK_REL_PROFILE_ATTRIBUTES), + Pair.of("PUT", Constants.LINK_REL_CURRENT_PROFILE_ATTRIBUTES), + Pair.of("DELETE", Constants.LINK_REL_CURRENT_PROFILE_ATTRIBUTES))); + + assertEquals(links, expectedLinks, "Difference " + Sets.symmetricDifference(links, expectedLinks) + "\n"); + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileManagerTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileManagerTest.java new file mode 100644 index 0000000000..5587c25ae1 --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileManagerTest.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * Tests for {@link ProfileManager}. + * + * @author Yevhenii Voevodin + */ +@Listeners(MockitoTestNGListener.class) +public class ProfileManagerTest { + + @Mock + private ProfileDao profileDao; + + @InjectMocks + private ProfileManager profileManager; + + @Test + public void shouldGetProfileById() throws Exception { + final ProfileImpl profile = new ProfileImpl("user123"); + when(profileDao.getById(profile.getUserId())).thenReturn(profile); + + assertEquals(profile, profileManager.getById(profile.getUserId())); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenGettingProfileByNullId() throws Exception { + profileManager.getById(null); + } + + @Test + public void shouldCreateProfile() throws Exception { + final ProfileImpl profile = new ProfileImpl("user123"); + + profileManager.create(profile); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(ProfileImpl.class); + verify(profileDao).create(captor.capture()); + assertEquals(captor.getValue(), profile); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenCreatingNullProfile() throws Exception { + profileManager.create(null); + } + + @Test + public void shouldUpdateProfile() throws Exception { + final ProfileImpl profile = new ProfileImpl("user123"); + + profileManager.update(profile); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(ProfileImpl.class); + verify(profileDao).update(captor.capture()); + assertEquals(captor.getValue(), profile); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenUpdatingNull() throws Exception { + profileManager.update(null); + } + + @Test + public void shouldRemoveProfile() throws Exception { + profileManager.remove("user123"); + + verify(profileDao).remove("user123"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenRemovingNull() throws Exception { + profileManager.remove(null); + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileServiceTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileServiceTest.java new file mode 100644 index 0000000000..9e3a6f0ab2 --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/ProfileServiceTest.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import com.google.common.collect.ImmutableMap; +import com.jayway.restassured.response.Response; + +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.Profile; +import org.eclipse.che.api.core.rest.ApiExceptionMapper; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.shared.dto.ProfileDto; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.subject.Subject; +import org.eclipse.che.commons.subject.SubjectImpl; +import org.eclipse.che.dto.server.DtoFactory; +import org.everrest.assured.EverrestJetty; +import org.everrest.core.Filter; +import org.everrest.core.GenericContainerRequest; +import org.everrest.core.RequestFilter; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static com.jayway.restassured.RestAssured.given; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME; +import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD; +import static org.everrest.assured.JettyHttpServer.SECURE_PATH; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * Tests for {@link ProfileService}. + * + * @author Yevhenii Voevodin + */ +@Listeners({EverrestJetty.class, MockitoTestNGListener.class}) +public class ProfileServiceTest { + + @SuppressWarnings("unused") + private static final ApiExceptionMapper MAPPER = new ApiExceptionMapper(); + @SuppressWarnings("unused") + private static final EnvironmentFilter FILTER = new EnvironmentFilter(); + private static final Subject SUBJECT = new SubjectImpl("user", "user123", "token", false); + + @Mock(answer = Answers.RETURNS_MOCKS) + private ProfileManager profileManager; + + @Mock + private ProfileLinksInjector linksInjector; + + @Mock(answer = Answers.RETURNS_MOCKS) + private UserManager userManager; + + @Captor + private ArgumentCaptor profileCaptor; + + @InjectMocks + private ProfileService profileService; + + @BeforeMethod + public void setUp() throws NotFoundException, ServerException { + when(linksInjector.injectLinks(any(), any())).thenAnswer(inv -> inv.getArguments()[0]); + + when(profileManager.getById(SUBJECT.getUserId())).thenReturn(new ProfileImpl(SUBJECT.getUserId())); + } + + @Test + public void shouldGetCurrentProfile() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/profile"); + + assertEquals(response.getStatusCode(), 200); + final ProfileDto profileDto = unwrapDto(response, ProfileDto.class); + assertEquals(profileDto.getUserId(), SUBJECT.getUserId()); + } + + @Test + public void shouldGetProfileById() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/profile/" + SUBJECT.getUserId()); + + assertEquals(response.getStatusCode(), 200); + final ProfileDto profileDto = unwrapDto(response, ProfileDto.class); + assertEquals(profileDto.getUserId(), SUBJECT.getUserId()); + } + + @Test + public void shouldBeAbleToUpdateCurrentProfileAttributes() throws Exception { + final ImmutableMap attributes = ImmutableMap.of("attr1", "value1", + "attr2", "value2", + "attr3", "value3"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .body(attributes) + .put(SECURE_PATH + "/profile/attributes"); + + assertEquals(response.getStatusCode(), 200); + verify(profileManager).update(profileCaptor.capture()); + final Profile profile = profileCaptor.getValue(); + assertEquals(profile.getAttributes(), attributes); + } + + @Test + public void shouldNotUpdateCurrentProfileAttributesIfNothingSent() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .put(SECURE_PATH + "/profile/attributes"); + + assertEquals(response.getStatusCode(), 400); + } + + @Test + public void shouldBeAbleToUpdateAttributesOfSpecifiedProfile() throws Exception { + final ImmutableMap attributes = ImmutableMap.of("attr1", "value1", + "attr2", "value2", + "attr3", "value3"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .body(attributes) + .put(SECURE_PATH + "/profile/" + SUBJECT.getUserId() + "/attributes/"); + + assertEquals(response.getStatusCode(), 200); + verify(profileManager).update(profileCaptor.capture()); + final Profile profile = profileCaptor.getValue(); + assertEquals(profile.getAttributes(), attributes); + } + + @Test + public void shouldNotUpdateSpecifiedProfileAttributesIfNothingSent() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .put(SECURE_PATH + "/profile/" + SUBJECT.getUserId() + "/attributes/"); + + assertEquals(response.getStatusCode(), 400); + } + + @Test + public void shouldBeAbleToRemoveSpecifiedAttributes() throws Exception { + when(profileManager.getById(SUBJECT.getUserId())) + .thenReturn(new ProfileImpl(SUBJECT.getUserId(), + ImmutableMap.of("attr1", "value1", + "attr2", "value2", + "attr3", "value3"))); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .body(asList("attr1", "attr3")) + .delete(SECURE_PATH + "/profile/attributes"); + + assertEquals(response.getStatusCode(), 204); + verify(profileManager).update(profileCaptor.capture()); + final Profile profile = profileCaptor.getValue(); + assertEquals(profile.getAttributes(), ImmutableMap.of("attr2", "value2")); + } + + @Test + public void shouldRemoveAllAttributeIfNoSpecified() throws Exception { + when(profileManager.getById(SUBJECT.getUserId())) + .thenReturn(new ProfileImpl(SUBJECT.getUserId(), + ImmutableMap.of("attr1", "value1", + "attr2", "value2", + "attr3", "value3"))); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .delete(SECURE_PATH + "/profile/attributes"); + + assertEquals(response.getStatusCode(), 204); + verify(profileManager).update(profileCaptor.capture()); + final Profile profile = profileCaptor.getValue(); + assertTrue(profile.getAttributes().isEmpty()); + } + + @Filter + public static class EnvironmentFilter implements RequestFilter { + public void doFilter(GenericContainerRequest request) { + EnvironmentContext.getCurrent().setSubject(SUBJECT); + } + } + + private static T unwrapDto(Response response, Class dtoClass) { + return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass); + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserLinksInjectorTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserLinksInjectorTest.java new file mode 100644 index 0000000000..ad14853c9d --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserLinksInjectorTest.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server; + +import com.google.common.collect.Sets; + +import org.eclipse.che.api.core.rest.ServiceContext; +import org.eclipse.che.api.user.shared.dto.UserDto; +import org.eclipse.che.commons.lang.Pair; +import org.eclipse.che.dto.server.DtoFactory; +import org.everrest.core.impl.uri.UriBuilderImpl; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * Tests for {@link UserLinksInjector}. + * + * @author Yevhenii Voevodin + */ +@Listeners(MockitoTestNGListener.class) +public class UserLinksInjectorTest { + + @Mock + private ServiceContext serviceContext; + + @InjectMocks + private UserLinksInjector linksInjector; + + @BeforeMethod + public void setUpContext() { + final UriBuilderImpl uriBuilder = new UriBuilderImpl(); + uriBuilder.uri("http://localhost:8080"); + when(serviceContext.getServiceUriBuilder()).thenReturn(uriBuilder); + when(serviceContext.getBaseUriBuilder()).thenReturn(uriBuilder); + } + + @Test + public void shouldInjectLinks() throws Exception { + final UserDto userDto = DtoFactory.newDto(UserDto.class) + .withId("user123") + .withEmail("user@codenvy.com") + .withName("user"); + + linksInjector.injectLinks(userDto, serviceContext); + + // [rel, method] pairs links + final Set> links = userDto.getLinks() + .stream() + .map(link -> Pair.of(link.getMethod(), link.getRel())) + .collect(Collectors.toSet()); + final Set> expectedLinks + = new HashSet<>(asList(Pair.of("GET", Constants.LINK_REL_SELF), + Pair.of("GET", Constants.LINK_REL_CURRENT_USER), + Pair.of("GET", Constants.LINK_REL_PROFILE), + Pair.of("GET", Constants.LINK_REL_CURRENT_USER_SETTINGS), + Pair.of("GET", Constants.LINK_REL_PREFERENCES), + Pair.of("POST", Constants.LINK_REL_CURRENT_USER_PASSWORD))); + + assertEquals(links, expectedLinks, "Difference " + Sets.symmetricDifference(links, expectedLinks) + "\n"); + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserManagerTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserManagerTest.java index a6769e8880..d590abfcea 100644 --- a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserManagerTest.java +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserManagerTest.java @@ -11,69 +11,234 @@ package org.eclipse.che.api.user.server; import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.Profile; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.PreferenceDao; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.eclipse.che.api.user.server.spi.UserDao; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Listeners; import org.testng.annotations.Test; +import java.util.Collections; +import java.util.concurrent.Callable; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMapOf; import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; /** - * Tests for {@link UserManager} + * Tests for {@link UserManager}. * * @author Max Shaposhnik (mshaposhnik@codenvy.com) + * @author Yevhenii Voevodin */ - -@Listeners(value = {MockitoTestNGListener.class}) +@Listeners(MockitoTestNGListener.class) public class UserManagerTest { @Mock - UserDao userDao; + private UserDao userDao; @Mock - UserProfileDao profileDao; + private ProfileDao profileDao; @Mock - PreferenceDao preferenceDao; + private PreferenceDao preferencesDao; - UserManager manager; + private UserManager manager; @BeforeMethod public void setUp() { - manager = new UserManager(userDao, profileDao, preferenceDao, new String[0]); + initMocks(this); + + manager = new UserManager(userDao, profileDao, preferencesDao, new String[] {"reserved"}); } @Test public void shouldCreateProfileAndPreferencesOnUserCreation() throws Exception { - final User user = new User().withEmail("test@email.com").withName("testName"); + final UserImpl user = new UserImpl(null, "test@email.com", "testName", null, null); manager.create(user, false); - verify(profileDao).create(any(Profile.class)); - verify(preferenceDao).setPreferences(anyString(), anyMapOf(String.class, String.class)); + verify(userDao).create(any(UserImpl.class)); + verify(profileDao).create(any(ProfileImpl.class)); + verify(preferencesDao).setPreferences(anyString(), anyMapOf(String.class, String.class)); + } + + @Test(dataProvider = "rollback") + public void shouldTryToRollbackWhenEntityCreationFailed(Callable preAction) throws Exception { + preAction.call(); + + // Creating new user + try { + manager.create(new UserImpl(null, "test@email.com", "testName", null, null), false); + fail("Had to throw Exception"); + } catch (Exception x) { + // defined by userDao mock + } + + // Capturing identifier + final ArgumentCaptor captor = ArgumentCaptor.forClass(UserImpl.class); + verify(userDao).create(captor.capture()); + final String userId = captor.getValue().getId(); + + // Verifying rollback + verify(userDao).remove(userId); + verify(preferencesDao).remove(userId); + verify(profileDao).remove(userId); } @Test - public void shouldGeneratedPasswordWhenCreatingUserAndItIsMissing() throws Exception { - final User user = new User().withEmail("test@email.com").withName("testName"); + public void shouldGeneratePasswordWhenCreatingUserAndItIsMissing() throws Exception { + final User user = new UserImpl(null, "test@email.com", "testName", null, null); manager.create(user, false); - verify(userDao).create(eq(user.withPassword(""))); + final ArgumentCaptor userCaptor = ArgumentCaptor.forClass(UserImpl.class); + verify(userDao).create(userCaptor.capture()); + assertNotNull(userCaptor.getValue().getPassword()); + } + + @Test + public void shouldGenerateIdentifierWhenCreatingUser() throws Exception { + final User user = new UserImpl("identifier", "test@email.com", "testName", null, null); + + manager.create(user, false); + + final ArgumentCaptor userCaptor = ArgumentCaptor.forClass(UserImpl.class); + verify(userDao).create(userCaptor.capture()); + final String id = userCaptor.getValue().getId(); + assertNotNull(id); + assertNotEquals(id, "identifier"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenUpdatingUserWithNullEntity() throws Exception { + manager.update(null); + } + + @Test + public void shouldUpdateUser() throws Exception { + final User user = new UserImpl("identifier", "test@email.com", "testName", "password", Collections.emptyList()); + + manager.update(user); + + verify(userDao).update(new UserImpl(user)); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrownNpeWhenTryingToGetUserByNullId() throws Exception { + manager.getById(null); + } + + @Test + public void shouldGetUserById() throws Exception { + final User user = new UserImpl("identifier", "test@email.com", "testName", "password", Collections.singletonList("alias")); + when(manager.getById(user.getId())).thenReturn(user); + + assertEquals(manager.getById(user.getId()), user); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrownNpeWhenTryingToGetUserByNullAlias() throws Exception { + manager.getByAlias(null); + } + + @Test + public void shouldGetUserByAlias() throws Exception { + final User user = new UserImpl("identifier", "test@email.com", "testName", "password", Collections.singletonList("alias")); + when(manager.getByAlias("alias")).thenReturn(user); + + assertEquals(manager.getByAlias("alias"), user); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrownNpeWhenTryingToGetUserByNullName() throws Exception { + manager.getByName(null); + } + + @Test + public void shouldGetUserByName() throws Exception { + final User user = new UserImpl("identifier", "test@email.com", "testName", "password", Collections.singletonList("alias")); + when(manager.getByName(user.getName())).thenReturn(user); + + assertEquals(manager.getByName(user.getName()), user); + } + + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrownNpeWhenTryingToGetUserWithNullEmail() throws Exception { + manager.getByEmail(null); + } + + @Test + public void shouldGetUserByEmail() throws Exception { + final User user = new UserImpl("identifier", "test@email.com", "testName", "password", Collections.singletonList("alias")); + when(manager.getByEmail(user.getEmail())).thenReturn(user); + + assertEquals(manager.getByEmail(user.getEmail()), user); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenRemovingUserByNullId() throws Exception { + manager.remove(null); + } + + @Test + public void shouldRemoveUser() throws Exception { + manager.remove("user123"); + + verify(userDao).remove("user123"); + verify(preferencesDao).remove("user123"); + verify(profileDao).remove("user123"); } @Test(expectedExceptions = ConflictException.class) public void shouldThrowConflictExceptionOnCreationIfUserNameIsReserved() throws Exception { - final User user = new User().withEmail("test@email.com").withName("reserved"); + final User user = new UserImpl("id", "test@email.com", "reserved"); - new UserManager(userDao, profileDao, preferenceDao, new String[] {"reserved"}).create(user, false); + manager.create(user, false); + } + + @DataProvider(name = "rollback") + public Object[][] rollbackTestPreActions() throws Exception { + return new Callable[][] { + + // User creation mocking + {() -> { + doThrow(new ServerException("error")) + .when(userDao) + .create(any()); + return null; + }}, + + // Preferences creation mocking + {() -> { + doThrow(new ServerException("error")) + .when(preferencesDao) + .setPreferences(anyString(), any()); + return null; + }}, + + // Profile creation mocking + {() -> { + doThrow(new ServerException("error")) + .when(profileDao) + .create(any()); + return null; + }} + }; } } diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserProfileServiceTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserProfileServiceTest.java deleted file mode 100644 index 16eee6c815..0000000000 --- a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserProfileServiceTest.java +++ /dev/null @@ -1,390 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.user.server; - -import sun.security.acl.PrincipalImpl; - -import org.eclipse.che.api.core.ForbiddenException; -import org.eclipse.che.api.core.rest.ApiExceptionMapper; -import org.eclipse.che.api.core.rest.shared.dto.Link; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.Profile; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; -import org.eclipse.che.api.user.server.dao.UserProfileDao; -import org.eclipse.che.api.user.shared.dto.ProfileDescriptor; -import org.eclipse.che.commons.json.JsonHelper; -import org.eclipse.che.commons.subject.Subject; -import org.everrest.core.impl.ApplicationContextImpl; -import org.everrest.core.impl.ApplicationProviderBinder; -import org.everrest.core.impl.ContainerRequest; -import org.everrest.core.impl.ContainerResponse; -import org.everrest.core.impl.EnvironmentContext; -import org.everrest.core.impl.EverrestConfiguration; -import org.everrest.core.impl.EverrestProcessor; -import org.everrest.core.impl.ProviderBinder; -import org.everrest.core.impl.ResourceBinderImpl; -import org.everrest.core.tools.DependencySupplierImpl; -import org.everrest.core.tools.ResourceLauncher; -import org.mockito.Mock; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static javax.ws.rs.core.Response.Status.NO_CONTENT; -import static javax.ws.rs.core.Response.Status.OK; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_CURRENT_USER_PROFILE; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_GET_USER_PROFILE_BY_ID; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_CURRENT_USER_PROFILE; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_PREFERENCES; -import static org.eclipse.che.api.user.server.Constants.LINK_REL_UPDATE_USER_PROFILE_BY_ID; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - -/** - * Tests for {@link UserProfileService} - * - * @author Max Shaposhnik - * @author Eugene Voevodin - */ -@Listeners(value = {MockitoTestNGListener.class}) -public class UserProfileServiceTest { - - private static final String BASE_URI = "http://localhost/service"; - private static final String SERVICE_PATH = BASE_URI + "/profile"; - - @Mock - private UserProfileDao profileDao; - @Mock - private UserDao userDao; - @Mock - private PreferenceDao preferenceDao; - @Mock - private User testUser; - @Mock - private UriInfo uriInfo; - @Mock - private EnvironmentContext environmentContext; - @Mock - private SecurityContext securityContext; - private ResourceLauncher launcher; - private UserProfileService service; - - @BeforeMethod - public void setUp() throws Exception { - final ResourceBinderImpl resources = new ResourceBinderImpl(); - resources.addResource(UserProfileService.class, null); - final DependencySupplierImpl dependencies = new DependencySupplierImpl(); - dependencies.addComponent(UserDao.class, userDao); - dependencies.addComponent(UserProfileDao.class, profileDao); - dependencies.addComponent(PreferenceDao.class, preferenceDao); - final URI uri = new URI(BASE_URI); - final ContainerRequest req = new ContainerRequest(null, uri, uri, null, null, securityContext); - final ApplicationContextImpl contextImpl = new ApplicationContextImpl(req, null, ProviderBinder.getInstance()); - contextImpl.setDependencySupplier(dependencies); - ApplicationContextImpl.setCurrent(contextImpl); - final ApplicationProviderBinder binder = new ApplicationProviderBinder(); - binder.addExceptionMapper(ApiExceptionMapper.class); - final EverrestProcessor processor = new EverrestProcessor(resources, - binder, - dependencies, - new EverrestConfiguration(), - null); - launcher = new ResourceLauncher(processor); - service = (UserProfileService)resources.getMatchedResource("/profile", new ArrayList()) - .getInstance(ApplicationContextImpl.getCurrent()); - //setup testUser - final String id = "user123abc456def"; - final String email = "user@testuser.com"; - when(testUser.getEmail()).thenReturn(email); - when(testUser.getId()).thenReturn(id); - when(environmentContext.get(SecurityContext.class)).thenReturn(securityContext); - when(securityContext.getUserPrincipal()).thenReturn(new PrincipalImpl(email)); - when(userDao.getByAlias(email)).thenReturn(testUser); - when(userDao.getById(id)).thenReturn(testUser); - org.eclipse.che.commons.env.EnvironmentContext.getCurrent().setSubject(new Subject() { - @Override - public String getUserName() { - return testUser.getEmail(); - } - - @Override - public boolean hasPermission(String domain, String instance, String action) { - return false; - } - - @Override - public void checkPermission(String domain, String instance, String action) throws ForbiddenException { - } - - @Override - public String getToken() { - return null; - } - - @Override - public String getUserId() { - return testUser.getId(); - } - - @Override - public boolean isTemporary() { - return false; - } - }); - } - - @Test - public void shouldBeAbleToGetCurrentProfile() throws Exception { - final Profile current = new Profile().withId(testUser.getId()).withUserId(testUser.getId()); - when(profileDao.getById(current.getId())).thenReturn(current); - - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH, null); - - assertEquals(response.getStatus(), OK.getStatusCode()); - final ProfileDescriptor descriptor = (ProfileDescriptor)response.getEntity(); - assertEquals(descriptor.getId(), current.getId()); - assertEquals(descriptor.getUserId(), current.getUserId()); - assertEquals(descriptor.getAttributes().get("email"), testUser.getEmail()); - } - - @Test - public void shouldBeAbleToGetPreferences() throws Exception { - final Map preferences = new HashMap<>(8); - preferences.put("test1", "test1"); - preferences.put("test2", "test2"); - preferences.put("test3", "test3"); - when(preferenceDao.getPreferences(testUser.getId())).thenReturn(preferences); - - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH + "/prefs", null); - - assertEquals(response.getStatus(), OK.getStatusCode()); - assertEquals(response.getEntity(), preferences); - } - - @Test - public void shouldBeAbleToRemoveAttributes() throws Exception { - final Map attributes = new HashMap<>(8); - attributes.put("test", "test"); - attributes.put("test1", "test"); - attributes.put("test2", "test"); - final Profile profile = new Profile().withId(testUser.getId()).withAttributes(attributes); - when(profileDao.getById(profile.getId())).thenReturn(profile); - - final ContainerResponse response = makeRequest(HttpMethod.DELETE, SERVICE_PATH + "/attributes", asList("test", "test2")); - - assertEquals(response.getStatus(), NO_CONTENT.getStatusCode()); - verify(profileDao, times(1)).update(profile); - assertEquals(attributes.size(), 1); - assertNotNull(attributes.get("test1")); - } - - @Test - public void shouldRemoveAllAttributesIfNullWasSent() throws Exception { - final Map attributes = new HashMap<>(8); - attributes.put("test", "test"); - attributes.put("test1", "test"); - attributes.put("test2", "test"); - final Profile profile = new Profile().withId(testUser.getId()).withAttributes(attributes); - when(profileDao.getById(profile.getId())).thenReturn(profile); - - final ContainerResponse response = makeRequest(HttpMethod.DELETE, SERVICE_PATH + "/attributes", null); - - assertEquals(response.getStatus(), NO_CONTENT.getStatusCode()); - verify(profileDao, times(1)).update(profile); - assertTrue(attributes.isEmpty()); - } - - @Test - public void shouldBeAbleToUpdatePreferences() throws Exception { - final Map preferences = new HashMap<>(8); - preferences.put("test1", "test1"); - preferences.put("test2", "test2"); - preferences.put("test3", "test3"); - when(preferenceDao.getPreferences(testUser.getId())).thenReturn(preferences); - final Map update = singletonMap("test1", "new_value"); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/prefs", update); - - preferences.putAll(update); - assertEquals(response.getStatus(), OK.getStatusCode()); - assertEquals(response.getEntity(), preferences); - verify(preferenceDao).setPreferences(testUser.getId(), preferences); - } - - @Test - public void shouldThrowExceptionIfPreferencesUpdateIsNull() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/prefs", null); - - assertEquals(response.getStatus(), 409); - } - - @Test - public void shouldThrowExceptionIfPreferencesUpdateIsEmpty() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/prefs", emptyMap()); - - assertEquals(response.getStatus(), 409); - } - - @Test - public void shouldBeAbleToRemovePreferences() throws Exception { - final Map preferences = new HashMap<>(8); - preferences.put("test1", "test1"); - preferences.put("test2", "test2"); - preferences.put("test3", "test3"); - when(preferenceDao.getPreferences(testUser.getId())).thenReturn(preferences); - - final ContainerResponse response = makeRequest(HttpMethod.DELETE, SERVICE_PATH + "/prefs", singletonList("test1")); - - assertEquals(response.getStatus(), NO_CONTENT.getStatusCode()); - assertNull(preferences.get("test1")); - verify(preferenceDao).setPreferences(testUser.getId(), preferences); - } - - @Test - public void shouldRemoveAllPreferencesIfNullWasSend() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.DELETE, SERVICE_PATH + "/prefs", null); - - assertEquals(response.getStatus(), NO_CONTENT.getStatusCode()); - verify(preferenceDao).remove(testUser.getId()); - } - - @Test - public void shouldBeAbleToGetProfileById() throws Exception { - final Profile profile = new Profile().withId(testUser.getId()) - .withUserId(testUser.getId()); - when(profileDao.getById(profile.getId())).thenReturn(profile); - - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH + "/" + profile.getId(), null); - - assertEquals(response.getStatus(), OK.getStatusCode()); - final ProfileDescriptor descriptor = (ProfileDescriptor)response.getEntity(); - assertEquals(descriptor.getUserId(), profile.getId()); - assertEquals(descriptor.getId(), profile.getId()); - assertEquals(descriptor.getAttributes().get("email"), testUser.getEmail()); - } - - @Test - public void shouldBeAbleToUpdateCurrentProfileAttributes() throws Exception { - final Profile profile = new Profile().withId(testUser.getId()) - .withAttributes(new HashMap<>(singletonMap("existed", "old"))); - when(profileDao.getById(profile.getId())).thenReturn(profile); - final Map attributes = new HashMap<>(4); - attributes.put("existed", "new"); - attributes.put("new", "value"); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH, attributes); - - assertEquals(response.getStatus(), OK.getStatusCode()); - verify(profileDao, times(1)).update(profile); - assertEquals(((ProfileDescriptor)response.getEntity()).getAttributes(), attributes); - } - - @Test - public void shouldThrowExceptionIfAttributesUpdateForCurrentProfileIsNull() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH, null); - - assertEquals(response.getStatus(), 409); - } - - @Test - public void shouldThrowExceptionIfAttributesUpdateForCurrentProfileIsEmpty() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH, emptyMap()); - - assertEquals(response.getStatus(), 409); - } - - @Test - public void shouldThrowExceptionIfAttributesUpdateForSpecificProfileIsNull() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/any_profile_id", null); - - assertEquals(response.getStatus(), 409); - } - - @Test - public void shouldThrowExceptionIfAttributesUpdateForSpecificProfileIsEmpty() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/any_profile_id", emptyMap()); - - assertEquals(response.getStatus(), 409); - } - - @Test - public void shouldBeAbleToUpdateProfileById() throws Exception { - final Profile profile = new Profile().withId(testUser.getId()) - .withUserId(testUser.getId()) - .withAttributes(new HashMap<>(singletonMap("existed", "old"))); - when(profileDao.getById(testUser.getId())).thenReturn(profile); - final Map attributes = new HashMap<>(4); - attributes.put("existed", "new"); - attributes.put("new", "value"); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/" + profile.getId(), attributes); - - assertEquals(response.getStatus(), OK.getStatusCode()); - assertEquals(((ProfileDescriptor)response.getEntity()).getAttributes(), attributes); - verify(profileDao, times(1)).update(profile); - } - - @Test - public void testLinks() { - final Profile profile = new Profile().withId(testUser.getId()); - - final Set expectedRels = new HashSet<>(asList(LINK_REL_GET_CURRENT_USER_PROFILE, - LINK_REL_UPDATE_CURRENT_USER_PROFILE, - LINK_REL_GET_USER_PROFILE_BY_ID, - LINK_REL_UPDATE_PREFERENCES, - LINK_REL_UPDATE_USER_PROFILE_BY_ID)); - - assertEquals(asRels(service.toDescriptor(profile).getLinks()), expectedRels); - } - - private Set asRels(List links) { - final Set rels = new HashSet<>(); - for (Link link : links) { - rels.add(link.getRel()); - } - return rels; - } - - private ContainerResponse makeRequest(String method, String path, Object entity) throws Exception { - Map> headers = null; - byte[] data = null; - if (entity != null) { - headers = new HashMap<>(); - headers.put(HttpHeaders.CONTENT_TYPE, singletonList(MediaType.APPLICATION_JSON)); - data = JsonHelper.toJson(entity).getBytes(); - } - return launcher.service(method, path, BASE_URI, headers, data, null, environmentContext); - } -} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserServiceTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserServiceTest.java index a24938e5ff..36a1742c13 100644 --- a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserServiceTest.java +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserServiceTest.java @@ -10,52 +10,47 @@ *******************************************************************************/ package org.eclipse.che.api.user.server; -import org.eclipse.che.api.core.ForbiddenException; -import org.eclipse.che.api.core.NotFoundException; -import org.eclipse.che.api.core.ServerException; +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.jayway.restassured.response.Response; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.core.rest.ApiExceptionMapper; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.shared.dto.UserDescriptor; -import org.eclipse.che.commons.json.JsonHelper; +import org.eclipse.che.api.core.rest.shared.dto.ServiceError; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.shared.dto.UserDto; +import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.subject.Subject; +import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.dto.server.DtoFactory; -import org.everrest.core.impl.ApplicationContextImpl; -import org.everrest.core.impl.ApplicationProviderBinder; -import org.everrest.core.impl.ContainerResponse; -import org.everrest.core.impl.EnvironmentContext; -import org.everrest.core.impl.EverrestConfiguration; -import org.everrest.core.impl.EverrestProcessor; -import org.everrest.core.impl.ProviderBinder; -import org.everrest.core.impl.ResourceBinderImpl; -import org.everrest.core.impl.uri.UriBuilderImpl; -import org.everrest.core.tools.DependencySupplierImpl; -import org.everrest.core.tools.ResourceLauncher; +import org.everrest.assured.EverrestJetty; +import org.everrest.core.Filter; +import org.everrest.core.GenericContainerRequest; +import org.everrest.core.RequestFilter; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; import org.testng.annotations.Test; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.UriInfo; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import static java.util.Collections.singletonList; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.CREATED; -import static javax.ws.rs.core.Response.Status.NO_CONTENT; -import static javax.ws.rs.core.Response.Status.OK; +import static com.jayway.restassured.RestAssured.given; +import static java.util.Collections.emptyList; +import static org.eclipse.che.dto.server.DtoFactory.newDto; +import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME; +import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD; +import static org.everrest.assured.JettyHttpServer.SECURE_PATH; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; import static org.testng.Assert.assertEquals; /** @@ -64,354 +59,364 @@ import static org.testng.Assert.assertEquals; * @author Eugene Veovodin * @author Max Shaposhnik */ -@Listeners(value = {MockitoTestNGListener.class}) +@Listeners({EverrestJetty.class, MockitoTestNGListener.class}) public class UserServiceTest { + @SuppressWarnings("unused") + private static final ApiExceptionMapper MAPPER = new ApiExceptionMapper(); + @SuppressWarnings("unused") + private static final EnvironmentFilter FILTER = new EnvironmentFilter(); + private static final Subject SUBJECT = new SubjectImpl("user", "user123", "token", false); - private final String BASE_URI = "http://localhost/service"; - private final String SERVICE_PATH = BASE_URI + "/user"; - + @Mock(answer = Answers.RETURNS_MOCKS) + private UserManager userManager; @Mock - TokenValidator tokenValidator; + private TokenValidator tokenValidator; @Mock - UserNameValidator userNameValidator; - @Mock - UriInfo uriInfo; - @Mock - EnvironmentContext environmentContext; - @Mock - UserManager userManager; - - UserService userService; - - ResourceLauncher launcher; + private UserLinksInjector linksInjector; + private UserValidator userValidator; + @Captor + private ArgumentCaptor userCaptor; + private UserService userService; @BeforeMethod - public void setUp() throws Exception { - ResourceBinderImpl resources = new ResourceBinderImpl(); - DependencySupplierImpl dependencies = new DependencySupplierImpl(); - dependencies.addComponent(UserManager.class, userManager); - dependencies.addComponent(TokenValidator.class, tokenValidator); + public void initService() { + initMocks(this); - userService = new UserService(userManager, tokenValidator, userNameValidator, true); - final Field uriField = userService.getClass() - .getSuperclass() - .getDeclaredField("uriInfo"); - uriField.setAccessible(true); - uriField.set(userService, uriInfo); + userValidator = new UserValidator(userManager); - resources.addResource(userService, null); + // Return the incoming instance when injectLinks is called + when(linksInjector.injectLinks(any(), any())).thenAnswer(inv -> inv.getArguments()[0]); - EverrestProcessor processor = new EverrestProcessor(resources, - new ApplicationProviderBinder(), - dependencies, - new EverrestConfiguration(), - null); - launcher = new ResourceLauncher(processor); - ProviderBinder providerBinder = ProviderBinder.getInstance(); - providerBinder.addExceptionMapper(ApiExceptionMapper.class); - ApplicationContextImpl.setCurrent(new ApplicationContextImpl(null, null, providerBinder)); - //set up user - final User user = createUser(); - - when(uriInfo.getBaseUriBuilder()).thenReturn(new UriBuilderImpl()); - - org.eclipse.che.commons.env.EnvironmentContext.getCurrent().setSubject(new Subject() { - - @Override - public String getUserName() { - return user.getEmail(); - } - - @Override - public boolean hasPermission(String domain, String instance, String action) { - return false; - } - - @Override - public void checkPermission(String domain, String instance, String action) throws ForbiddenException { - } - - @Override - public String getToken() { - return null; - } - - @Override - public String getUserId() { - return user.getId(); - } - - @Override - public boolean isTemporary() { - return false; - } - }); - } - - @Test(enabled = false) - //TODO Should be fixed within https://jira.codenvycorp.com/browse/CHE-1078 - public void shouldBeAbleToCreateNewUser() throws Exception { - final User userByToken = new User().withEmail("test@email.com").withName("test"); - final String userEmail = "test@email.com"; - final String userName = "test"; - final String token = "test_token"; - when(tokenValidator.validateToken(token)).thenReturn(userByToken); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create?token=" + token, null); - - assertEquals(response.getStatus(), CREATED.getStatusCode()); - final UserDescriptor user = (UserDescriptor)response.getEntity(); - assertEquals(user.getEmail(), userEmail); - assertEquals(user.getName(), userName); - assertEquals(user.getPassword(), ""); - verify(userManager).create(any(User.class), eq(false)); - } - - @Test(enabled = false) - //TODO Should be fixed within https://jira.codenvycorp.com/browse/CHE-1078 - public void shouldBeAbleToCreateNewUserWithEmail() throws Exception { - final String name = "name"; - final String email = "test_user@email.com"; - final UserDescriptor newUser = DtoFactory.getInstance() - .createDto(UserDescriptor.class) - .withName(name) - .withEmail(email); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", newUser); - - assertEquals(response.getStatus(), CREATED.getStatusCode()); - final UserDescriptor descriptor = (UserDescriptor)response.getEntity(); - assertEquals(descriptor.getName(), name); - assertEquals(descriptor.getEmail(), email); - } - - @Test(enabled = false) - //TODO Should be fixed within https://jira.codenvycorp.com/browse/CHE-1078 - public void shouldBeAbleToCreateNewUserWithUserDto() throws Exception { - final UserDescriptor newUser = DtoFactory.getInstance() - .createDto(UserDescriptor.class) - .withName("test") - .withPassword("password123") - .withEmail("test@mail.com"); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", newUser); - - assertEquals(response.getStatus(), CREATED.getStatusCode()); - final UserDescriptor descriptor = (UserDescriptor)response.getEntity(); - assertEquals(descriptor.getName(), newUser.getName()); - assertEquals(descriptor.getPassword(), ""); - verify(userManager).create(any(User.class), eq(false)); + userService = new UserService(userManager, tokenValidator, userValidator, linksInjector, true); } @Test - public void shouldThrowBadRequestExceptionWhenCreatingUserWithInvalidUsername() throws Exception { - final UserDescriptor newUser = DtoFactory.getInstance() - .createDto(UserDescriptor.class) - .withName("test-123@gmail.com") - .withPassword("password"); + public void shouldCreateUserFromToken() throws Exception { + when(tokenValidator.validateToken("token_value")).thenReturn(new UserImpl("id", "test@eclipse.org", "test")); - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", newUser); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .post(SECURE_PATH + "/user?token=token_value"); - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).create(any(User.class), anyBoolean()); + assertEquals(response.statusCode(), 201); + verify(userManager).create(userCaptor.capture(), anyBoolean()); + final User user = userCaptor.getValue(); + assertEquals(user.getEmail(), "test@eclipse.org"); + assertEquals(user.getName(), "test"); } @Test - public void shouldThrowForbiddenExceptionWhenCreatingUserWithInvalidPassword() throws Exception { - final UserDescriptor newUser = DtoFactory.getInstance() - .createDto(UserDescriptor.class) - .withName("test") - .withPassword("password"); + public void shouldCreateUserFromEntity() throws Exception { + final UserDto newUser = newDto(UserDto.class).withName("test") + .withEmail("test@codenvy.com") + .withPassword("password12345"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .body(newUser) + .contentType("application/json") + .post(SECURE_PATH + "/user"); - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", newUser); - - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).create(any(User.class), anyBoolean()); + assertEquals(response.statusCode(), 201); + verify(userManager).create(userCaptor.capture(), anyBoolean()); + final User user = userCaptor.getValue(); + assertEquals(user.getEmail(), "test@codenvy.com"); + assertEquals(user.getName(), "test"); + assertEquals(user.getPassword(), "password12345"); } @Test - public void shouldThrowForbiddenExceptionWhenCreatingUserBasedOnEntityWhichIsNull() throws Exception { + public void shouldNotCreateUserFromEntityWhenPasswordIsNotValid() throws Exception { + final UserDto newUser = newDto(UserDto.class).withName("test") + .withEmail("test@codenvy.com") + .withPassword("1"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .body(newUser) + .contentType("application/json") + .post(SECURE_PATH + "/user"); - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", null); - - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).create(any(User.class), anyBoolean()); + assertEquals(response.statusCode(), 400); + assertEquals(unwrapError(response), "Password should contain at least 8 characters"); } @Test - public void shouldThrowForbiddenExceptionWhenCreatingUserBasedOnEntityWhichContainsNullEmail() throws Exception { - final UserDescriptor newUser = DtoFactory.getInstance().createDto(UserDescriptor.class); + public void shouldNotCreateUserIfTokenIsNotValid() throws Exception { + when(tokenValidator.validateToken("token_value")).thenThrow(new ConflictException("error")); - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", newUser); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .post(SECURE_PATH + "/user?token=token_value"); - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).create(any(User.class), anyBoolean()); + assertEquals(response.statusCode(), 409); + assertEquals(unwrapError(response), "error"); + } + + @Test + public void shouldNotCreateUserFromEntityIfEntityIsNull() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .contentType("application/json") + .post(SECURE_PATH + "/user"); + + assertEquals(response.statusCode(), 400); + assertEquals(unwrapError(response), "User required"); + } + + @Test + public void shouldNotCreateUserFromEntityIfEmailIsNull() throws Exception { + final UserDto newUser = newDto(UserDto.class).withName("test") + .withPassword("password12345"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .body(newUser) + .contentType("application/json") + .post(SECURE_PATH + "/user"); + + assertEquals(response.statusCode(), 400); + assertEquals(unwrapError(response), "User email required"); + } + + @Test + public void shouldNotCreateUserFromEntityIfNameIsNull() throws Exception { + final UserDto newUser = newDto(UserDto.class).withEmail("test@codenvy.com") + .withPassword("password12345"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .body(newUser) + .contentType("application/json") + .post(SECURE_PATH + "/user"); + + assertEquals(response.statusCode(), 400); + assertEquals(unwrapError(response), "User name required"); + } + + @Test + public void shouldNotCreateUserFromEntityIfPasswordIsNotValid() throws Exception { + final UserDto newUser = newDto(UserDto.class).withEmail("test@codenvy.com") + .withName("test") + .withPassword("1"); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .body(newUser) + .contentType("application/json") + .post(SECURE_PATH + "/user"); + + assertEquals(response.statusCode(), 400); + assertEquals(unwrapError(response), "Password should contain at least 8 characters"); } @Test public void shouldBeAbleToGetCurrentUser() throws Exception { - final User user = createUser(); + when(userManager.getById(SUBJECT.getUserId())).thenReturn(copySubject()); - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH, null); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user"); - assertEquals(response.getStatus(), OK.getStatusCode()); - final UserDescriptor descriptor = (UserDescriptor)response.getEntity(); - assertEquals(descriptor.getId(), user.getId()); - assertEquals(descriptor.getEmail(), user.getEmail()); - assertEquals(descriptor.getAliases(), user.getAliases()); + assertEquals(response.getStatusCode(), 200); } @Test public void shouldBeAbleToGetUserById() throws Exception { - final User user = createUser(); + final UserImpl testUser = copySubject(); + when(userManager.getById(SUBJECT.getUserId())).thenReturn(testUser); - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH + "/" + user.getId(), null); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user/" + SUBJECT.getUserId()); - assertEquals(response.getStatus(), OK.getStatusCode()); - final UserDescriptor descriptor = (UserDescriptor)response.getEntity(); - assertEquals(descriptor.getId(), user.getId()); - assertEquals(descriptor.getEmail(), user.getEmail()); - assertEquals(descriptor.getAliases(), user.getAliases()); + assertEquals(response.getStatusCode(), 200); + final UserDto fetchedUser = unwrapDto(response, UserDto.class); + assertEquals(fetchedUser.getId(), testUser.getId()); + assertEquals(fetchedUser.getName(), testUser.getName()); + assertEquals(fetchedUser.getEmail(), testUser.getEmail()); } @Test - public void shouldBeAbleToGetUserByEmail() throws Exception { - final User user = createUser(); + public void shouldBeAbleToFindUserByEmail() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getByEmail(testUser.getEmail())).thenReturn(testUser); - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH + "?alias=" + user.getEmail(), null); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user/find?email=" + testUser.getEmail()); - assertEquals(response.getStatus(), OK.getStatusCode()); - final UserDescriptor descriptor = (UserDescriptor)response.getEntity(); - assertEquals(descriptor.getId(), user.getId()); - assertEquals(descriptor.getEmail(), user.getEmail()); - assertEquals(descriptor.getAliases(), user.getAliases()); + assertEquals(response.getStatusCode(), 200); + final UserDto fetchedUser = unwrapDto(response, UserDto.class); + assertEquals(fetchedUser.getId(), testUser.getId()); + assertEquals(fetchedUser.getName(), testUser.getName()); + assertEquals(fetchedUser.getEmail(), testUser.getEmail()); } @Test - public void shouldBeAbleToUpdateUserPassword() throws Exception { - final User user = createUser(); - final String newPassword = "validPass123"; - final Map> headers = new HashMap<>(); - headers.put(HttpHeaders.CONTENT_TYPE, singletonList(MediaType.APPLICATION_FORM_URLENCODED)); + public void shouldBeAbleToFindUserByName() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getByName(testUser.getName())).thenReturn(testUser); - final ContainerResponse response = launcher.service(HttpMethod.POST, - SERVICE_PATH + "/password", - BASE_URI, - headers, - ("password=" + newPassword).getBytes(), - null, - environmentContext); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user/find?name=" + testUser.getName()); - assertEquals(response.getStatus(), NO_CONTENT.getStatusCode()); - verify(userManager).update(user.withPassword(newPassword)); + assertEquals(response.getStatusCode(), 200); + final UserDto fetchedUser = unwrapDto(response, UserDto.class); + assertEquals(fetchedUser.getId(), testUser.getId()); + assertEquals(fetchedUser.getName(), testUser.getName()); + assertEquals(fetchedUser.getEmail(), testUser.getEmail()); } @Test - public void shouldFailUpdatePasswordContainsOnlyLetters() throws Exception { - final User user = createUser(); - final String newPassword = "password"; - final Map> headers = new HashMap<>(); - headers.put(HttpHeaders.CONTENT_TYPE, singletonList(MediaType.APPLICATION_FORM_URLENCODED)); + public void shouldNotFindUserByNameOrEmailWhenBothSpecified() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getByName(testUser.getName())).thenReturn(testUser); - final ContainerResponse response = launcher.service(HttpMethod.POST, - SERVICE_PATH + "/password", - BASE_URI, - headers, - ("password=" + newPassword).getBytes(), - null, - environmentContext); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user/find?" + + "name=" + testUser.getName() + + "&email=" + testUser.getEmail()); - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).update(user.withPassword(newPassword)); + assertEquals(response.getStatusCode(), 400); + assertEquals(unwrapError(response), "Expected either user's email or name, while both values received"); } @Test - public void shouldFailUpdatePasswordContainsOnlyDigits() throws Exception { - final User user = createUser(); - final String newPassword = "12345678"; - final Map> headers = new HashMap<>(); - headers.put(HttpHeaders.CONTENT_TYPE, singletonList(MediaType.APPLICATION_FORM_URLENCODED)); + public void shouldNotFindUserByNameOrEmailWhenBothAreEmpty() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getByName(testUser.getName())).thenReturn(testUser); - final ContainerResponse response = launcher.service(HttpMethod.POST, - SERVICE_PATH + "/password", - BASE_URI, - headers, - ("password=" + newPassword).getBytes(), - null, - environmentContext); + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user/find"); - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).update(user.withPassword(newPassword)); + assertEquals(response.getStatusCode(), 400); + assertEquals(unwrapError(response), "Missed user's email or name"); } @Test - public void shouldFailUpdatePasswordWhichLessEightChar() throws Exception { - final User user = createUser(); - final String newPassword = "abc123"; - final Map> headers = new HashMap<>(); - headers.put(HttpHeaders.CONTENT_TYPE, singletonList(MediaType.APPLICATION_FORM_URLENCODED)); - - final ContainerResponse response = launcher.service(HttpMethod.POST, - SERVICE_PATH + "/password", - BASE_URI, - headers, - ("password=" + newPassword).getBytes(), - null, - environmentContext); - - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - verify(userManager, never()).update(user.withPassword(newPassword)); - } - - @Test - public void shouldBeAbleToRemoveUser() throws Exception { - final User testUser = createUser(); - - final ContainerResponse response = makeRequest(HttpMethod.DELETE, SERVICE_PATH + "/" + testUser.getId(), null); - - assertEquals(response.getStatus(), NO_CONTENT.getStatusCode()); - verify(userManager).remove(testUser.getId()); - } - - @Test - public void shouldNotBeAbleToCreateUserWithoutEmailBySystemAdmin() throws Exception { - final UserDescriptor newUser = DtoFactory.getInstance() - .createDto(UserDescriptor.class) - .withName("user") - .withPassword("password"); - - final ContainerResponse response = makeRequest(HttpMethod.POST, SERVICE_PATH + "/create", newUser); - - assertEquals(response.getStatus(), BAD_REQUEST.getStatusCode()); - } - - @Test - public void shouldBeAbleToGetSettingOfUserService() throws Exception { - final ContainerResponse response = makeRequest(HttpMethod.GET, SERVICE_PATH + "/settings", null); - - assertEquals(response.getStatus(), OK.getStatusCode()); - @SuppressWarnings("unchecked") - final Map settings = (Map)response.getEntity(); - assertEquals(settings.size(), 1); - assertEquals(settings.get(UserService.USER_SELF_CREATION_ALLOWED), "true"); - } - - private User createUser() throws NotFoundException, ServerException { - final User testUser = new User().withId("test_id") - .withEmail("test@email"); + public void shouldUpdatePassword() throws Exception { + final UserImpl testUser = copySubject(); when(userManager.getById(testUser.getId())).thenReturn(testUser); - when(userManager.getByAlias(testUser.getEmail())).thenReturn(testUser); - return testUser; + + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/x-www-form-urlencoded") + .body("password=password12345") + .when() + .post(SECURE_PATH + "/user/password"); + + verify(userManager).update(userCaptor.capture()); + final User fetchedUser = userCaptor.getValue(); + assertEquals(fetchedUser.getPassword(), "password12345"); } - private ContainerResponse makeRequest(String method, String path, Object entity) throws Exception { - Map> headers = null; - byte[] data = null; - if (entity != null) { - headers = new HashMap<>(); - headers.put(HttpHeaders.CONTENT_TYPE, singletonList(MediaType.APPLICATION_JSON)); - data = JsonHelper.toJson(entity).getBytes(); + @Test + public void shouldNotUpdatePasswordIfPasswordContainsOnlyDigits() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getById(testUser.getId())).thenReturn(testUser); + + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/x-www-form-urlencoded") + .body("password=1234567890") + .when() + .post(SECURE_PATH + "/user/password"); + + assertEquals(response.getStatusCode(), 400); + assertEquals(unwrapError(response), "Password should contain letters and digits"); + } + + @Test + public void shouldNotUpdatePasswordIfPasswordContainsLessThan8Chars() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getById(testUser.getId())).thenReturn(testUser); + + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/x-www-form-urlencoded") + .body("password=0xf") + .when() + .post(SECURE_PATH + "/user/password"); + + assertEquals(response.getStatusCode(), 400); + assertEquals(unwrapError(response), "Password should contain at least 8 characters"); + } + + @Test + public void shouldNotUpdatePasswordIfPasswordIsNull() throws Exception { + final UserImpl testUser = copySubject(); + when(userManager.getById(testUser.getId())).thenReturn(testUser); + + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .contentType("application/x-www-form-urlencoded") + .when() + .post(SECURE_PATH + "/user/password"); + + assertEquals(response.getStatusCode(), 400); + assertEquals(unwrapError(response), "Password required"); + } + + @Test + public void shouldRemoveUser() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .delete(SECURE_PATH + "/user/" + SUBJECT.getUserId()); + + assertEquals(response.getStatusCode(), 204); + verify(userManager).remove(SUBJECT.getUserId()); + } + + @Test + public void shouldBeAbleToGetSettings() throws Exception { + final Response response = given().auth() + .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD) + .when() + .get(SECURE_PATH + "/user/settings"); + + assertEquals(response.getStatusCode(), 200); + final Map settings = new Gson().fromJson(response.print(), + new TypeToken>() {}.getType()); + assertEquals(settings, ImmutableMap.of("user.self.creation.allowed", "true")); + } + + @Filter + public static class EnvironmentFilter implements RequestFilter { + + public void doFilter(GenericContainerRequest request) { + EnvironmentContext.getCurrent().setSubject(SUBJECT); } - return launcher.service(method, path, BASE_URI, headers, data, null, environmentContext); + } + + private static T unwrapDto(Response response, Class dtoClass) { + return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass); + } + + private static String unwrapError(Response response) { + return unwrapDto(response, ServiceError.class).getMessage(); + } + + private static UserImpl copySubject() { + return new UserImpl(SUBJECT.getUserId(), + SUBJECT.getUserName() + "@codenvy.com", + SUBJECT.getUserName(), + null, + emptyList()); } } diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserNameValidatorTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserValidatorTest.java similarity index 92% rename from wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserNameValidatorTest.java rename to wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserValidatorTest.java index cf72910845..c3d486b3e7 100644 --- a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserNameValidatorTest.java +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/UserValidatorTest.java @@ -15,7 +15,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; import org.testng.Assert; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Listeners; import org.testng.annotations.Test; @@ -24,16 +23,19 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doThrow; /** + * Tests of {@link UserValidator}. + * * @author Mihail Kuznyetsov + * @author Yevhenii Voevodin */ @Listeners(MockitoTestNGListener.class) -public class UserNameValidatorTest { +public class UserValidatorTest { @Mock private UserManager userManager; @InjectMocks - private UserNameValidator userNameValidator; + private UserValidator userNameValidator; @Test(dataProvider = "normalizeNames") public void testNormalizeUserName(String input, String expected) throws Exception { @@ -47,7 +49,7 @@ public class UserNameValidatorTest { public void testValidUserName(String input, boolean expected) throws Exception { doThrow(NotFoundException.class).when(userManager).getByName(anyString()); - Assert.assertEquals(userNameValidator.isValidUserName(input), expected); + Assert.assertEquals(userNameValidator.isValidName(input), expected); } @DataProvider(name = "normalizeNames") diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/model/impl/DataObjectsTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/model/impl/DataObjectsTest.java new file mode 100644 index 0000000000..4dafc5f7f0 --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/model/impl/DataObjectsTest.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server.model.impl; + +import com.google.common.collect.ImmutableMap; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.HashMap; + +import static java.util.Collections.singletonList; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * Tests for {@link UserImpl} and {@link ProfileImpl}. + * + * @author Yevhenii Voevodin + */ +public class DataObjectsTest { + + @Test + public void testUserCreation() { + final UserImpl user = new UserImpl("user123", + "user@company.com", + "user_name", + "password", + singletonList("google:id")); + + assertEquals(user.getId(), "user123"); + assertEquals(user.getEmail(), "user@company.com"); + assertEquals(user.getName(), "user_name"); + assertEquals(user.getPassword(), "password"); + assertEquals(user.getAliases(), singletonList("google:id")); + } + + @Test + public void testUserModification() throws Exception { + final UserImpl user = new UserImpl("user123", + "user@company.com", + "user_name", + "password", + singletonList("google:id")); + + user.setName("new_name"); + user.setEmail("new_email@company.com"); + user.setPassword("new-password"); + user.setAliases(singletonList("new-alias")); + + assertEquals(user.getName(), "new_name"); + assertEquals(user.getEmail(), "new_email@company.com"); + assertEquals(user.getPassword(), "new-password"); + assertEquals(user.getAliases(), singletonList("new-alias")); + } + + @Test + public void testUserCopyConstructor() throws Exception { + final UserImpl user = new UserImpl("user123", + "user@company.com", + "user_name", + "password", + new ArrayList<>(singletonList("google:id"))); + final UserImpl copy = new UserImpl(user); + + user.getAliases().add("new-alias"); + + assertEquals(copy.getName(), "user_name"); + assertEquals(copy.getEmail(), "user@company.com"); + assertEquals(copy.getPassword(), "password"); + assertEquals(copy.getAliases(), singletonList("google:id")); + assertFalse(copy.getAliases().contains("new-alias")); + } + + @Test + public void testProfileCreation() { + final ProfileImpl profile = new ProfileImpl("user123", + ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")); + + assertEquals(profile.getUserId(), "user123"); + assertEquals(profile.getAttributes(), ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")); + } + + @Test + public void testProfileModification() throws Exception { + final ProfileImpl profile = new ProfileImpl("user123", + ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")); + + profile.setAttributes(ImmutableMap.of("attribute1", "value1")); + + assertEquals(profile.getAttributes(), ImmutableMap.of("attribute1", "value1")); + } + + @Test + public void testProfileCopyConstructor() throws Exception { + final ProfileImpl profile = new ProfileImpl("user123", + new HashMap<>(ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3"))); + + final ProfileImpl copy = new ProfileImpl(profile); + profile.getAttributes().put("new-attribute", "new-value"); + + assertEquals(copy.getUserId(), "user123"); + assertEquals(copy.getAttributes(), ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")); + assertFalse(copy.getAttributes().containsKey("new-attribute")); + } + + @Test(dataProvider = "singleObjectProvider") + @SuppressWarnings("all") + public void testReflexiveness(Object obj) throws Exception { + assertTrue(obj.equals(obj)); + } + + @Test(dataProvider = "symmetryDataProvider") + public void testSymmetry(Object object1, Object object2) throws Exception { + assertTrue(object1.equals(object2)); + assertTrue(object2.equals(object1)); + } + + @Test(dataProvider = "transitivityDataProvider") + public void testTransitivity(Object object1, Object object2, Object object3) { + assertTrue(object1.equals(object2)); + assertTrue(object2.equals(object3)); + assertTrue(object3.equals(object1)); + } + + @Test(dataProvider = "consistencyDataProvider") + public void testConsistency(Object object1, Object object2) { + assertTrue(object1.equals(object2)); + } + + @Test(dataProvider = "singleObjectProvider") + @SuppressWarnings("all") + public void testNotEqualityToNull(Object object) throws Exception { + assertFalse(object.equals(null)); + } + + @Test(dependsOnMethods = {"testReflexiveness", "testSymmetry", "testTransitivity", "testConsistency", "testNotEqualityToNull"}) + public void testHashCodeContract() throws Exception { + final UserImpl user1 = new UserImpl("user123", + "user@company.com", + "user_name", + "password", + null); + final UserImpl user2 = new UserImpl("user123", + "user@company.com", + "user_name", + "password", + new ArrayList<>()); + + assertEquals(user1.hashCode(), user2.hashCode()); + } + + @DataProvider(name = "reflexivenessProvider") + public Object[][] singleObjectProvider() { + return new Object[][] { + { + new UserImpl("user123", "user@company.com", "user_name", "password", singletonList("google:id")) + }, + { + new ProfileImpl("user123", ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")) + } + }; + } + + @DataProvider(name = "symmetryDataProvider") + public Object[][] symmetryDataProvider() { + return new Object[][] { + { + new UserImpl("user123", "user@company.com", "user_name", "password", singletonList("google:id")), + new UserImpl("user123", "user@company.com", "user_name", "password", singletonList("google:id")) + }, + { + new ProfileImpl("user123", ImmutableMap.of("attribute1", "value1")), + new ProfileImpl("user123", ImmutableMap.of("attribute1", "value1")) + } + }; + } + + @DataProvider(name = "transitivityDataProvider") + public Object[][] transitivityDataProvider() { + return new Object[][] { + { + new UserImpl("user123", "user@company.com", "user_name", "password", null), + new UserImpl("user123", "user@company.com", "user_name", "password", new ArrayList<>()), + new UserImpl("user123", "user@company.com", "user_name", "password", null) + }, + { + new ProfileImpl("user123", ImmutableMap.of("attribute1", "value1")), + new ProfileImpl("user123", ImmutableMap.of("attribute1", "value1")), + new ProfileImpl("user123", ImmutableMap.of("attribute1", "value1")) + } + }; + } + + @DataProvider(name = "consistencyDataProvider") + public Object[][] consistencyDatProvider() { + return new Object[][] { + { + new UserImpl("user123", "user@company.com", "user_name", "password", null), + new UserImpl("user123", "user@company.com", "user_name", "password", new ArrayList<>()) + }, + { + new ProfileImpl("user123", null), + new ProfileImpl("user123", new HashMap<>()) + } + }; + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/ProfileDaoTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/ProfileDaoTest.java new file mode 100644 index 0000000000..e634ef40ab --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/ProfileDaoTest.java @@ -0,0 +1,168 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server.spi.tck; + +import com.google.common.collect.ImmutableMap; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.user.server.Constants; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.eclipse.che.commons.lang.NameGenerator; +import org.eclipse.che.commons.test.tck.TckModuleFactory; +import org.eclipse.che.commons.test.tck.repository.TckRepository; +import org.eclipse.che.commons.test.tck.repository.TckRepositoryException; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.testng.Assert.assertEquals; + +/** + * Tests {@link ProfileDao} contract. + * + * @author Yevhenii Voevodin + */ +@Guice(moduleFactory = TckModuleFactory.class) +@Test(suiteName = ProfileDaoTest.SUITE_NAME) +public class ProfileDaoTest { + + public static final String SUITE_NAME = "ProfileDaoTck"; + + private static final int COUNT_OF_PROFILES = 5; + + private ProfileImpl[] profiles; + + @Inject + private ProfileDao profileDao; + + @Inject + private TckRepository tckRepository; + + @BeforeMethod + private void setUp() throws TckRepositoryException { + profiles = new ProfileImpl[COUNT_OF_PROFILES]; + + for (int i = 0; i < COUNT_OF_PROFILES; i++) { + final String userId = NameGenerator.generate("user", Constants.ID_LENGTH); + final Map attributes = new HashMap<>(); + attributes.put("firstName", "first-name-" + i); + attributes.put("lastName", "last-name-" + i); + attributes.put("company", "company-" + i); + profiles[i] = new ProfileImpl(userId, attributes); + } + + tckRepository.createAll(Arrays.asList(profiles)); + } + + @AfterMethod + private void cleanup() throws TckRepositoryException { + tckRepository.removeAll(); + } + + @Test + public void shouldGetProfileById() throws Exception { + final ProfileImpl profile = profiles[0]; + + assertEquals(profileDao.getById(profile.getUserId()), profile); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNotFoundExceptionWhenGettingNonExistingProfileById() throws Exception { + profileDao.getById("non-existing-user-id"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenGettingProfileByNullId() throws Exception { + profileDao.getById(null); + } + + @Test(dependsOnMethods = "shouldGetProfileById") + public void shouldCreateProfile() throws Exception { + final ProfileImpl newProfile = new ProfileImpl("user123", + ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")); + + profileDao.create(newProfile); + + assertEquals(profileDao.getById(newProfile.getUserId()), newProfile); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenCreatingProfileThatAlreadyExistsForUserWithGivenId() throws Exception { + final ProfileImpl newProfile = new ProfileImpl(profiles[0].getUserId(), + ImmutableMap.of("attribute1", "value1", + "attribute2", "value2", + "attribute3", "value3")); + + profileDao.create(newProfile); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenCreatingNull() throws Exception { + profileDao.create(null); + } + + @Test(dependsOnMethods = "shouldGetProfileById") + public void shouldUpdateProfile() throws Exception { + final ProfileImpl profile = profiles[0]; + + profileDao.update(new ProfileImpl(profile.getUserId(), + ImmutableMap.of("firstName", "new-first-name", + "lastName", "new-second-name", + "company", "new-company"))); + + final ProfileImpl updated = profileDao.getById(profile.getUserId()); + assertEquals(updated.getUserId(), profile.getUserId()); + assertEquals(updated.getAttributes(), ImmutableMap.of("firstName", "new-first-name", + "lastName", "new-second-name", + "company", "new-company")); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNotFoundExceptionWhenUpdatingProfileOfNonExistingUser() throws Exception { + final ProfileImpl profile = profiles[0]; + + profileDao.update(new ProfileImpl("non-existing-user-id", profile.getAttributes())); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenUpdatingNull() throws Exception { + profileDao.update(null); + } + + @Test(expectedExceptions = NotFoundException.class, + dependsOnMethods = "shouldThrowNotFoundExceptionWhenGettingNonExistingProfileById") + public void shouldRemoveProfile() throws Exception { + final ProfileImpl profile = profiles[0]; + + profileDao.remove(profile.getUserId()); + profileDao.getById(profile.getUserId()); + } + + @Test + public void shouldNotThrowAnyExceptionWhenRemovingNonExistingUser() throws Exception { + profileDao.remove("non-existing-id"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenRemovingNull() throws Exception { + profileDao.remove(null); + } +} diff --git a/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/UserDaoTest.java b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/UserDaoTest.java new file mode 100644 index 0000000000..8094d9ae1b --- /dev/null +++ b/wsmaster/che-core-api-user/src/test/java/org/eclipse/che/api/user/server/spi/tck/UserDaoTest.java @@ -0,0 +1,337 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.user.server.spi.tck; + +import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.UnauthorizedException; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.user.server.Constants; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.server.spi.UserDao; +import org.eclipse.che.commons.lang.NameGenerator; +import org.eclipse.che.commons.test.tck.TckModuleFactory; +import org.eclipse.che.commons.test.tck.repository.TckRepository; +import org.eclipse.che.commons.test.tck.repository.TckRepositoryException; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.testng.Assert.assertEquals; + +/** + * Tests {@link UserDao} contract. + * + * @author Yevhenii Voevodin + */ +@Guice(moduleFactory = TckModuleFactory.class) +@Test(suiteName = UserDaoTest.SUITE_NAME) +public class UserDaoTest { + + public static final String SUITE_NAME = "UserDaoTck"; + + private static final int COUNT_OF_USERS = 5; + + private UserImpl[] users; + + @Inject + private UserDao userDao; + + @Inject + private TckRepository tckRepository; + + @BeforeMethod + public void setUp() throws TckRepositoryException { + users = new UserImpl[COUNT_OF_USERS]; + + for (int i = 0; i < users.length; i++) { + final String id = NameGenerator.generate("user", Constants.ID_LENGTH); + final String name = "user_name-" + i; + final String email = name + "@eclipse.org"; + final String password = NameGenerator.generate("", Constants.PASSWORD_LENGTH); + final List aliases = new ArrayList<>(asList("google:" + name, "github:" + name)); + users[i] = new UserImpl(id, email, name, password, aliases); + } + + tckRepository.createAll(Arrays.asList(users)); + } + + @AfterMethod + public void cleanUp() throws TckRepositoryException { + tckRepository.removeAll(); + } + + @Test + public void shouldAuthenticateUserByName() throws Exception { + final UserImpl user = users[0]; + + assertEquals(userDao.authenticate(user.getName(), user.getPassword()), user.getId()); + } + + @Test + public void shouldAuthenticateUserByEmail() throws Exception { + final UserImpl user = users[0]; + + assertEquals(userDao.authenticate(user.getEmail(), user.getPassword()), user.getId()); + } + + @Test + public void shouldAuthenticateUserByAlias() throws Exception { + final UserImpl user = users[0]; + + assertEquals(userDao.authenticate(user.getAliases().get(0), user.getPassword()), user.getId()); + } + + @Test(expectedExceptions = UnauthorizedException.class) + public void shouldNotAuthenticateUserById() throws Exception { + final UserImpl user = users[0]; + + assertEquals(userDao.authenticate(user.getId(), user.getPassword()), user.getId()); + } + + @Test(expectedExceptions = UnauthorizedException.class) + public void shouldNotAuthenticateUserWithWrongPassword() throws Exception { + final UserImpl user = users[0]; + + assertEquals(userDao.authenticate(user.getName(), "fake" + user.getPassword()), user.getId()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenAuthorizingUserWithNullAlias() throws Exception { + userDao.authenticate(null, "password"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenAuthorizingUserWithNullPassword() throws Exception { + userDao.authenticate("alias", null); + } + + @Test + public void shouldGetUserById() throws Exception { + final UserImpl user = users[0]; + + assertEqualsNoPassword(userDao.getById(user.getId()), user); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNotFoundExceptionWhenGettingNonExistingUserById() throws Exception { + userDao.getById("non-existing"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenGettingUserByNullId() throws Exception { + userDao.getById(null); + } + + @Test + public void shouldGetUserByEmail() throws Exception { + final UserImpl user = users[0]; + + assertEqualsNoPassword(userDao.getByEmail(user.getEmail()), user); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNotFoundExceptionWhenGettingNonExistingUserByEmail() throws Exception { + userDao.getByEmail("non-existing-email@eclipse.org"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenGettingUserByNullEmail() throws Exception { + userDao.getByEmail(null); + } + + @Test + public void shouldGetUserByName() throws Exception { + final UserImpl user = users[0]; + + assertEqualsNoPassword(userDao.getByName(user.getName()), user); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNoFountExceptionWhenGettingNonExistingUserByName() throws Exception { + userDao.getByName("non-existing-name"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenGettingUserByNullName() throws Exception { + userDao.getByName(null); + } + + @Test + public void shouldGetUserByAlias() throws Exception { + final UserImpl user = users[0]; + + assertEqualsNoPassword(userDao.getByAlias(user.getAliases().get(0)), user); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNotFoundExceptionWhenGettingNonExistingUserByAlias() throws Exception { + userDao.getByAlias("non-existing-alias"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenGettingUserByNullAlias() throws Exception { + userDao.getByAlias(null); + } + + @Test(dependsOnMethods = "shouldGetUserById") + public void shouldCreateUser() throws Exception { + final UserImpl newUser = new UserImpl("user123", + "user123@eclipse.org", + "user_name", + "password", + asList("google:user123", "github:user123")); + + userDao.create(newUser); + + assertEqualsNoPassword(userDao.getById(newUser.getId()), newUser); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenCreatingUserWithExistingId() throws Exception { + final UserImpl newUser = new UserImpl(users[0].getId(), + "user123@eclipse.org", + "user_name", + "password", + asList("google:user123", "github:user123")); + + userDao.create(newUser); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenCreatingUserWithExistingEmail() throws Exception { + final UserImpl newUser = new UserImpl("user123", + users[0].getEmail(), + "user_name", + "password", + asList("google:user123", "github:user123")); + + userDao.create(newUser); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenCreatingUserWithExistingName() throws Exception { + final UserImpl newUser = new UserImpl("user123", + "user123@eclipse.org", + users[0].getName(), + "password", + asList("google:user123", "github:user123")); + + userDao.create(newUser); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenCreatingUserWithExistingAliases() throws Exception { + final UserImpl newUser = new UserImpl("user123", + "user123@eclipse.org", + "user_name", + "password", + users[0].getAliases()); + + userDao.create(newUser); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrownNpeWhenTryingToCreateNullUser() throws Exception { + userDao.create(null); + } + + @Test(dependsOnMethods = "shouldGetUserById") + public void shouldUpdateUser() throws Exception { + final UserImpl user = users[0]; + + userDao.update(new UserImpl(user.getId(), + "new-email", + "new-name", + "new-password", + asList("google:new-alias", "github:new-alias"))); + + final UserImpl updated = userDao.getById(user.getId()); + assertEquals(updated.getId(), user.getId()); + assertEquals(updated.getEmail(), "new-email"); + assertEquals(updated.getName(), "new-name"); + assertEquals(updated.getAliases(), asList("google:new-alias", "github:new-alias")); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenUpdatingUserWithReservedEmail() throws Exception { + final UserImpl user = users[0]; + + user.setEmail(users[1].getEmail()); + + userDao.update(user); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenUpdatingUserWithReservedName() throws Exception { + final UserImpl user = users[0]; + + user.setName(users[1].getName()); + + userDao.update(user); + } + + @Test(expectedExceptions = ConflictException.class) + public void shouldThrowConflictExceptionWhenUpdatingUserWithReservedAlias() throws Exception { + final UserImpl user = users[0]; + + user.setAliases(users[1].getAliases()); + + userDao.update(user); + } + + @Test(expectedExceptions = NotFoundException.class) + public void shouldThrowNotFoundExceptionWhenUpdatingNonExistingUser() throws Exception { + userDao.update(new UserImpl("non-existing-id", + "new-email", + "new-name", + "new-password", + asList("google:new-alias", "github:new-alias"))); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenUpdatingNull() throws Exception { + userDao.update(null); + } + + @Test(expectedExceptions = NotFoundException.class, + dependsOnMethods = "shouldThrowNotFoundExceptionWhenGettingNonExistingUserById") + public void shouldRemoveUser() throws Exception { + final UserImpl user = users[0]; + + userDao.remove(user.getId()); + userDao.getById(user.getId()); + } + + @Test + public void shouldNotThrowAnyExceptionWhenRemovingNonExistingUser() throws Exception { + userDao.remove("non-existing-user"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNpeWhenRemovingNull() throws Exception { + userDao.remove(null); + } + + private static void assertEqualsNoPassword(User actual, User expected) { + assertEquals(actual.getId(), expected.getId()); + assertEquals(actual.getEmail(), expected.getEmail()); + assertEquals(actual.getName(), expected.getName()); + assertEquals(actual.getAliases(), expected.getAliases()); + } +} diff --git a/wsmaster/wsmaster-local/pom.xml b/wsmaster/wsmaster-local/pom.xml index 1576268822..1ad608295e 100644 --- a/wsmaster/wsmaster-local/pom.xml +++ b/wsmaster/wsmaster-local/pom.xml @@ -40,6 +40,10 @@ commons-io commons-io + + io.swagger + swagger-annotations + javax.annotation javax.annotation-api @@ -48,10 +52,18 @@ javax.inject javax.inject + + javax.ws.rs + javax.ws.rs-api + org.eclipse.che.core che-core-api-core + + org.eclipse.che.core + che-core-api-dto + org.eclipse.che.core che-core-api-machine @@ -68,6 +80,10 @@ org.eclipse.che.core che-core-api-user + + org.eclipse.che.core + che-core-api-user-shared + org.eclipse.che.core che-core-api-workspace @@ -98,11 +114,22 @@ logback-classic test + + org.eclipse.che.core + che-core-api-user + tests + test + org.eclipse.che.core che-core-commons-lang test + + org.eclipse.che.core + che-core-commons-test + test + org.everrest everrest-assured @@ -113,6 +140,11 @@ mockito-all test + + org.mockito + mockito-core + test + org.mockitong mockitong @@ -124,4 +156,24 @@ test + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + unpack-dependencies + + + ${project.build.testOutputDirectory} + che-core-api-user + test + + + + + + diff --git a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/DummyTokenValidator.java b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/DummyTokenValidator.java index a7419e2c8e..973329cd54 100644 --- a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/DummyTokenValidator.java +++ b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/DummyTokenValidator.java @@ -11,9 +11,9 @@ package org.eclipse.che.api.local; import org.eclipse.che.api.core.ConflictException; +import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.user.server.TokenValidator; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.api.user.server.model.impl.UserImpl; import javax.inject.Singleton; @@ -28,6 +28,6 @@ public class DummyTokenValidator implements TokenValidator { @Override public User validateToken(String token) throws ConflictException { - return new User().withName("che").withEmail("che@eclipse.org"); + return new UserImpl("che", "che", "che@eclipse.org"); } } diff --git a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalInfrastructureModule.java b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalInfrastructureModule.java index f3e99914cf..cf8d826c54 100644 --- a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalInfrastructureModule.java +++ b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalInfrastructureModule.java @@ -18,14 +18,15 @@ import org.eclipse.che.api.machine.server.dao.RecipeDao; import org.eclipse.che.api.machine.server.dao.SnapshotDao; import org.eclipse.che.api.ssh.server.spi.SshDao; import org.eclipse.che.api.user.server.TokenValidator; -import org.eclipse.che.api.user.server.dao.PreferenceDao; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.server.spi.PreferenceDao; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.workspace.server.spi.StackDao; import org.eclipse.che.api.workspace.server.spi.WorkspaceDao; import javax.inject.Named; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -34,7 +35,7 @@ public class LocalInfrastructureModule extends AbstractModule { protected void configure() { bind(UserDao.class).to(LocalUserDaoImpl.class); bind(WorkspaceDao.class).to(LocalWorkspaceDaoImpl.class); - bind(UserProfileDao.class).to(LocalProfileDaoImpl.class); + bind(ProfileDao.class).to(LocalProfileDaoImpl.class); bind(PreferenceDao.class).to(LocalPreferenceDaoImpl.class); bind(SnapshotDao.class).to(LocalSnapshotDaoImpl.class); bind(SshDao.class).to(LocalSshDaoImpl.class); @@ -46,13 +47,13 @@ public class LocalInfrastructureModule extends AbstractModule { @Provides @Named("codenvy.local.infrastructure.users") - Set users() { - final Set users = new HashSet<>(1); - final User user = new User().withId("che") - .withName("che") - .withEmail("che@eclipse.org") - .withPassword("secret"); - user.getAliases().add("che@eclipse.org"); + Set users() { + final Set users = new HashSet<>(); + final UserImpl user = new UserImpl("che", + "che@eclipse.org", + "che", + "secret", + Collections.emptyList()); users.add(user); return users; } diff --git a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalPreferenceDaoImpl.java b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalPreferenceDaoImpl.java index dd5be498d6..775de4c741 100644 --- a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalPreferenceDaoImpl.java +++ b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalPreferenceDaoImpl.java @@ -13,11 +13,10 @@ package org.eclipse.che.api.local; import com.google.common.reflect.TypeToken; -import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.local.storage.LocalStorage; import org.eclipse.che.api.local.storage.LocalStorageFactory; -import org.eclipse.che.api.user.server.dao.PreferenceDao; +import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,7 +71,7 @@ public class LocalPreferenceDaoImpl implements PreferenceDao { } @Override - public void setPreferences(String userId, Map prefs) throws ServerException, NotFoundException { + public void setPreferences(String userId, Map prefs) throws ServerException { lock.writeLock().lock(); try { preferences.put(userId, new HashMap<>(prefs)); diff --git a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalProfileDaoImpl.java b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalProfileDaoImpl.java index e84a7eb7db..0325f50c87 100644 --- a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalProfileDaoImpl.java +++ b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalProfileDaoImpl.java @@ -11,13 +11,17 @@ package org.eclipse.che.api.local; +import com.google.common.annotations.VisibleForTesting; import com.google.common.reflect.TypeToken; +import org.eclipse.che.api.core.ConflictException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.local.storage.LocalStorage; import org.eclipse.che.api.local.storage.LocalStorageFactory; -import org.eclipse.che.api.user.server.dao.Profile; -import org.eclipse.che.api.user.server.dao.UserProfileDao; +import org.eclipse.che.api.core.model.user.Profile; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.eclipse.che.api.user.server.spi.UserDao; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -30,35 +34,39 @@ import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + /** * @author Anton Korneta */ @Singleton -public class LocalProfileDaoImpl implements UserProfileDao { +public class LocalProfileDaoImpl implements ProfileDao { - private final Map profiles; - private final ReadWriteLock lock; - private final LocalStorage profileStorage; + @VisibleForTesting + final Map profiles; + + private final ReadWriteLock lock; + private final LocalStorage profileStorage; @Inject public LocalProfileDaoImpl(LocalStorageFactory storageFactory) throws IOException { - profiles = new HashMap<>(); + profiles = new LinkedHashMap<>(); lock = new ReentrantReadWriteLock(); profileStorage = storageFactory.create("profiles.json"); } @PostConstruct private void start() { - profiles.putAll(profileStorage.loadMap(new TypeToken>() {})); + profiles.putAll(profileStorage.loadMap(new TypeToken>() {})); // Add default entry if file doesn't exist or invalid or empty. if (profiles.isEmpty()) { final Map attributes = new HashMap<>(2); attributes.put("firstName", "Che"); attributes.put("lastName", "Codenvy"); - Profile profile = new Profile().withId("che") - .withUserId("che") - .withAttributes(attributes); - profiles.put(profile.getId(), profile); + + ProfileImpl profile = new ProfileImpl("che", attributes); + profiles.put(profile.getUserId(), profile); } } @@ -68,25 +76,27 @@ public class LocalProfileDaoImpl implements UserProfileDao { } @Override - public void create(Profile profile) { + public void create(ProfileImpl profile) throws ConflictException { + requireNonNull(profile, "Required non-null profile"); lock.writeLock().lock(); try { - // just replace existed profile - final Profile copy = new Profile().withId(profile.getId()).withUserId(profile.getUserId()) - .withAttributes(new LinkedHashMap<>(profile.getAttributes())); - profiles.put(copy.getId(), copy); + if (profiles.containsKey(profile.getUserId())) { + throw new ConflictException(format("Profile for user '%s' already exists", profile.getUserId())); + } + profiles.put(profile.getUserId(), new ProfileImpl(profile)); } finally { lock.writeLock().unlock(); } } @Override - public void update(Profile profile) throws NotFoundException { + public void update(ProfileImpl profile) throws NotFoundException { + requireNonNull(profile, "Required non-null profile"); lock.writeLock().lock(); try { - final Profile myProfile = profiles.get(profile.getId()); + final Profile myProfile = profiles.get(profile.getUserId()); if (myProfile == null) { - throw new NotFoundException(String.format("Profile not found %s", profile.getId())); + throw new NotFoundException(format("Profile with id '%s' not found", profile.getUserId())); } myProfile.getAttributes().clear(); myProfile.getAttributes().putAll(profile.getAttributes()); @@ -96,28 +106,26 @@ public class LocalProfileDaoImpl implements UserProfileDao { } @Override - public void remove(String id) throws NotFoundException { + public void remove(String id) { + requireNonNull(id, "Required non-null id"); lock.writeLock().lock(); try { - final Profile profile = profiles.remove(id); - if (profile == null) { - throw new NotFoundException(String.format("Profile not found %s", id)); - } + profiles.remove(id); } finally { lock.writeLock().unlock(); } } @Override - public Profile getById(String id) throws NotFoundException { + public ProfileImpl getById(String id) throws NotFoundException { + requireNonNull(id, "Required non-null id"); lock.readLock().lock(); try { final Profile profile = profiles.get(id); if (profile == null) { - throw new NotFoundException(String.format("Profile not found %s", id)); + throw new NotFoundException(format("Profile with id '%s' not found", id)); } - return new Profile().withId(profile.getId()).withUserId(profile.getUserId()) - .withAttributes(new LinkedHashMap<>(profile.getAttributes())); + return new ProfileImpl(profile); } finally { lock.readLock().unlock(); } diff --git a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalUserDaoImpl.java b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalUserDaoImpl.java index cab1243156..8afeac36f6 100644 --- a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalUserDaoImpl.java +++ b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalUserDaoImpl.java @@ -11,16 +11,18 @@ package org.eclipse.che.api.local; +import com.google.common.annotations.VisibleForTesting; import com.google.common.reflect.TypeToken; import org.eclipse.che.api.core.ConflictException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.UnauthorizedException; +import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.local.storage.LocalStorage; import org.eclipse.che.api.local.storage.LocalStorageFactory; -import org.eclipse.che.api.user.server.dao.User; -import org.eclipse.che.api.user.server.dao.UserDao; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.server.spi.UserDao; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -29,189 +31,211 @@ import javax.inject.Named; import javax.inject.Singleton; import java.io.IOException; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Predicate; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; /** * @author Anton Korneta + * @author Yevhenii Voevodin */ @Singleton public class LocalUserDaoImpl implements UserDao { - private final List users; - private final ReadWriteLock lock; + @VisibleForTesting + final Map users; + + private final ReadWriteLock rwLock; private final LocalStorage userStorage; @Inject public LocalUserDaoImpl(LocalStorageFactory storageFactory) throws IOException { - this.users = new LinkedList<>(); - lock = new ReentrantReadWriteLock(); + this.users = new HashMap<>(); + rwLock = new ReentrantReadWriteLock(); userStorage = storageFactory.create("users.json"); } @Inject @PostConstruct - public void start(@Named("codenvy.local.infrastructure.users") Set defaultUsers) { - List storedUsers = userStorage.loadList(new TypeToken>() {}); - users.addAll(storedUsers.isEmpty() ? defaultUsers : storedUsers); + public void start(@Named("codenvy.local.infrastructure.users") Set defaultUsers) { + final Map storedUsers = userStorage.loadMap(new TypeToken>() {}); + rwLock.writeLock().lock(); + try { + final Collection preloadedUsers = storedUsers.isEmpty() ? defaultUsers : storedUsers.values(); + for (UserImpl defaultUser : preloadedUsers) { + users.put(defaultUser.getId(), new UserImpl(defaultUser)); + } + } finally { + rwLock.writeLock().unlock(); + } } @PreDestroy public void stop() throws IOException { - userStorage.store(users); - } - - @Override - public String authenticate(String alias, String password) throws UnauthorizedException, ServerException { - lock.readLock().lock(); + rwLock.readLock().lock(); try { - User myUser = null; - for (int i = 0, size = users.size(); i < size && myUser == null; i++) { - if (users.get(i).getAliases().contains(alias)) { - myUser = users.get(i); - } - } - if (myUser == null || !password.equals(myUser.getPassword())) { - throw new UnauthorizedException(String.format("Authentication failed for user %s", alias)); - } - return myUser.getId(); + userStorage.store(new HashMap<>(users)); } finally { - lock.readLock().unlock(); + rwLock.readLock().unlock(); } } @Override - public void create(User user) throws ConflictException { - lock.writeLock().lock(); + public String authenticate(String aliasOrNameOrEmail, String password) throws UnauthorizedException, ServerException { + requireNonNull(aliasOrNameOrEmail); + requireNonNull(password); + rwLock.readLock().lock(); try { - final String userId = user.getId(); - final Set aliases = new HashSet<>(user.getAliases()); - for (User u : users) { - if (u.getId().equals(userId)) { + final Optional userOpt = users.values() + .stream() + .filter(user -> user.getName().equals(aliasOrNameOrEmail) + || user.getEmail().equals(aliasOrNameOrEmail) + || user.getAliases().contains(aliasOrNameOrEmail)) + .findAny(); + if (!userOpt.isPresent() || !userOpt.get().getPassword().equals(password)) { + throw new UnauthorizedException(format("Authentication failed for user '%s'", aliasOrNameOrEmail)); + } + return userOpt.get().getId(); + } finally { + rwLock.readLock().unlock(); + } + } + + @Override + public void create(UserImpl newUser) throws ConflictException { + requireNonNull(newUser); + rwLock.writeLock().lock(); + try { + if (users.containsKey(newUser.getId())) { + throw new ConflictException(format("Couldn't create user, user with id '%s' already exists", + newUser.getId())); + } + checkConflicts(newUser, "create"); + users.put(newUser.getId(), new UserImpl(newUser)); + } finally { + rwLock.writeLock().unlock(); + } + } + + @Override + public void update(UserImpl update) throws NotFoundException, ConflictException { + requireNonNull(update); + rwLock.writeLock().lock(); + try { + final UserImpl user = users.get(update.getId()); + if (user == null) { + throw new NotFoundException(format("User with id '%s' doesn't exist", update.getId())); + } + checkConflicts(update, "update"); + users.put(update.getId(), new UserImpl(update)); + } finally { + rwLock.writeLock().unlock(); + } + } + + @Override + public void remove(String id) { + requireNonNull(id); + rwLock.writeLock().lock(); + try { + users.remove(id); + } finally { + rwLock.writeLock().unlock(); + } + } + + @Override + public UserImpl getByAlias(String alias) throws NotFoundException { + requireNonNull(alias, "Required non-null alias"); + rwLock.readLock().lock(); + try { + return new UserImpl(find(user -> user.getAliases().contains(alias), "alias", alias)); + } finally { + rwLock.readLock().unlock(); + } + } + + @Override + public UserImpl getById(String id) throws NotFoundException { + requireNonNull(id, "Required non-null id"); + rwLock.readLock().lock(); + try { + final User user = users.get(id); + if (user == null) { + throw new NotFoundException(format("User with id '%s' doesn't exist", id)); + } + return new UserImpl(user); + } finally { + rwLock.readLock().unlock(); + } + } + + @Override + public UserImpl getByName(String name) throws NotFoundException { + requireNonNull(name, "Required non-null name"); + rwLock.readLock().lock(); + try { + return new UserImpl(find(user -> user.getName().equals(name), "name", name)); + } finally { + rwLock.readLock().unlock(); + } + } + + @Override + public UserImpl getByEmail(String email) throws NotFoundException, ServerException { + requireNonNull(email, "Required non-null email"); + rwLock.readLock().lock(); + try { + return new UserImpl(find(user -> user.getEmail().equals(email), "email", email)); + } finally { + rwLock.readLock().unlock(); + } + } + + private void checkConflicts(UserImpl user, String operation) throws ConflictException { + for (UserImpl existingUser : users.values()) { + if (!existingUser.getId().equals(user.getId())) { + if (existingUser.getName().equals(user.getName())) throw new ConflictException( - String.format("Unable create new user '%s'. User id %s is already in use.", user.getEmail(), userId)); + format("Unable to %s a new user with name '%s' the name is already in use.", + operation, + user.getName())); + if (existingUser.getEmail().equals(user.getEmail())) { + throw new ConflictException( + format("Unable to %s a new user with email '%s' the email is already in use.", + operation, + user.getEmail())); } - for (String alias : u.getAliases()) { - if (aliases.contains(alias)) { - throw new ConflictException( - String.format("Unable create new user '%s'. User alias %s is already in use.", user.getEmail(), alias)); - } + if (!Collections.disjoint(existingUser.getAliases(), user.getAliases())) { + final HashSet aliases = new HashSet<>(existingUser.getAliases()); + aliases.retainAll(user.getAliases()); + throw new ConflictException( + format("Unable to %s a new user with aliases '%s', the aliases are already in use", + operation, + aliases)); } } - users.add(doClone(user)); - } finally { - lock.writeLock().unlock(); } } - @Override - public void update(User user) throws NotFoundException { - lock.writeLock().lock(); - try { - User myUser = null; - for (int i = 0, size = users.size(); i < size && myUser == null; i++) { - if (users.get(i).getId().equals(user.getId())) { - myUser = users.get(i); - } - } - if (myUser == null) { - throw new NotFoundException(String.format("User not found %s", user.getId())); - } - myUser.getAliases().clear(); - myUser.getAliases().addAll(user.getAliases()); - myUser.setEmail(user.getEmail()); - myUser.setPassword(user.getPassword()); - } finally { - lock.writeLock().unlock(); + private UserImpl find(Predicate predicate, String subjectName, String subject) throws NotFoundException { + final Optional userOpt = users.values() + .stream() + .filter(predicate) + .findAny(); + if (!userOpt.isPresent()) { + throw new NotFoundException(format("User with %s '%s' doesn't exist", subjectName, subject)); } - } - - @Override - public void remove(String id) throws NotFoundException { - lock.writeLock().lock(); - try { - User myUser = null; - for (int i = 0, size = users.size(); i < size && myUser == null; i++) { - if (users.get(i).getId().equals(id)) { - myUser = users.get(i); - } - } - if (myUser == null) { - throw new NotFoundException(String.format("User not found %s", id)); - } - users.remove(myUser); - } finally { - lock.writeLock().unlock(); - } - } - - @Override - public User getByAlias(String alias) throws NotFoundException { - lock.readLock().lock(); - try { - User user = null; - for (int i = 0, size = users.size(); i < size && user == null; i++) { - if (users.get(i).getAliases().contains(alias)) { - user = users.get(i); - } - } - if (user == null) { - throw new NotFoundException(String.format("User not found %s", alias)); - } - return doClone(user); - } finally { - lock.readLock().unlock(); - } - } - - @Override - public User getById(String id) throws NotFoundException { - lock.readLock().lock(); - try { - User user = null; - for (int i = 0, size = users.size(); i < size && user == null; i++) { - if (users.get(i).getId().equals(id)) { - user = users.get(i); - } - } - if (user == null) { - throw new NotFoundException(String.format("User not found %s", id)); - } - return doClone(user); - } finally { - lock.readLock().unlock(); - } - } - - @Override - public User getByName(String name) throws NotFoundException { - lock.readLock().lock(); - try { - User user = null; - for (int i = 0, size = users.size(); i < size && user == null; i++) { - if (users.get(i).getName().equals(name)) { - user = users.get(i); - } - } - if (user == null) { - throw new NotFoundException(String.format("User not found %s", name)); - } - return doClone(user); - } finally { - lock.readLock().unlock(); - } - } - - private User doClone(User user) { - return new User().withId(user.getId()) - .withName(user.getName()) - .withEmail(user.getEmail()) - .withPassword(user.getPassword()) - .withAliases(new ArrayList<>(user.getAliases())); + return userOpt.get(); } } diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalProfileTckRepository.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalProfileTckRepository.java new file mode 100644 index 0000000000..86f9b38d80 --- /dev/null +++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalProfileTckRepository.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.local; + +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.commons.test.tck.repository.TckRepository; +import org.eclipse.che.commons.test.tck.repository.TckRepositoryException; + +import javax.inject.Inject; +import java.util.Collection; + +/** + * @author Yevhenii Voevodin + */ +public class LocalProfileTckRepository implements TckRepository { + + @Inject + private LocalProfileDaoImpl profileDao; + + @Override + public void createAll(Collection profiles) throws TckRepositoryException { + for (ProfileImpl profile : profiles) { + profileDao.profiles.put(profile.getUserId(), new ProfileImpl(profile)); + } + } + + @Override + public void removeAll() throws TckRepositoryException { + profileDao.profiles.clear(); + } +} diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalTckModule.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalTckModule.java new file mode 100644 index 0000000000..261e3932bd --- /dev/null +++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalTckModule.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.local; + +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; + +import org.eclipse.che.api.local.storage.LocalStorage; +import org.eclipse.che.api.local.storage.LocalStorageFactory; +import org.eclipse.che.api.user.server.model.impl.ProfileImpl; +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.api.user.server.spi.ProfileDao; +import org.eclipse.che.api.user.server.spi.UserDao; +import org.eclipse.che.commons.test.tck.TckModule; +import org.eclipse.che.commons.test.tck.repository.TckRepository; + +import javax.inject.Singleton; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; + +import static java.util.Collections.emptySet; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Yevhenii Voevodin + */ +public class LocalTckModule extends TckModule { + + @Override + public void configure() { + // configuring local storage to deal with mocks + final LocalStorage storage = mock(LocalStorage.class); + when(storage.loadMap(any())).thenReturn(Collections.emptyMap()); + final LocalStorageFactory factory = mock(LocalStorageFactory.class); + try { + when(factory.create(any())).thenReturn(storage); + } catch (IOException x) { + throw new RuntimeException(x.getMessage(), x); + } + bind(LocalStorageFactory.class).toInstance(factory); + + bind(new TypeLiteral>() {}).annotatedWith(Names.named("codenvy.local.infrastructure.users")).toInstance(emptySet()); + + bind(new TypeLiteral>() {}).to(LocalUserTckRepository.class).in(Singleton.class); + bind(new TypeLiteral>() {}).to(LocalProfileTckRepository.class).in(Singleton.class); + + bind(UserDao.class).to(LocalUserDaoImpl.class).in(Singleton.class); + bind(ProfileDao.class).to(LocalProfileDaoImpl.class).in(Singleton.class); + } +} diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalUserTckRepository.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalUserTckRepository.java new file mode 100644 index 0000000000..0159e543b3 --- /dev/null +++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalUserTckRepository.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.local; + +import org.eclipse.che.api.user.server.model.impl.UserImpl; +import org.eclipse.che.commons.test.tck.repository.TckRepository; + +import javax.inject.Inject; +import java.util.Collection; + +/** + * @author Yevhenii Voevodin + */ +public class LocalUserTckRepository implements TckRepository { + + @Inject + private LocalUserDaoImpl userDao; + + @Override + public void createAll(Collection entities) { + for (UserImpl user : entities) { + userDao.users.put(user.getId(), new UserImpl(user)); + } + } + + @Override + public void removeAll() { + userDao.users.clear(); + } +} diff --git a/wsmaster/wsmaster-local/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule b/wsmaster/wsmaster-local/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule new file mode 100644 index 0000000000..3ea967f453 --- /dev/null +++ b/wsmaster/wsmaster-local/src/test/resources/META-INF/services/org.eclipse.che.commons.test.tck.TckModule @@ -0,0 +1 @@ +org.eclipse.che.api.local.LocalTckModule