REST APIs that allows saving/edit/delete/search/share devfiles (#17843)

* REST APIs that allows saving/edit/delete/search/share devfiles

Signed-off-by: Sergii Kabashniuk <skabashniuk@redhat.com>
7.20.x
Sergii Kabashniuk 2020-10-06 13:00:55 +03:00 committed by GitHub
parent 2bd0ea208f
commit f703f6fe1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 6714 additions and 353 deletions

View File

@ -123,6 +123,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory</artifactId>

View File

@ -163,6 +163,8 @@ public class WsMasterModule extends AbstractModule {
bind(WorkspaceEntityProvider.class);
bind(org.eclipse.che.api.workspace.server.TemporaryWorkspaceRemover.class);
bind(org.eclipse.che.api.workspace.server.WorkspaceService.class);
bind(org.eclipse.che.api.devfile.server.DevfileService.class);
bind(org.eclipse.che.api.devfile.server.UserDevfileEntityProvider.class);
install(new FactoryModuleBuilder().build(ServersCheckerFactory.class));
Multibinder<InternalEnvironmentProvisioner> internalEnvironmentProvisioners =
@ -300,6 +302,7 @@ public class WsMasterModule extends AbstractModule {
install(new org.eclipse.che.api.user.server.jpa.UserJpaModule());
install(new org.eclipse.che.api.workspace.server.jpa.WorkspaceJpaModule());
install(new org.eclipse.che.api.devfile.server.jpa.UserDevfileJpaModule());
bind(org.eclipse.che.api.user.server.CheUserCreator.class);
@ -361,6 +364,11 @@ public class WsMasterModule extends AbstractModule {
new org.eclipse.che.multiuser.permission.workspace.server.jpa
.MultiuserWorkspaceJpaModule());
install(new MultiUserWorkspaceActivityModule());
install(
new org.eclipse.che.multiuser.permission.devfile.server.jpa
.MultiuserUserDevfileJpaModule());
install(
new org.eclipse.che.multiuser.permission.devfile.server.UserDevfileApiPermissionsModule());
// Permission filters
bind(org.eclipse.che.multiuser.permission.system.SystemServicePermissionsFilter.class);
@ -375,7 +383,6 @@ public class WsMasterModule extends AbstractModule {
bind(org.eclipse.che.multiuser.permission.user.UserServicePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.logger.LoggerServicePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.devfile.DevfilePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.workspace.activity.ActivityPermissionsFilter.class);
bind(AdminPermissionInitializer.class).asEagerSingleton();
bind(

View File

@ -58,12 +58,14 @@
<class>org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl</class>
<class>org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl</class>
<class>org.eclipse.che.api.workspace.server.devfile.SerializableConverter</class>
<class>org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl</class>
<class>org.eclipse.che.api.ssh.server.model.impl.SshPairImpl</class>
<class>org.eclipse.che.multiuser.api.permission.server.model.impl.SystemPermissionsImpl</class>
<class>org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions</class>
<class>org.eclipse.che.multiuser.permission.workspace.server.model.impl.WorkerImpl</class>
<class>org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl</class>
<class>org.eclipse.che.multiuser.resource.spi.impl.FreeResourcesLimitImpl</class>
<class>org.eclipse.che.multiuser.resource.spi.impl.ResourceImpl</class>

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.core.model.workspace.devfile;
/** Devfile that is persisted in permanent storage. */
public interface UserDevfile {
/** Returns the identifier of this persisted devfile instance. It is mandatory and unique. */
String getId();
/** Returns the name of devfile. It is mandatory. */
String getName();
/**
* Returns the namespace also known as the account name. This name can be the name of the
* organization or the name of the user to which this devfile belong to. Namespace and name
* uniquely identify devfile.
*/
String getNamespace();
/** Returns description of devfile */
String getDescription();
/** Returns devfile content */
Devfile getDevfile();
}

View File

@ -83,6 +83,11 @@
<artifactId>che-core-api-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory</artifactId>
@ -178,6 +183,11 @@
<artifactId>che-multiuser-machine-authentication</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>

View File

@ -24,6 +24,9 @@ import static org.eclipse.che.multiuser.integration.jpa.cascaderemoval.TestObjec
import static org.eclipse.che.multiuser.integration.jpa.cascaderemoval.TestObjectsFactory.createUser;
import static org.eclipse.che.multiuser.integration.jpa.cascaderemoval.TestObjectsFactory.createWorker;
import static org.eclipse.che.multiuser.integration.jpa.cascaderemoval.TestObjectsFactory.createWorkspace;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DELETE;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.READ;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.UPDATE;
import static org.eclipse.che.multiuser.resource.spi.jpa.JpaFreeResourcesLimitDao.RemoveFreeResourcesLimitSubscriber;
import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertEquals;
@ -33,6 +36,7 @@ import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@ -54,6 +58,8 @@ 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.notification.EventService;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.factory.server.jpa.FactoryJpaModule;
import org.eclipse.che.api.factory.server.model.impl.FactoryImpl;
import org.eclipse.che.api.factory.server.spi.FactoryDao;
@ -106,6 +112,10 @@ import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.spi.MemberDao;
import org.eclipse.che.multiuser.organization.spi.impl.MemberImpl;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.permission.devfile.server.jpa.MultiuserUserDevfileJpaModule;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao.RemoveUserDevfilePermissionsBeforeUserRemovedEventSubscriber;
import org.eclipse.che.multiuser.permission.workspace.server.jpa.MultiuserWorkspaceJpaModule;
import org.eclipse.che.multiuser.permission.workspace.server.spi.WorkerDao;
import org.eclipse.che.multiuser.resource.api.AvailableResourcesProvider;
@ -143,6 +153,8 @@ public class JpaEntitiesCascadeRemovalTest {
private SshDao sshDao;
private FactoryDao factoryDao;
private WorkerDao workerDao;
private UserDevfilePermissionDao userDevfilePermissionDao;
private UserDevfileDao userDevfileDao;
private SignatureKeyDao signatureKeyDao;
private FreeResourcesLimitDao freeResourcesLimitDao;
private OrganizationManager organizationManager;
@ -195,6 +207,9 @@ public class JpaEntitiesCascadeRemovalTest {
private FreeResourcesLimitImpl freeResourcesLimit2;
private UserDevfileImpl devfile;
private UserDevfilePermissionImpl devfilePermission;
private H2JpaCleaner h2JpaCleaner;
@BeforeMethod
@ -221,6 +236,8 @@ public class JpaEntitiesCascadeRemovalTest {
install(new MultiuserWorkspaceJpaModule());
install(new MachineAuthModule());
install(new DevfileModule());
install(new MultiuserUserDevfileJpaModule());
bind(ExecutorServiceWrapper.class).to(NoopExecutorServiceWrapper.class);
bind(FreeResourcesLimitDao.class).to(JpaFreeResourcesLimitDao.class);
@ -305,6 +322,8 @@ public class JpaEntitiesCascadeRemovalTest {
workspaceDao = injector.getInstance(WorkspaceDao.class);
factoryDao = injector.getInstance(FactoryDao.class);
workerDao = injector.getInstance(WorkerDao.class);
userDevfileDao = injector.getInstance(UserDevfileDao.class);
userDevfilePermissionDao = injector.getInstance(UserDevfilePermissionDao.class);
signatureKeyDao = injector.getInstance(SignatureKeyDao.class);
freeResourcesLimitDao = injector.getInstance(FreeResourcesLimitDao.class);
organizationManager = injector.getInstance(OrganizationManager.class);
@ -334,10 +353,18 @@ public class JpaEntitiesCascadeRemovalTest {
assertTrue(preferenceDao.getPreferences(user.getId()).isEmpty());
assertTrue(sshDao.get(user.getId()).isEmpty());
assertTrue(workspaceDao.getByNamespace(account.getName(), 30, 0).isEmpty());
assertTrue(userDevfileDao.getByNamespace(account.getName(), 30, 0).isEmpty());
assertTrue(factoryDao.getByUser(user.getId(), 30, 0).isEmpty());
// Check workers and parent entity is removed
assertTrue(workspaceDao.getByNamespace(user2.getId(), 30, 0).isEmpty());
assertTrue(userDevfileDao.getByNamespace(user2.getId(), 30, 0).isEmpty());
assertEquals(workerDao.getWorkers(workspace3.getId(), 1, 0).getTotalItemsCount(), 0);
assertNull(
notFoundToNull(
() ->
userDevfilePermissionDao.getUserDevfilePermission(devfile.getId(), user2.getId())));
assertFalse(userDevfileDao.getById(devfile.getId()).isPresent());
// Permissions are removed
// Non-removed user permissions and stack are present
// Check existence of organizations
@ -391,6 +418,11 @@ public class JpaEntitiesCascadeRemovalTest {
assertNotNull(notFoundToNull(() -> organizationManager.getById(childOrganization.getId())));
assertNotNull(notFoundToNull(() -> organizationManager.getById(organization2.getId())));
assertNotNull(notFoundToNull(() -> signatureKeyDao.get(workspace2.getId())));
assertTrue(userDevfileDao.getById(devfile.getId()).isPresent());
assertNotNull(
notFoundToNull(
() ->
userDevfilePermissionDao.getUserDevfilePermission(devfile.getId(), user2.getId())));
assertFalse(
organizationResourcesDistributor.getResourcesCaps(childOrganization.getId()).isEmpty());
wipeTestData();
@ -399,7 +431,11 @@ public class JpaEntitiesCascadeRemovalTest {
@DataProvider(name = "beforeRemoveRollbackActions")
public Object[][] beforeRemoveActions() {
return new Class[][] {
{RemoveOrganizationOnLastUserRemovedEventSubscriber.class, BeforeUserRemovedEvent.class}
{RemoveOrganizationOnLastUserRemovedEventSubscriber.class, BeforeUserRemovedEvent.class},
{
RemoveUserDevfilePermissionsBeforeUserRemovedEventSubscriber.class,
BeforeUserRemovedEvent.class
}
};
}
@ -453,6 +489,13 @@ public class JpaEntitiesCascadeRemovalTest {
organizationResourcesDistributor.capResources(
childOrganization.getId(),
singletonList(new ResourceImpl(RamResourceType.ID, 1024, RamResourceType.UNIT)));
userDevfileDao.create(
devfile = TestObjectsFactory.createUserDevfile("id-dev1", "devfile1", account));
userDevfilePermissionDao.store(
devfilePermission =
new UserDevfilePermissionImpl(
devfile.getId(), user2.getId(), ImmutableList.of(READ, DELETE, UPDATE)));
}
private void prepareCreator(String userId) {
@ -477,6 +520,9 @@ public class JpaEntitiesCascadeRemovalTest {
workerDao.removeWorker(workspace3.getId(), user2.getId());
userDevfilePermissionDao.removeUserDevfilePermission(devfile.getId(), user2.getId());
userDevfileDao.remove(devfile.getId());
factoryDao.remove(factory1.getId());
factoryDao.remove(factory2.getId());

View File

@ -12,6 +12,8 @@
package org.eclipse.che.multiuser.integration.jpa.cascaderemoval;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import com.google.common.collect.ImmutableMap;
import java.security.KeyPair;
@ -22,6 +24,7 @@ import java.util.HashMap;
import java.util.Map;
import org.eclipse.che.account.shared.model.Account;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.factory.server.model.impl.AuthorImpl;
import org.eclipse.che.api.factory.server.model.impl.FactoryImpl;
import org.eclipse.che.api.ssh.server.model.impl.SshPairImpl;
@ -29,6 +32,16 @@ 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.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.multiuser.machine.authentication.server.signature.model.impl.SignatureKeyPairImpl;
import org.eclipse.che.multiuser.permission.workspace.server.model.impl.WorkerImpl;
import org.eclipse.che.multiuser.resource.spi.impl.FreeResourcesLimitImpl;
@ -112,5 +125,139 @@ public final class TestObjectsFactory {
return new SignatureKeyPairImpl(workspaceId, pair.getPublic(), pair.getPrivate());
}
public static UserDevfileImpl createUserDevfile(String id, String name, Account account) {
return new UserDevfileImpl(id, account, name, "descr", createDevfile(name));
}
public static DevfileImpl createDevfile(String name) {
SourceImpl source1 =
new SourceImpl(
"type1",
"http://location",
"branch1",
"point1",
"tag1",
"commit1",
"sparseCheckoutDir1");
ProjectImpl project1 = new ProjectImpl("project1", source1, "path1");
SourceImpl source2 =
new SourceImpl(
"type2",
"http://location",
"branch2",
"point2",
"tag2",
"commit2",
"sparseCheckoutDir2");
ProjectImpl project2 = new ProjectImpl("project2", source2, "path2");
ActionImpl action1 =
new ActionImpl("exec1", "component1", "run.sh", "/home/user/1", null, null);
ActionImpl action2 =
new ActionImpl("exec2", "component2", "run.sh", "/home/user/2", null, null);
CommandImpl command1 =
new CommandImpl(name + "-1", singletonList(action1), singletonMap("attr1", "value1"), null);
CommandImpl command2 =
new CommandImpl(name + "-2", singletonList(action2), singletonMap("attr2", "value2"), null);
EntrypointImpl entrypoint1 =
new EntrypointImpl(
"parentName1",
singletonMap("parent1", "selector1"),
"containerName1",
asList("command1", "command2"),
asList("arg1", "arg2"));
EntrypointImpl entrypoint2 =
new EntrypointImpl(
"parentName2",
singletonMap("parent2", "selector2"),
"containerName2",
asList("command3", "command4"),
asList("arg3", "arg4"));
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume1 =
new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name1", "path1");
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume2 =
new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name2", "path2");
EnvImpl env1 = new EnvImpl("name1", "value1");
EnvImpl env2 = new EnvImpl("name2", "value2");
EndpointImpl endpoint1 = new EndpointImpl("name1", 1111, singletonMap("key1", "value1"));
EndpointImpl endpoint2 = new EndpointImpl("name2", 2222, singletonMap("key2", "value2"));
ComponentImpl component1 =
new ComponentImpl(
"kubernetes",
"component1",
"eclipse/che-theia/0.0.1",
ImmutableMap.of("java.home", "/home/user/jdk11"),
"https://mysite.com/registry/somepath1",
"/dev.yaml",
"refcontent1",
ImmutableMap.of("app.kubernetes.io/component", "db"),
asList(entrypoint1, entrypoint2),
"image",
"256G",
"128M",
"2",
"130m",
false,
false,
singletonList("command"),
singletonList("arg"),
asList(volume1, volume2),
asList(env1, env2),
asList(endpoint1, endpoint2));
component1.setSelector(singletonMap("key1", "value1"));
ComponentImpl component2 =
new ComponentImpl(
"kubernetes",
"component2",
"eclipse/che-theia/0.0.1",
ImmutableMap.of(
"java.home",
"/home/user/jdk11aertwertert",
"java.boolean",
true,
"java.long",
123444L),
"https://mysite.com/registry/somepath2",
"/dev.yaml",
"refcontent2",
ImmutableMap.of("app.kubernetes.io/component", "webapp"),
asList(entrypoint1, entrypoint2),
"image",
"256G",
"256M",
"3",
"180m",
false,
false,
singletonList("command"),
singletonList("arg"),
asList(volume1, volume2),
asList(env1, env2),
asList(endpoint1, endpoint2));
component2.setSelector(singletonMap("key2", "value2"));
DevfileImpl devfile =
new DevfileImpl(
"0.0.1",
asList(project1, project2),
asList(component1, component2),
asList(command1, command2),
singletonMap("attribute1", "value1"),
new MetadataImpl(name));
return devfile;
}
private TestObjectsFactory() {}
}

View File

@ -74,6 +74,8 @@
<class>org.eclipse.che.multiuser.resource.spi.impl.ResourceImpl</class>
<class>org.eclipse.che.multiuser.resource.spi.impl.FreeResourcesLimitImpl</class>
<class>org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl</class>
<class>org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>

View File

@ -47,6 +47,11 @@
<artifactId>che-multiuser-api-resource</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>
@ -112,6 +117,11 @@
<artifactId>che-multiuser-machine-authentication</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>
@ -165,12 +175,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>analyze</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
<execution>
<goals>
<goal>unpack-dependencies</goal>
@ -178,9 +182,10 @@
<configuration>
<outputDirectory>${project.build.testOutputDirectory}</outputDirectory>
<includeArtifactIds>che-multiuser-api-resource,
che-multiuser-api-organization,
che-multiuser-api-permission,
che-multiuser-permission-workspace</includeArtifactIds>
che-multiuser-api-organization,
che-multiuser-api-permission,
che-multiuser-permission-devfile,
che-multiuser-permission-workspace</includeArtifactIds>
<includeScope>test</includeScope>
</configuration>
</execution>
@ -192,7 +197,7 @@
</goals>
<configuration>
<includeArtifactIds>che-core-sql-schema,
che-multiuser-sql-schema</includeArtifactIds>
che-multiuser-sql-schema</includeArtifactIds>
<includes>che-schema/</includes>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>

View File

@ -38,6 +38,7 @@ import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.commons.test.tck.JpaCleaner;
@ -68,6 +69,11 @@ import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.organization.spi.jpa.JpaMemberDao;
import org.eclipse.che.multiuser.organization.spi.jpa.JpaOrganizationDao;
import org.eclipse.che.multiuser.organization.spi.jpa.JpaOrganizationDistributedResourcesDao;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.workspace.server.model.impl.WorkerImpl;
import org.eclipse.che.multiuser.permission.workspace.server.spi.WorkerDao;
import org.eclipse.che.multiuser.permission.workspace.server.spi.jpa.JpaWorkerDao;
@ -159,6 +165,9 @@ public class MultiuserMySqlTckModule extends TckModule {
bind(new TypeLiteral<TckRepository<SignatureKeyPairImpl>>() {})
.toInstance(new JpaTckRepository<>(SignatureKeyPairImpl.class));
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
// dao
bind(OrganizationDao.class).to(JpaOrganizationDao.class);
bind(OrganizationDistributedResourcesDao.class)
@ -171,6 +180,12 @@ public class MultiuserMySqlTckModule extends TckModule {
bind(new TypeLiteral<PermissionsDao<MemberImpl>>() {}).to(JpaMemberDao.class);
bind(new TypeLiteral<AbstractPermissionsDomain<MemberImpl>>() {}).to(OrganizationDomain.class);
bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
bind(new TypeLiteral<AbstractPermissionsDomain<UserDevfilePermissionImpl>>() {})
.to(UserDevfileDomain.class);
bind(new TypeLiteral<TckRepository<UserDevfilePermission>>() {})
.toInstance(new JpaTckRepository<>(UserDevfilePermission.class));
// SHA-512 ecnryptor is faster than PBKDF2 so it is better for testing
bind(PasswordEncryptor.class).to(SHA512PasswordEncryptor.class).in(Singleton.class);

View File

@ -43,6 +43,8 @@
<class>org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl</class>
<class>org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl</class>
<class>org.eclipse.che.api.workspace.server.devfile.SerializableConverter</class>
<class>org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl</class>
<class>org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl</class>
<class>org.eclipse.che.api.workspace.server.model.impl.CommandImpl</class>
<class>org.eclipse.che.workspace.infrastructure.docker.snapshot.MachineSourceImpl</class>

View File

@ -47,6 +47,11 @@
<artifactId>che-multiuser-api-resource</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>
@ -107,6 +112,11 @@
<artifactId>che-multiuser-machine-authentication</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>
@ -174,6 +184,7 @@
<includeArtifactIds>che-multiuser-api-resource,
che-multiuser-api-organization,
che-multiuser-api-permission,
che-multiuser-permission-devfile,
che-multiuser-permission-workspace</includeArtifactIds>
<includeScope>test</includeScope>
</configuration>

View File

@ -38,6 +38,7 @@ import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.commons.test.tck.JpaCleaner;
@ -68,6 +69,11 @@ import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.organization.spi.jpa.JpaMemberDao;
import org.eclipse.che.multiuser.organization.spi.jpa.JpaOrganizationDao;
import org.eclipse.che.multiuser.organization.spi.jpa.JpaOrganizationDistributedResourcesDao;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.workspace.server.model.impl.WorkerImpl;
import org.eclipse.che.multiuser.permission.workspace.server.spi.WorkerDao;
import org.eclipse.che.multiuser.permission.workspace.server.spi.jpa.JpaWorkerDao;
@ -158,6 +164,9 @@ public class MultiuserPostgresqlTckModule extends TckModule {
bind(new TypeLiteral<TckRepository<SignatureKeyPairImpl>>() {})
.toInstance(new JpaTckRepository<>(SignatureKeyPairImpl.class));
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
// dao
bind(OrganizationDao.class).to(JpaOrganizationDao.class);
bind(OrganizationDistributedResourcesDao.class)
@ -170,6 +179,12 @@ public class MultiuserPostgresqlTckModule extends TckModule {
bind(new TypeLiteral<PermissionsDao<MemberImpl>>() {}).to(JpaMemberDao.class);
bind(new TypeLiteral<AbstractPermissionsDomain<MemberImpl>>() {}).to(OrganizationDomain.class);
bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
bind(new TypeLiteral<AbstractPermissionsDomain<UserDevfilePermissionImpl>>() {})
.to(UserDevfileDomain.class);
bind(new TypeLiteral<TckRepository<UserDevfilePermission>>() {})
.toInstance(new JpaTckRepository<>(UserDevfilePermission.class));
// SHA-512 ecnryptor is faster than PBKDF2 so it is better for testing
bind(PasswordEncryptor.class).to(SHA512PasswordEncryptor.class).in(Singleton.class);

View File

@ -43,6 +43,8 @@
<class>org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl</class>
<class>org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl</class>
<class>org.eclipse.che.api.workspace.server.devfile.SerializableConverter</class>
<class>org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl</class>
<class>org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl</class>
<class>org.eclipse.che.api.workspace.server.model.impl.CommandImpl</class>
<class>org.eclipse.che.workspace.infrastructure.docker.snapshot.MachineSourceImpl</class>

View File

@ -22,6 +22,18 @@
<artifactId>che-multiuser-permission-devfile</artifactId>
<name>Che Multiuser :: Devfile Permissions</name>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
@ -30,31 +42,94 @@
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-account</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-user</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-test</artifactId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-test</artifactId>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-persist</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-db</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission-shared</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
@ -67,7 +142,37 @@
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-shared</artifactId>
<artifactId>che-core-commons-inject</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-json</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-db-vendor-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-sql-schema</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-sql-schema</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@ -75,6 +180,11 @@
<artifactId>everrest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
@ -93,17 +203,20 @@
</dependencies>
<build>
<plugins>
<!-- Create the test jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<!-- compiler inlines constants, so it is impossible to find reference on dependency -->
<execution>
<id>analyze</id>
<goals>
<goal>test-jar</goal>
</goals>
<configuration>
<ignoredDependencies>
<ignoreDependency>org.eclipse.che.multiuser:che-multiuser-api-permission</ignoreDependency>
</ignoredDependencies>
<includes>
<include>**/spi/tck/*.*</include>
<include>**/TestObjectGenerator.*</include>
</includes>
</configuration>
</execution>
</executions>

View File

@ -1,49 +0,0 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile;
import javax.inject.Inject;
import javax.ws.rs.Path;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileService;
import org.eclipse.che.everrest.CheMethodInvokerFilter;
import org.everrest.core.Filter;
import org.everrest.core.resource.GenericResourceMethod;
/** Restricts access to methods of {@link DevfileService} by user's permissions. */
@Filter
@Path("/devfile{path:(/.*)?}")
public class DevfilePermissionsFilter extends CheMethodInvokerFilter {
public static final String GET_SCHEMA_METHOD = "getSchema";
private final WorkspaceManager workspaceManager;
@Inject
public DevfilePermissionsFilter(WorkspaceManager workspaceManager) {
this.workspaceManager = workspaceManager;
}
@Override
protected void filter(GenericResourceMethod genericResourceMethod, Object[] arguments)
throws ForbiddenException {
final String methodName = genericResourceMethod.getMethod().getName();
switch (methodName) {
// public methods
case GET_SCHEMA_METHOD:
return;
default:
throw new ForbiddenException("The user does not have permission to perform this operation");
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import org.eclipse.che.multiuser.api.permission.server.SuperPrivilegesChecker;
import org.eclipse.che.multiuser.api.permission.shared.model.PermissionsDomain;
import org.eclipse.che.multiuser.permission.devfile.server.filters.UserDevfilePermissionsFilter;
public class UserDevfileApiPermissionsModule extends AbstractModule {
@Override
protected void configure() {
bind(UserDevfilePermissionsFilter.class);
bind(UserDevfileCreatorPermissionsProvider.class).asEagerSingleton();
Multibinder.newSetBinder(
binder(),
PermissionsDomain.class,
Names.named(SuperPrivilegesChecker.SUPER_PRIVILEGED_DOMAINS))
.addBinding()
.to(UserDevfileDomain.class);
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server;
import java.util.ArrayList;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.notification.EventSubscriber;
import org.eclipse.che.api.devfile.shared.event.DevfileCreatedEvent;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Adds permissions for creator after user devfile creation */
@Singleton
public class UserDevfileCreatorPermissionsProvider implements EventSubscriber<DevfileCreatedEvent> {
private static final Logger LOG =
LoggerFactory.getLogger(UserDevfileCreatorPermissionsProvider.class);
private final UserDevfilePermissionDao userDevfilePermissionDao;
private final EventService eventService;
@Inject
public UserDevfileCreatorPermissionsProvider(
EventService eventService, UserDevfilePermissionDao userDevfilePermissionDao) {
this.userDevfilePermissionDao = userDevfilePermissionDao;
this.eventService = eventService;
}
@PostConstruct
void subscribe() {
eventService.subscribe(this);
}
@PreDestroy
void unsubscribe() {
eventService.unsubscribe(this);
}
@Override
public void onEvent(DevfileCreatedEvent event) {
try {
userDevfilePermissionDao.store(
new UserDevfilePermissionImpl(
event.getUserDevfile().getId(),
EnvironmentContext.getCurrent().getSubject().getUserId(),
new ArrayList<>(new UserDevfileDomain().getAllowedActions())));
} catch (ServerException e) {
LOG.error(
"Can't add creator's permissions for user devfile with id '"
+ event.getUserDevfile().getId()
+ "'",
e);
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
/** Domain for storing devfile's permissions */
public class UserDevfileDomain extends AbstractPermissionsDomain<UserDevfilePermissionImpl> {
public static final String READ = "read";
public static final String DELETE = "delete";
public static final String UPDATE = "update";
public static final String DOMAIN_ID = "devfile";
public UserDevfileDomain() {
super(DOMAIN_ID, ImmutableList.of(READ, DELETE, UPDATE));
}
@Override
public UserDevfilePermissionImpl doCreateInstance(
String userId, String instanceId, List<String> allowedActions) {
return new UserDevfilePermissionImpl(instanceId, userId, allowedActions);
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.filters;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DELETE;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DOMAIN_ID;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.READ;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.UPDATE;
import com.google.common.annotations.VisibleForTesting;
import javax.inject.Inject;
import javax.ws.rs.Path;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.devfile.server.DevfileService;
import org.eclipse.che.api.devfile.server.UserDevfileManager;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.everrest.CheMethodInvokerFilter;
import org.everrest.core.Filter;
import org.everrest.core.resource.GenericResourceMethod;
/**
* Restricts access to methods of {@link DevfileService} by users' permissions.
*
* <p>Filter contains rules for protecting of all methods of {@link DevfileService}.<br>
* In case when requested method is unknown filter throws {@link ForbiddenException}
*/
@Filter
@Path("/devfile{path:(/.*)?}")
public class UserDevfilePermissionsFilter extends CheMethodInvokerFilter {
private final UserDevfileManager userDevfileManager;
@Inject
public UserDevfilePermissionsFilter(UserDevfileManager userDevfileManager) {
this.userDevfileManager = userDevfileManager;
}
@Override
public void filter(GenericResourceMethod genericResourceMethod, Object[] arguments)
throws ForbiddenException {
final String methodName = genericResourceMethod.getMethod().getName();
switch (methodName) {
case "getById":
doCheckPermission(DOMAIN_ID, ((String) arguments[0]), READ);
break;
case "update":
doCheckPermission(DOMAIN_ID, ((String) arguments[0]), UPDATE);
break;
case "delete":
doCheckPermission(DOMAIN_ID, ((String) arguments[0]), DELETE);
break;
case "createFromDevfileYaml":
case "createFromUserDevfile":
case "getUserDevfiles":
case "getSchema":
return;
default:
throw new ForbiddenException("The user does not have permission to perform this operation");
}
}
@VisibleForTesting
void doCheckPermission(String domain, String instance, String action) throws ForbiddenException {
EnvironmentContext.getCurrent().getSubject().checkPermission(domain, instance, action);
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.jpa;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import org.eclipse.che.api.devfile.server.RemoveUserDevfileBeforeAccountRemovedEventSubscriber;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
import org.eclipse.che.multiuser.api.permission.server.spi.PermissionsDao;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao.RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao.RemoveUserDevfilePermissionsBeforeUserRemovedEventSubscriber;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.MultiuserJpaUserDevfileDao;
public class MultiuserUserDevfileJpaModule extends AbstractModule {
@Override
protected void configure() {
bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
bind(UserDevfileDao.class).to(MultiuserJpaUserDevfileDao.class);
bind(RemoveUserDevfileBeforeAccountRemovedEventSubscriber.class).asEagerSingleton();
bind(RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber.class)
.asEagerSingleton();
bind(RemoveUserDevfilePermissionsBeforeUserRemovedEventSubscriber.class).asEagerSingleton();
bind(new TypeLiteral<AbstractPermissionsDomain<UserDevfilePermissionImpl>>() {})
.to(UserDevfileDomain.class);
Multibinder<PermissionsDao<? extends AbstractPermissions>> daos =
Multibinder.newSetBinder(
binder(), new TypeLiteral<PermissionsDao<? extends AbstractPermissions>>() {});
daos.addBinding().to(JpaUserDevfilePermissionDao.class);
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.model;
import java.util.List;
public interface UserDevfilePermission {
/** Returns user id */
String getUserId();
/** Returns user devfile id */
String getUserDevfileId();
/** Returns list of user devfile actions which can be performed by current user */
List<String> getActions();
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.model.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
/** Data object for {@link UserDevfilePermission} */
@Entity(name = "UserDevfilePermission")
@NamedQueries({
@NamedQuery(
name = "UserDevfilePermission.getByUserDevfileId",
query =
"SELECT permission "
+ "FROM UserDevfilePermission permission "
+ "WHERE permission.userDevfileId = :userDevfileId "),
@NamedQuery(
name = "UserDevfilePermission.getCountByUserDevfileId",
query =
"SELECT COUNT(permission) "
+ "FROM UserDevfilePermission permission "
+ "WHERE permission.userDevfileId = :userDevfileId "),
@NamedQuery(
name = "UserDevfilePermission.getByUserId",
query =
"SELECT permission "
+ "FROM UserDevfilePermission permission "
+ "WHERE permission.userId = :userId "),
@NamedQuery(
name = "UserDevfilePermission.getByUserAndUserDevfileId",
query =
"SELECT permission "
+ "FROM UserDevfilePermission permission "
+ "WHERE permission.userId = :userId "
+ "AND permission.userDevfileId = :userDevfileId ",
hints = {@QueryHint(name = "eclipselink.query-results-cache", value = "true")})
})
@Table(name = "che_userdevfile_permissions")
public class UserDevfilePermissionImpl extends AbstractPermissions
implements UserDevfilePermission {
@Column(name = "userdevfile_id")
private String userDevfileId;
@ManyToOne
@JoinColumn(name = "userdevfile_id", insertable = false, updatable = false)
private UserDevfileImpl userDevfile;
@ElementCollection(fetch = FetchType.EAGER)
@Column(name = "actions")
@CollectionTable(
name = "che_userdevfile_permissions_actions",
joinColumns = @JoinColumn(name = "userdevfile_permissions_id"))
protected List<String> actions;
public UserDevfilePermissionImpl() {}
public UserDevfilePermissionImpl(String userDevfileId, String userId, List<String> actions) {
super(userId);
this.userDevfileId = userDevfileId;
if (actions != null) {
this.actions = new ArrayList<>(actions);
}
}
public UserDevfilePermissionImpl(UserDevfilePermission userDevfilePermission) {
this(
userDevfilePermission.getUserDevfileId(),
userDevfilePermission.getUserId(),
userDevfilePermission.getActions());
}
@Override
public String getInstanceId() {
return userDevfileId;
}
@Override
public String getDomainId() {
return UserDevfileDomain.DOMAIN_ID;
}
@Override
public List<String> getActions() {
return actions;
}
@Override
public String getUserDevfileId() {
return userDevfileId;
}
@Override
public String toString() {
return "UserDevfilePermissionImpl{"
+ "userDevfileId='"
+ userDevfileId
+ '\''
+ ", userDevfile="
+ userDevfile
+ ", actions="
+ actions
+ "} "
+ super.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
UserDevfilePermissionImpl that = (UserDevfilePermissionImpl) o;
return Objects.equals(userDevfileId, that.userDevfileId)
&& Objects.equals(actions, that.actions);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), userDevfileId, actions);
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi;
import java.util.List;
import java.util.Optional;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
/** Defines data access object contract for {@link UserDevfilePermissionImpl}. */
public interface UserDevfilePermissionDao {
/**
* Stores (adds or updates) UserDevfilePermissions.
*
* @param userDevfilePermissions userDevfilePermissions to store
* @return optional with updated userDevfilePermissions, other way empty optional must be returned
* @throws NullPointerException when {@code userDevfilePermissions} is null
* @throws ServerException when any other error occurs during userDevfilePermissions storing
*/
Optional<UserDevfilePermissionImpl> store(UserDevfilePermissionImpl userDevfilePermissions)
throws ServerException;
/**
* Gets userDevfilePermissions by user and userDevfileId
*
* @param userDevfileId user devfile identifier
* @param userId user identifier
* @return userDevfilePermissions instance, never null
* @throws NullPointerException when {@code userDevfileId} or {@code userId} is null
* @throws NotFoundException when permission with given {@code userDevfileId} and {@code userId}
* was not found
* @throws ServerException when any other error occurs during permission fetching
*/
UserDevfilePermissionImpl getUserDevfilePermission(String userDevfileId, String userId)
throws ServerException, NotFoundException;
/**
* Removes userDevfilePermissions
*
* <p>Doesn't throw an exception when userDevfilePermissions with given {@code userDevfileId} and
* {@code userId} does not exist
*
* @param userDevfileId workspace identifier
* @param userId user identifier
* @throws NullPointerException when {@code userDevfileId} or {@code userId} is null
* @throws ServerException when any other error occurs during userDevfilePermissions removing
*/
void removeUserDevfilePermission(String userDevfileId, String userId) throws ServerException;
/**
* Gets userDevfilePermissions by user devfile id.
*
* @param userDevfileId user devfile identifier
* @param maxItems the maximum number of userDevfilePermissions to return
* @param skipCount the number of userDevfilePermissions to skip
* @return list of userDevfilePermissions instance
* @throws NullPointerException when {@code userDevfileId} is null
* @throws ServerException when any other error occurs during userDevfilePermissions fetching
*/
Page<UserDevfilePermissionImpl> getUserDevfilePermission(
String userDevfileId, int maxItems, long skipCount) throws ServerException;
/**
* Gets UserDevfilePermissions by user
*
* @param userId user identifier
* @return list of UserDevfilePermissions instance
* @throws NullPointerException when {@code userId} is null
* @throws ServerException when any other error occurs during UserDevfilePermissions fetching
*/
List<UserDevfilePermissionImpl> getUserDevfilePermissionByUser(String userId)
throws ServerException;
}

View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.persist.Transactional;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.devfile.server.event.BeforeDevfileRemovedEvent;
import org.eclipse.che.api.user.server.event.BeforeUserRemovedEvent;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.core.db.cascade.CascadeEventSubscriber;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.api.permission.server.jpa.AbstractJpaPermissionsDao;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
/** JPA implementation of {@link UserDevfilePermissionDao}. */
public class JpaUserDevfilePermissionDao
extends AbstractJpaPermissionsDao<UserDevfilePermissionImpl>
implements UserDevfilePermissionDao {
@Inject
public JpaUserDevfilePermissionDao(
AbstractPermissionsDomain<UserDevfilePermissionImpl> supportedDomain) {
super(supportedDomain);
}
@Override
public UserDevfilePermissionImpl get(String userId, String instanceId)
throws ServerException, NotFoundException {
requireNonNull(instanceId, "User devfile identifier required");
requireNonNull(userId, "User identifier required");
try {
return new UserDevfilePermissionImpl(getEntity(wildcardToNull(userId), instanceId));
} catch (RuntimeException x) {
throw new ServerException(x.getLocalizedMessage(), x);
}
}
@Override
public List<UserDevfilePermissionImpl> getByUser(String userId) throws ServerException {
requireNonNull(userId, "User identifier required");
return doGetByUser(wildcardToNull(userId))
.stream()
.map(UserDevfilePermissionImpl::new)
.collect(toList());
}
@Override
@Transactional
public Page<UserDevfilePermissionImpl> getByInstance(
String instanceId, int maxItems, long skipCount) throws ServerException {
requireNonNull(instanceId, "User devfile identifier required");
checkArgument(
skipCount <= Integer.MAX_VALUE,
"The number of items to skip can't be greater than " + Integer.MAX_VALUE);
try {
final EntityManager entityManager = managerProvider.get();
final List<UserDevfilePermissionImpl> permissions =
entityManager
.createNamedQuery(
"UserDevfilePermission.getByUserDevfileId", UserDevfilePermissionImpl.class)
.setParameter("userDevfileId", instanceId)
.setMaxResults(maxItems)
.setFirstResult((int) skipCount)
.getResultList()
.stream()
.map(UserDevfilePermissionImpl::new)
.collect(toList());
final Long permissionsCount =
entityManager
.createNamedQuery("UserDevfilePermission.getCountByUserDevfileId", Long.class)
.setParameter("userDevfileId", instanceId)
.getSingleResult();
return new Page<>(permissions, skipCount, maxItems, permissionsCount);
} catch (RuntimeException e) {
throw new ServerException(e.getLocalizedMessage(), e);
}
}
@Override
protected UserDevfilePermissionImpl getEntity(String userId, String instanceId)
throws NotFoundException, ServerException {
try {
return doGet(userId, instanceId);
} catch (NoResultException e) {
throw new NotFoundException(
format("User %s does not have permissions assigned to devfile %s.", instanceId, userId));
} catch (RuntimeException e) {
throw new ServerException(e.getMessage(), e);
}
}
@Override
public UserDevfilePermissionImpl getUserDevfilePermission(String userDevfileId, String userId)
throws ServerException, NotFoundException {
return new UserDevfilePermissionImpl(get(userId, userDevfileId));
}
@Override
public void removeUserDevfilePermission(String userDevfileId, String userId)
throws ServerException {
try {
super.remove(userId, userDevfileId);
} catch (NotFoundException e) {
throw new ServerException(e);
}
}
@Override
public Page<UserDevfilePermissionImpl> getUserDevfilePermission(
String userDevfileId, int maxItems, long skipCount) throws ServerException {
return getByInstance(userDevfileId, maxItems, skipCount);
}
@Override
public List<UserDevfilePermissionImpl> getUserDevfilePermissionByUser(String userId)
throws ServerException {
return getByUser(userId);
}
@Transactional
protected UserDevfilePermissionImpl doGet(String userId, String instanceId) {
return managerProvider
.get()
.createNamedQuery(
"UserDevfilePermission.getByUserAndUserDevfileId", UserDevfilePermissionImpl.class)
.setParameter("userDevfileId", instanceId)
.setParameter("userId", userId)
.getSingleResult();
}
@Transactional
protected List<UserDevfilePermissionImpl> doGetByUser(@Nullable String userId)
throws ServerException {
try {
return managerProvider
.get()
.createNamedQuery("UserDevfilePermission.getByUserId", UserDevfilePermissionImpl.class)
.setParameter("userId", userId)
.getResultList();
} catch (RuntimeException e) {
throw new ServerException(e.getLocalizedMessage(), e);
}
}
@Singleton
public static class RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber
extends CascadeEventSubscriber<BeforeDevfileRemovedEvent> {
private static final int PAGE_SIZE = 100;
@Inject private EventService eventService;
@Inject private UserDevfilePermissionDao userDevfilePermissionDao;
@PostConstruct
public void subscribe() {
eventService.subscribe(this, BeforeDevfileRemovedEvent.class);
}
@PreDestroy
public void unsubscribe() {
eventService.unsubscribe(this, BeforeDevfileRemovedEvent.class);
}
@Override
public void onCascadeEvent(BeforeDevfileRemovedEvent event) throws Exception {
removeUserDevfilePermissions(event.getUserDevfile().getId(), PAGE_SIZE);
}
@VisibleForTesting
void removeUserDevfilePermissions(String userDevfileId, int pageSize) throws ServerException {
Page<UserDevfilePermissionImpl> permissionsPage;
do {
// skip count always equals to 0 because elements will be shifted after removing previous
// items
permissionsPage =
userDevfilePermissionDao.getUserDevfilePermission(userDevfileId, pageSize, 0);
for (UserDevfilePermissionImpl permission : permissionsPage.getItems()) {
userDevfilePermissionDao.removeUserDevfilePermission(
permission.getInstanceId(), permission.getUserId());
}
} while (permissionsPage.hasNextPage());
}
}
@Singleton
public static class RemoveUserDevfilePermissionsBeforeUserRemovedEventSubscriber
extends CascadeEventSubscriber<BeforeUserRemovedEvent> {
@Inject private EventService eventService;
@Inject private UserDevfilePermissionDao userDevfilePermissionDao;
@PostConstruct
public void subscribe() {
eventService.subscribe(this, BeforeUserRemovedEvent.class);
}
@PreDestroy
public void unsubscribe() {
eventService.unsubscribe(this, BeforeUserRemovedEvent.class);
}
@Override
public void onCascadeEvent(BeforeUserRemovedEvent event) throws Exception {
for (UserDevfilePermissionImpl permission :
userDevfilePermissionDao.getUserDevfilePermissionByUser(event.getUser().getId())) {
userDevfilePermissionDao.removeUserDevfilePermission(
permission.getInstanceId(), permission.getUserId());
}
}
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.subject.Subject;
/** JPA based implementation of {@link UserDevfileDao}. */
@Singleton
public class MultiuserJpaUserDevfileDao extends JpaUserDevfileDao {
@Inject
public MultiuserJpaUserDevfileDao(
Provider<EntityManager> managerProvider, AccountDao accountDao, EventService eventService) {
super(managerProvider, accountDao, eventService);
}
@Override
public Page<UserDevfile> getDevfiles(
int maxItems,
int skipCount,
List<Pair<String, String>> filter,
List<Pair<String, String>> order)
throws ServerException {
checkArgument(maxItems > 0, "The number of items has to be positive.");
checkArgument(
skipCount >= 0,
"The number of items to skip can't be negative or greater than " + Integer.MAX_VALUE);
final Subject subject = EnvironmentContext.getCurrent().getSubject();
if (subject.isAnonymous()) {
throw new ServerException("Unexpected state. Current user is not set.");
}
return doGetDevfiles(
maxItems,
skipCount,
filter,
order,
() ->
MultiuserUserDevfileSearchQueryBuilder.newBuilder(managerProvider.get())
.withUserId(subject.getUserId()));
}
public static class MultiuserUserDevfileSearchQueryBuilder
extends JpaUserDevfileDao.UserDevfileSearchQueryBuilder {
MultiuserUserDevfileSearchQueryBuilder(EntityManager entityManager) {
super(entityManager);
}
public MultiuserUserDevfileSearchQueryBuilder withUserId(String userId) {
params.put("userId", userId);
return this;
}
public static MultiuserUserDevfileSearchQueryBuilder newBuilder(EntityManager entityManager) {
return new MultiuserUserDevfileSearchQueryBuilder(entityManager);
}
@Override
public JpaUserDevfileDao.UserDevfileSearchQueryBuilder withFilter(
List<Pair<String, String>> filter) {
super.withFilter(filter);
if (this.filter.isEmpty()) {
this.filter = "WHERE permission.userId = :userId AND 'read' MEMBER OF permission.actions";
} else {
this.filter += " AND permission.userId = :userId AND 'read' MEMBER OF permission.actions";
}
return this;
}
@Override
public TypedQuery<Long> buildCountQuery() {
StringBuilder query =
new StringBuilder()
.append("SELECT ")
.append(" COUNT(userdevfile) ")
.append("FROM UserDevfilePermission permission ")
.append("LEFT JOIN permission.userDevfile userdevfile ")
.append(filter);
TypedQuery<Long> typedQuery = entityManager.createQuery(query.toString(), Long.class);
params.forEach(typedQuery::setParameter);
return typedQuery;
}
@Override
public TypedQuery<UserDevfileImpl> buildSelectItemsQuery() {
StringBuilder query =
new StringBuilder()
.append("SELECT ")
.append(" userdevfile ")
.append("FROM UserDevfilePermission permission ")
.append("LEFT JOIN permission.userDevfile userdevfile ")
.append(filter)
.append(order);
TypedQuery<UserDevfileImpl> typedQuery =
entityManager
.createQuery(query.toString(), UserDevfileImpl.class)
.setFirstResult(skipCount)
.setMaxResults(maxItems);
params.forEach((k, v) -> typedQuery.setParameter(k, v));
return typedQuery;
}
}
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import com.google.common.collect.ImmutableMap;
import org.eclipse.che.account.shared.model.Account;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.devfile.server.DtoConverter;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
public class TestObjectGenerator {
public static final String TEST_CHE_NAMESPACE = "user";
public static final String CURRENT_USER_ID = NameGenerator.generate("usrid", 6);
public static final Subject TEST_SUBJECT =
new SubjectImpl(TEST_CHE_NAMESPACE, CURRENT_USER_ID, "token", false);
public static final String USER_DEVFILE_ID = NameGenerator.generate("usrd", 16);
public static final AccountImpl TEST_ACCOUNT =
new AccountImpl("acc-id042u3ui3oi", TEST_CHE_NAMESPACE, "test");
public static UserDevfileDto createUserDevfileDto() {
return DtoConverter.asDto(createUserDevfile(NameGenerator.generate("name", 6)));
}
public static UserDevfileImpl createUserDevfile() {
return createUserDevfile(NameGenerator.generate("name", 6));
}
public static UserDevfileImpl createUserDevfile(String name) {
return createUserDevfile(NameGenerator.generate("id", 6), name);
}
public static UserDevfileImpl createUserDevfile(String id, String name) {
return new UserDevfileImpl(id, TEST_ACCOUNT, name, "devfile description", createDevfile(name));
}
public static UserDevfileImpl createUserDevfile(String id, Account account, String name) {
return new UserDevfileImpl(id, account, name, "devfile description", createDevfile(name));
}
public static DevfileImpl createDevfile(String name) {
return createDevfile(name, "rosetta-");
}
public static DevfileImpl createDevfile(String name, String generatedName) {
SourceImpl source1 =
new SourceImpl(
"type1",
"http://location",
"branch1",
"point1",
"tag1",
"commit1",
"sparseCheckoutDir1");
ProjectImpl project1 = new ProjectImpl("project1", source1, "path1");
SourceImpl source2 =
new SourceImpl(
"type2",
"http://location",
"branch2",
"point2",
"tag2",
"commit2",
"sparseCheckoutDir2");
ProjectImpl project2 = new ProjectImpl("project2", source2, "path2");
ActionImpl action1 =
new ActionImpl("exec1", "component1", "run.sh", "/home/user/1", null, null);
ActionImpl action2 =
new ActionImpl("exec2", "component2", "run.sh", "/home/user/2", null, null);
CommandImpl command1 =
new CommandImpl(name + "-1", singletonList(action1), singletonMap("attr1", "value1"), null);
CommandImpl command2 =
new CommandImpl(name + "-2", singletonList(action2), singletonMap("attr2", "value2"), null);
EntrypointImpl entrypoint1 =
new EntrypointImpl(
"parentName1",
singletonMap("parent1", "selector1"),
"containerName1",
asList("command1", "command2"),
asList("arg1", "arg2"));
EntrypointImpl entrypoint2 =
new EntrypointImpl(
"parentName2",
singletonMap("parent2", "selector2"),
"containerName2",
asList("command3", "command4"),
asList("arg3", "arg4"));
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume1 =
new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name1", "path1");
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume2 =
new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name2", "path2");
EnvImpl env1 = new EnvImpl("name1", "value1");
EnvImpl env2 = new EnvImpl("name2", "value2");
EndpointImpl endpoint1 = new EndpointImpl("name1", 1111, singletonMap("key1", "value1"));
EndpointImpl endpoint2 = new EndpointImpl("name2", 2222, singletonMap("key2", "value2"));
ComponentImpl component1 =
new ComponentImpl(
"kubernetes",
"component1",
"eclipse/che-theia/0.0.1",
ImmutableMap.of("java.home", "/home/user/jdk11"),
"https://mysite.com/registry/somepath1",
"/dev.yaml",
"refcontent1",
ImmutableMap.of("app.kubernetes.io/component", "db"),
asList(entrypoint1, entrypoint2),
"image",
"256G",
"128M",
"2",
"130m",
false,
false,
singletonList("command"),
singletonList("arg"),
asList(volume1, volume2),
asList(env1, env2),
asList(endpoint1, endpoint2));
component1.setSelector(singletonMap("key1", "value1"));
ComponentImpl component2 =
new ComponentImpl(
"kubernetes",
"component2",
"eclipse/che-theia/0.0.1",
ImmutableMap.of(
"java.home",
"/home/user/jdk11aertwertert",
"java.boolean",
true,
"java.long",
123444L),
"https://mysite.com/registry/somepath2",
"/dev.yaml",
"refcontent2",
ImmutableMap.of("app.kubernetes.io/component", "webapp"),
asList(entrypoint1, entrypoint2),
"image",
"256G",
"256M",
"3",
"180m",
false,
false,
singletonList("command"),
singletonList("arg"),
asList(volume1, volume2),
asList(env1, env2),
asList(endpoint1, endpoint2));
component2.setSelector(singletonMap("key2", "value2"));
MetadataImpl metadata = new MetadataImpl(name);
metadata.setGenerateName(generatedName);
DevfileImpl devfile =
new DevfileImpl(
"0.0.1",
asList(project1, project2),
asList(component1, component2),
asList(command1, command2),
singletonMap("attribute1", "value1"),
metadata);
return devfile;
}
}

View File

@ -0,0 +1,320 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.filters;
import static com.jayway.restassured.RestAssured.given;
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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.testng.Assert.assertEquals;
import com.jayway.restassured.response.Response;
import java.util.Collections;
import java.util.HashSet;
import org.eclipse.che.api.core.BadRequestException;
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.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.devfile.server.DevfileService;
import org.eclipse.che.api.devfile.server.UserDevfileManager;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileEntityProvider;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** Tests for {@link UserDevfilePermissionsFilter}. */
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class UserDevfilePermissionsFilterTest {
private static final String USERNAME = "userok";
ApiExceptionMapper mapper;
CheJsonProvider jsonProvider = new CheJsonProvider(new HashSet<>());
private DevfileEntityProvider devfileEntityProvider =
new DevfileEntityProvider(
new DevfileParser(
new DevfileSchemaValidator(new DevfileSchemaProvider()),
new DevfileIntegrityValidator(Collections.emptyMap())));
@SuppressWarnings("unused")
private static final EnvironmentFilter FILTER = new EnvironmentFilter();
@Mock private static Subject subject;
@Mock private UserDevfileManager userDevfileManager;
private UserDevfilePermissionsFilter permissionsFilter;
@Mock private DevfileService devfileService;
private UserDevfileDto userDevfileDto = TestObjectGenerator.createUserDevfileDto();
private UserDevfileImpl userDevfile =
new UserDevfileImpl(userDevfileDto, TestObjectGenerator.TEST_ACCOUNT);
//
@BeforeMethod
public void setUp() throws Exception {
lenient().when(subject.getUserName()).thenReturn(USERNAME);
lenient().when(userDevfileManager.getById(any())).thenReturn(userDevfile);
permissionsFilter = spy(new UserDevfilePermissionsFilter(userDevfileManager));
lenient()
.doThrow(new ForbiddenException(""))
.when(subject)
.checkPermission(anyString(), anyString(), anyString());
}
@Test
public void shouldNotCheckAnyPermissionOnDevfileCreate() {
// given
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(userDevfileDto)
.when()
.post(SECURE_PATH + "/devfile/");
// then
assertEquals(response.getStatusCode(), 204);
verifyZeroInteractions(subject);
}
@Test
public void shouldNotCheckAnyPermissionOnDevfileSearch()
throws BadRequestException, ForbiddenException, NotFoundException, ServerException {
// given
Mockito.when(devfileService.getUserDevfiles(any(), any(), any()))
.thenReturn(javax.ws.rs.core.Response.ok().build());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile/search");
// then
assertEquals(response.getStatusCode(), 200);
verifyZeroInteractions(subject);
}
@Test
public void shouldNotCheckAnyPermissionOnDevfileSchema()
throws BadRequestException, ForbiddenException, NotFoundException, ServerException {
// given
Mockito.when(devfileService.getSchema()).thenReturn(javax.ws.rs.core.Response.ok().build());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile");
// then
assertEquals(response.getStatusCode(), 200);
verifyZeroInteractions(subject);
}
@Test
public void shouldCheckReadPermissionsOnFetchingUserDevfileById() throws Exception {
// given
Mockito.when(devfileService.getById(eq(userDevfileDto.getId()))).thenReturn(userDevfileDto);
doNothing()
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 200);
verify(devfileService).getById(eq(userDevfileDto.getId()));
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
}
@Test
public void shouldBeAbleToFailOnCheckPermissionDevfileReadByID() throws ForbiddenException {
// given
doThrow(new ForbiddenException("forbidden"))
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 403);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
}
@Test
public void shouldChecksPermissionDevfileUpdate() throws ForbiddenException {
// given
doNothing()
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(userDevfileDto)
.when()
.put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 204);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
}
@Test
public void shouldBeAbleToFailOnCheckPermissionDevfileUpdate() throws ForbiddenException {
// given
doThrow(new ForbiddenException("forbidden"))
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(userDevfileDto)
.when()
.put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 403);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
}
@Test
public void shouldChecksPermissionDevfileDelete() throws ForbiddenException {
// given
doNothing()
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 204);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
}
@Test
public void shouldBeAbleToFailOnCheckPermissionDevfileDelete() throws ForbiddenException {
// given
doThrow(new ForbiddenException("forbidden"))
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 403);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(subject);
}
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.jpa;
import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator.createUserDevfile;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DELETE;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.READ;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.UPDATE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.MultiuserJpaUserDevfileDao;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MultiuserJpaUserDevfileDaoTest {
private TckResourcesCleaner tckResourcesCleaner;
private EntityManager manager;
private MultiuserJpaUserDevfileDao dao;
private List<UserDevfilePermissionImpl> permissions;
private List<UserImpl> users;
private List<UserDevfileImpl> userDevfiles;
private List<AccountImpl> accounts;
@BeforeClass
public void setupEntities() throws Exception {
permissions =
ImmutableList.of(
new UserDevfilePermissionImpl(
"devfile_id1", "user1", Arrays.asList(READ, DELETE, UPDATE)),
new UserDevfilePermissionImpl("devfile_id2", "user1", Arrays.asList(READ, UPDATE)),
new UserDevfilePermissionImpl("devfile_id3", "user1", Arrays.asList(DELETE, UPDATE)),
new UserDevfilePermissionImpl(
"devfile_id1", "user2", Arrays.asList(READ, DELETE, UPDATE)));
users =
ImmutableList.of(
new UserImpl("user1", "user1@com.com", "usr1"),
new UserImpl("user2", "user2@com.com", "usr2"));
accounts =
ImmutableList.of(
new AccountImpl("acc-1", NameGenerator.generate("account", 6), "user"),
new AccountImpl("acc-2", NameGenerator.generate("account", 6), "user"));
userDevfiles =
ImmutableList.of(
createUserDevfile("devfile_id1", accounts.get(0), generate("name", 6)),
createUserDevfile("devfile_id2", accounts.get(0), generate("name", 6)),
createUserDevfile("devfile_id3", accounts.get(0), generate("name", 6)));
Injector injector = Guice.createInjector(new UserDevfileTckModule());
manager = injector.getInstance(EntityManager.class);
dao = injector.getInstance(MultiuserJpaUserDevfileDao.class);
tckResourcesCleaner = injector.getInstance(TckResourcesCleaner.class);
}
@BeforeMethod
public void setUp() throws Exception {
manager.getTransaction().begin();
users.stream().map(UserImpl::new).forEach(manager::persist);
accounts.stream().map(AccountImpl::new).forEach(manager::persist);
userDevfiles.stream().map(UserDevfileImpl::new).forEach(manager::persist);
permissions.stream().map(UserDevfilePermissionImpl::new).forEach(manager::persist);
manager.getTransaction().commit();
manager.clear();
}
@AfterMethod
public void cleanup() {
manager.getTransaction().begin();
manager
.createQuery("SELECT e FROM UserDevfilePermission e", UserDevfilePermissionImpl.class)
.getResultList()
.forEach(manager::remove);
manager
.createQuery("SELECT w FROM UserDevfile w", UserDevfileImpl.class)
.getResultList()
.forEach(manager::remove);
manager
.createQuery("SELECT a FROM Account a", AccountImpl.class)
.getResultList()
.forEach(manager::remove);
manager
.createQuery("SELECT u FROM Usr u", UserImpl.class)
.getResultList()
.forEach(manager::remove);
manager.getTransaction().commit();
}
@Test
public void shouldGetTotalWorkspaceCount() throws ServerException {
assertEquals(dao.getTotalCount(), 3);
}
@AfterClass
public void shutdown() throws Exception {
tckResourcesCleaner.clean();
EnvironmentContext.reset();
}
@Test
public void shouldFindDevfilesByByPermissions() throws Exception {
EnvironmentContext expected = EnvironmentContext.getCurrent();
expected.setSubject(new SubjectImpl("user", users.get(0).getId(), "token", false));
List<UserDevfile> results =
dao.getDevfiles(30, 0, Collections.emptyList(), Collections.emptyList()).getItems();
assertEquals(results.size(), 2);
assertTrue(results.contains(userDevfiles.get(0)));
assertTrue(results.contains(userDevfiles.get(1)));
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.jpa;
import com.google.inject.TypeLiteral;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.account.spi.jpa.JpaAccountDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.workspace.server.devfile.SerializableConverter;
import org.eclipse.che.api.workspace.server.model.impl.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl;
import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.commons.test.db.H2DBTestServer;
import org.eclipse.che.commons.test.db.H2JpaCleaner;
import org.eclipse.che.commons.test.db.PersistTestModuleBuilder;
import org.eclipse.che.commons.test.tck.TckModule;
import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
import org.eclipse.che.commons.test.tck.repository.JpaTckRepository;
import org.eclipse.che.commons.test.tck.repository.TckRepository;
import org.eclipse.che.core.db.DBInitializer;
import org.eclipse.che.core.db.h2.jpa.eclipselink.H2ExceptionHandler;
import org.eclipse.che.core.db.schema.SchemaInitializer;
import org.eclipse.che.core.db.schema.impl.flyway.FlywaySchemaInitializer;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.tck.UserDevfilePermissionDaoTest;
import org.h2.Driver;
public class UserDevfileTckModule extends TckModule {
@Override
protected void configure() {
H2DBTestServer server = H2DBTestServer.startDefault();
install(
new PersistTestModuleBuilder()
.setDriver(Driver.class)
.runningOn(server)
.addEntityClasses(
AccountImpl.class,
UserImpl.class,
WorkspaceImpl.class,
WorkspaceConfigImpl.class,
ProjectConfigImpl.class,
EnvironmentImpl.class,
MachineConfigImpl.class,
SourceStorageImpl.class,
ServerConfigImpl.class,
CommandImpl.class,
RecipeImpl.class,
VolumeImpl.class,
// devfile
UserDevfileImpl.class,
UserDevfilePermissionImpl.class,
ActionImpl.class,
org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl.class,
ComponentImpl.class,
DevfileImpl.class,
EndpointImpl.class,
EntrypointImpl.class,
EnvImpl.class,
ProjectImpl.class,
SourceImpl.class,
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl.class)
.addEntityClass(
"org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute")
.addClass(SerializableConverter.class)
.setExceptionHandler(H2ExceptionHandler.class)
.build());
bind(DBInitializer.class).asEagerSingleton();
bind(SchemaInitializer.class)
.toInstance(new FlywaySchemaInitializer(server.getDataSource(), "che-schema"));
bind(TckResourcesCleaner.class).toInstance(new H2JpaCleaner(server));
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
bind(new TypeLiteral<TckRepository<UserImpl>>() {})
.toInstance(new JpaTckRepository<>(UserImpl.class));
bind(new TypeLiteral<TckRepository<UserDevfilePermissionImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfilePermissionImpl.class));
bind(new TypeLiteral<AbstractPermissionsDomain<UserDevfilePermissionImpl>>() {})
.to(UserDevfilePermissionDaoTest.TestDomain.class);
bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
bind(AccountDao.class).to(JpaAccountDao.class);
bind(new TypeLiteral<TckRepository<AccountImpl>>() {})
.toInstance(new JpaTckRepository<>(AccountImpl.class));
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
import com.google.inject.Inject;
import com.google.inject.Provider;
import javax.persistence.EntityManager;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class EntityManagerExceptionInterceptor implements MethodInterceptor {
@Inject Provider<EntityManager> emf;
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
emf.get().getTransaction().setRollbackOnly();
throw new RuntimeException("Database exception");
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
import com.google.inject.TypeLiteral;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.account.spi.jpa.JpaAccountDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.workspace.server.devfile.SerializableConverter;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.commons.test.db.H2DBTestServer;
import org.eclipse.che.commons.test.db.H2JpaCleaner;
import org.eclipse.che.commons.test.db.PersistTestModuleBuilder;
import org.eclipse.che.commons.test.tck.TckModule;
import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
import org.eclipse.che.commons.test.tck.repository.JpaTckRepository;
import org.eclipse.che.commons.test.tck.repository.TckRepository;
import org.eclipse.che.core.db.DBInitializer;
import org.eclipse.che.core.db.h2.jpa.eclipselink.H2ExceptionHandler;
import org.eclipse.che.core.db.schema.SchemaInitializer;
import org.eclipse.che.core.db.schema.impl.flyway.FlywaySchemaInitializer;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.eclipse.che.multiuser.permission.devfile.server.spi.tck.UserDevfilePermissionDaoTest;
import org.h2.Driver;
public class JpaTckModule extends TckModule {
@Override
protected void configure() {
H2DBTestServer server = H2DBTestServer.startDefault();
install(
new PersistTestModuleBuilder()
.setDriver(Driver.class)
.runningOn(server)
.addEntityClasses(
AccountImpl.class,
UserImpl.class,
UserDevfilePermissionImpl.class,
// devfile
ActionImpl.class,
org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl.class,
ComponentImpl.class,
DevfileImpl.class,
EndpointImpl.class,
EntrypointImpl.class,
EnvImpl.class,
ProjectImpl.class,
SourceImpl.class,
UserDevfileImpl.class,
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl.class)
.addClass(SerializableConverter.class)
.setExceptionHandler(H2ExceptionHandler.class)
.build());
bind(new TypeLiteral<AbstractPermissionsDomain<UserDevfilePermissionImpl>>() {})
.to(UserDevfilePermissionDaoTest.TestDomain.class);
bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
bind(AccountDao.class).to(JpaAccountDao.class);
bind(new TypeLiteral<TckRepository<UserDevfilePermission>>() {})
.toInstance(new JpaTckRepository<>(UserDevfilePermission.class));
bind(new TypeLiteral<TckRepository<UserImpl>>() {})
.toInstance(new JpaTckRepository<>(UserImpl.class));
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
bind(new TypeLiteral<TckRepository<AccountImpl>>() {})
.toInstance(new JpaTckRepository<>(AccountImpl.class));
bind(SchemaInitializer.class)
.toInstance(new FlywaySchemaInitializer(server.getDataSource(), "che-schema"));
bind(DBInitializer.class).asEagerSingleton();
bind(TckResourcesCleaner.class).toInstance(new H2JpaCleaner(server));
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.eclipse.che.inject.Matchers.names;
import static org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain.SET_PERMISSIONS;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import org.aopalliance.intercept.MethodInterceptor;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.commons.test.tck.TckModule;
import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
import org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class JpaUserDevfilePermissionDaoTest {
private JpaUserDevfilePermissionDao userDevfilePermissionsDao;
private EntityManager manager;
private TckResourcesCleaner tckResourcesCleaner;
@BeforeMethod
private void setUpManager() {
final Injector injector =
Guice.createInjector(new JpaTckModule(), new ExceptionEntityManagerModule());
manager = injector.getInstance(EntityManager.class);
userDevfilePermissionsDao = injector.getInstance(JpaUserDevfilePermissionDao.class);
tckResourcesCleaner = injector.getInstance(TckResourcesCleaner.class);
}
@AfterMethod
private void cleanup() {
manager.getTransaction().begin();
final List<Object> entities = new ArrayList<>();
entities.addAll(manager.createQuery("SELECT p FROM UserDevfilePermission p").getResultList());
entities.addAll(manager.createQuery("SELECT d FROM UserDevfile d").getResultList());
entities.addAll(manager.createQuery("SELECT a FROM Account a").getResultList());
entities.addAll(manager.createQuery("SELECT u FROM Usr u").getResultList());
for (Object entity : entities) {
manager.remove(entity);
}
manager.getTransaction().commit();
tckResourcesCleaner.clean();
}
@Test(
expectedExceptions = ServerException.class,
expectedExceptionsMessageRegExp = "Database exception")
public void shouldThrowServerExceptionOnExistsWhenRuntimeExceptionOccursInDoGetMethod()
throws Exception {
// Persist the account
manager.getTransaction().begin();
manager.persist(TestObjectGenerator.TEST_ACCOUNT);
manager.getTransaction().commit();
manager.clear();
final UserDevfileImpl userDevfile = TestObjectGenerator.createUserDevfile();
// Persist the userdevfile
manager.getTransaction().begin();
manager.persist(userDevfile);
manager.getTransaction().commit();
manager.clear();
final UserImpl user = new UserImpl(generate("user", 6), "user0@com.com", "usr0");
// Persist the user
manager.getTransaction().begin();
manager.persist(user);
manager.getTransaction().commit();
manager.clear();
// Persist the worker
UserDevfilePermissionImpl worker =
new UserDevfilePermissionImpl(
userDevfile.getId(), user.getId(), Collections.singletonList(SET_PERMISSIONS));
manager.getTransaction().begin();
manager.persist(worker);
manager.getTransaction().commit();
manager.clear();
userDevfilePermissionsDao.exists(user.getId(), userDevfile.getId(), SET_PERMISSIONS);
}
public class ExceptionEntityManagerModule extends TckModule {
@Override
protected void configure() {
MethodInterceptor interceptor = new EntityManagerExceptionInterceptor();
requestInjection(interceptor);
bindInterceptor(
Matchers.subclassesOf(JpaUserDevfilePermissionDao.class), names("doGet"), interceptor);
}
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.testng.AssertJUnit.assertEquals;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.util.Arrays;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
import org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao.RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/** Tests for {@link RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber} */
public class RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriberTest {
private TckResourcesCleaner tckResourcesCleaner;
private EntityManager manager;
private JpaUserDevfilePermissionDao userDevfilePermissionsDao;
private JpaUserDevfileDao userDevfileDao;
private RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber subscriber;
private UserDevfileImpl userDevfile;
private UserDevfilePermissionImpl[] userDevfilePermissions;
private UserImpl[] users;
@BeforeClass
public void setupEntities() throws Exception {
users =
new UserImpl[] {
new UserImpl("user1", "user1@com.com", "usr1"),
new UserImpl("user2", "user2@com.com", "usr2")
};
userDevfile = TestObjectGenerator.createUserDevfile("devfile_id1", generate("name", 6));
userDevfilePermissions =
new UserDevfilePermissionImpl[] {
new UserDevfilePermissionImpl(
userDevfile.getId(), "user1", Arrays.asList("read", "use", "run")),
new UserDevfilePermissionImpl(userDevfile.getId(), "user2", Arrays.asList("read", "use"))
};
Injector injector = Guice.createInjector(new JpaTckModule());
manager = injector.getInstance(EntityManager.class);
userDevfilePermissionsDao = injector.getInstance(JpaUserDevfilePermissionDao.class);
userDevfileDao = injector.getInstance(JpaUserDevfileDao.class);
subscriber =
injector.getInstance(
RemoveUserDevfilePermissionsBeforeUserDevfileRemovedEventSubscriber.class);
subscriber.subscribe();
tckResourcesCleaner = injector.getInstance(TckResourcesCleaner.class);
}
@BeforeMethod
public void setUp() throws Exception {
manager.getTransaction().begin();
manager.persist(userDevfile);
Stream.of(users).forEach(manager::persist);
manager.persist(TestObjectGenerator.TEST_ACCOUNT);
Stream.of(userDevfilePermissions).forEach(manager::persist);
manager.getTransaction().commit();
manager.clear();
}
@AfterMethod
public void cleanup() {
manager.getTransaction().begin();
manager
.createQuery("SELECT e FROM UserDevfilePermission e", UserDevfilePermissionImpl.class)
.getResultList()
.forEach(manager::remove);
manager
.createQuery("SELECT w FROM UserDevfile w", UserDevfileImpl.class)
.getResultList()
.forEach(manager::remove);
manager
.createQuery("SELECT a FROM Account a", AccountImpl.class)
.getResultList()
.forEach(manager::remove);
manager
.createQuery("SELECT u FROM Usr u", UserImpl.class)
.getResultList()
.forEach(manager::remove);
manager.getTransaction().commit();
}
@AfterClass
public void shutdown() throws Exception {
subscriber.unsubscribe();
tckResourcesCleaner.clean();
}
@Test
public void shouldRemoveAllPermissionsWhenUserDevfileIsRemoved() throws Exception {
userDevfileDao.remove(userDevfile.getId());
assertEquals(
userDevfilePermissionsDao
.getUserDevfilePermission(userDevfile.getId(), 1, 0)
.getTotalItemsCount(),
0);
}
@Test
public void shouldRemoveAllPermissionsWhenPageSizeEqualsToOne() throws Exception {
subscriber.removeUserDevfilePermissions(userDevfile.getId(), 1);
assertEquals(
userDevfilePermissionsDao
.getUserDevfilePermission(userDevfile.getId(), 1, 0)
.getTotalItemsCount(),
0);
}
}

View File

@ -0,0 +1,251 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.spi.tck;
import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator.createUserDevfile;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DELETE;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.READ;
import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.UPDATE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.commons.test.tck.TckListener;
import org.eclipse.che.commons.test.tck.repository.TckRepository;
import org.eclipse.che.commons.test.tck.repository.TckRepositoryException;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** Compatibility test for {@link UserDevfilePermissionDao} */
@Listeners(TckListener.class)
@Test(suiteName = "UserDevfilePermissionTck")
public class UserDevfilePermissionDaoTest {
@Inject private UserDevfilePermissionDao permissionDao;
@Inject private TckRepository<UserDevfilePermission> permissionTckRepository;
@Inject private TckRepository<UserImpl> userRepository;
@Inject private TckRepository<UserDevfileImpl> devfileRepository;
@Inject private TckRepository<AccountImpl> accountRepository;
UserDevfilePermissionImpl[] permissions;
@BeforeMethod
public void setUp() throws TckRepositoryException {
accountRepository.createAll(ImmutableList.of(TestObjectGenerator.TEST_ACCOUNT));
permissions =
new UserDevfilePermissionImpl[] {
new UserDevfilePermissionImpl(
"devfile_id1", "user1", Arrays.asList(READ, DELETE, UPDATE)),
new UserDevfilePermissionImpl("devfile_id1", "user2", Arrays.asList(READ, DELETE)),
new UserDevfilePermissionImpl("devfile_id2", "user1", Arrays.asList(READ, UPDATE)),
new UserDevfilePermissionImpl(
"devfile_id2", "user2", Arrays.asList(READ, DELETE, UPDATE)),
new UserDevfilePermissionImpl("devfile_id2", "user0", Arrays.asList(READ, DELETE, UPDATE))
};
final UserImpl[] users =
new UserImpl[] {
new UserImpl("user0", "user0@com.com", "usr0"),
new UserImpl("user1", "user1@com.com", "usr1"),
new UserImpl("user2", "user2@com.com", "usr2")
};
userRepository.createAll(Arrays.asList(users));
devfileRepository.createAll(
ImmutableList.of(
createUserDevfile("devfile_id1", generate("name", 6)),
createUserDevfile("devfile_id2", generate("name", 6)),
createUserDevfile("devfile_id3", generate("name", 6)),
createUserDevfile("devfile_id4", generate("name", 6)),
createUserDevfile("devfile_id5", generate("name", 6))));
permissionTckRepository.createAll(
Stream.of(permissions).map(UserDevfilePermissionImpl::new).collect(Collectors.toList()));
}
@AfterMethod
public void cleanUp() throws TckRepositoryException {
permissionTckRepository.removeAll();
devfileRepository.removeAll();
accountRepository.removeAll();
userRepository.removeAll();
}
@Test
public void shouldStorePermissions() throws Exception {
UserDevfilePermissionImpl permission =
new UserDevfilePermissionImpl("devfile_id1", "user0", Arrays.asList(READ, DELETE, UPDATE));
permissionDao.store(permission);
Assert.assertEquals(
permissionDao.getUserDevfilePermission("devfile_id1", "user0"),
new UserDevfilePermissionImpl(permission));
}
@Test
public void shouldReplaceExistingPermissionOnStoring() throws Exception {
UserDevfilePermissionImpl replace =
new UserDevfilePermissionImpl("devfile_id1", "user1", Collections.singletonList("READ"));
permissionDao.store(replace);
Assert.assertEquals(permissionDao.getUserDevfilePermission("devfile_id1", "user1"), replace);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenStoringArgumentIsNull() throws Exception {
permissionDao.store(null);
}
@Test
public void shouldGetPermissionByWorkspaceIdAndUserId() throws Exception {
Assert.assertEquals(
permissionDao.getUserDevfilePermission("devfile_id1", "user1"), permissions[0]);
Assert.assertEquals(
permissionDao.getUserDevfilePermission("devfile_id2", "user2"), permissions[3]);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenGetPermissionDevfileIdArgumentIsNull() throws Exception {
permissionDao.getUserDevfilePermission(null, "user1");
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenGetPermissionUserIdArgumentIsNull() throws Exception {
permissionDao.getUserDevfilePermission("devfile_id1", null);
}
@Test(expectedExceptions = NotFoundException.class)
public void shouldThrowNotFoundExceptionOnGetIfPermissionWithSuchDevfileIdOrUserIdDoesNotExist()
throws Exception {
permissionDao.getUserDevfilePermission("devfile_id9", "user1");
}
@Test
public void shouldGetPermissionsByDevfileId() throws Exception {
Page<UserDevfilePermissionImpl> permissionsPage =
permissionDao.getUserDevfilePermission("devfile_id2", 1, 1);
final List<UserDevfilePermissionImpl> fetchedPermissions = permissionsPage.getItems();
assertEquals(permissionsPage.getTotalItemsCount(), 3);
assertEquals(permissionsPage.getItemsCount(), 1);
assertTrue(
fetchedPermissions.contains(permissions[2])
^ fetchedPermissions.contains(permissions[3])
^ fetchedPermissions.contains(permissions[4]));
}
@Test
public void shouldGetPermissionsByUserId() throws Exception {
List<UserDevfilePermissionImpl> actual = permissionDao.getUserDevfilePermissionByUser("user1");
List<UserDevfilePermissionImpl> expected = Arrays.asList(permissions[0], permissions[2]);
assertEquals(actual.size(), expected.size());
assertTrue(new HashSet<>(actual).equals(new HashSet<>(expected)));
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenGetPermissionsByDevfileArgumentIsNull() throws Exception {
permissionDao.getUserDevfilePermission(null, 1, 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenGetPermissionsByUserArgumentIsNull() throws Exception {
permissionDao.getUserDevfilePermissionByUser(null);
}
@Test
public void shouldReturnEmptyListIfPermissionsWithSuchDevfileIdDoesNotFound() throws Exception {
assertEquals(
0, permissionDao.getUserDevfilePermission("unexisted_devfile_id", 1, 0).getItemsCount());
}
@Test
public void shouldReturnEmptyListIfPermissionsWithSuchUserIdDoesNotFound() throws Exception {
assertEquals(0, permissionDao.getUserDevfilePermissionByUser("unexisted_user").size());
}
@Test
public void shouldRemovePermission() throws Exception {
permissionDao.removeUserDevfilePermission("devfile_id1", "user1");
assertEquals(1, permissionDao.getUserDevfilePermissionByUser("user1").size());
assertNull(
notFoundToNull(() -> permissionDao.getUserDevfilePermission("devfile_id1", "user1")));
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenRemovePermissionDevfileIdArgumentIsNull() throws Exception {
permissionDao.removeUserDevfilePermission(null, "user1");
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowExceptionWhenRemovePermissionUserIdArgumentIsNull() throws Exception {
permissionDao.removeUserDevfilePermission("devfile_id1", null);
}
@Test(expectedExceptions = ServerException.class)
public void shouldThrowNotFoundExceptionOnRemoveIfPermissionWithSuchDevfileIdDoesNotExist()
throws Exception {
permissionDao.removeUserDevfilePermission("unexisted_ws", "user1");
}
@Test(expectedExceptions = ServerException.class)
public void shouldThrowNotFoundExceptionOnRemoveIfPermissionWithSuchUserIdDoesNotExist()
throws Exception {
permissionDao.removeUserDevfilePermission("devfile_id1", "unexisted_user");
}
public static class TestDomain extends AbstractPermissionsDomain<UserDevfilePermissionImpl> {
public TestDomain() {
super(UserDevfileDomain.DOMAIN_ID, Arrays.asList(READ, DELETE, UPDATE));
}
@Override
protected UserDevfilePermissionImpl doCreateInstance(
String userId, String instanceId, List<String> allowedActions) {
return new UserDevfilePermissionImpl(userId, instanceId, allowedActions);
}
}
private static <T> T notFoundToNull(Callable<T> action) throws Exception {
try {
return action.call();
} catch (NotFoundException x) {
return null;
}
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permissions.devfile;
import static com.jayway.restassured.RestAssured.given;
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.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import com.jayway.restassured.response.Response;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.che.api.workspace.server.devfile.DevfileService;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.multiuser.permission.devfile.DevfilePermissionsFilter;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class DevfilePermissionsFilterTest {
@SuppressWarnings("unused")
private static final EnvironmentFilter FILTER = new EnvironmentFilter();
@Mock private static Subject subject;
@Mock private DevfileService service;
@SuppressWarnings("unused")
@InjectMocks
private DevfilePermissionsFilter permissionsFilter;
@Test
public void shouldTestThatAllPublicMethodsAreCoveredByPermissionsFilter() throws Exception {
// given
final List<String> collect =
Stream.of(DevfileService.class.getDeclaredMethods())
.filter(method -> Modifier.isPublic(method.getModifiers()))
.map(Method::getName)
.collect(Collectors.toList());
// then
assertEquals(collect.size(), 1);
assertTrue(collect.contains(DevfilePermissionsFilter.GET_SCHEMA_METHOD));
}
@Test
public void shouldNotCheckPermissionsOnExportingSchema() throws Exception {
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 204);
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(subject);
}
}
}

View File

@ -0,0 +1 @@
org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaTckModule

View File

@ -17,7 +17,7 @@
<parent>
<artifactId>che-multiuser-permission</artifactId>
<groupId>org.eclipse.che.multiuser</groupId>
<version>7.19.0-SNAPSHOT</version>
<version>7.20.0-SNAPSHOT</version>
</parent>
<artifactId>che-multiuser-permission-factory</artifactId>
<name>Che Multiuser :: Factory Permissions</name>

View File

@ -11,36 +11,15 @@
*/
package org.eclipse.che.multiuser.permission.factory;
import static com.jayway.restassured.RestAssured.given;
import static org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain.DOMAIN_ID;
import static org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain.READ;
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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import com.jayway.restassured.response.Response;
import java.util.Map;
import javax.ws.rs.core.UriInfo;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.model.factory.Factory;
import org.eclipse.che.api.factory.server.FactoryManager;
import org.eclipse.che.api.factory.server.FactoryService;
import org.eclipse.che.api.factory.server.model.impl.AuthorImpl;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.dto.server.DtoFactory;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
@ -85,7 +64,6 @@ public class FactoryPermissionsFilterTest {
@DataProvider(name = "publicMethods")
public Object[][] publicMethods() {
return new Object[][] {
{"resolveFactory", new Class[] {Map.class, Boolean.class}},
};
}

View File

@ -0,0 +1,40 @@
--
-- Copyright (c) 2012-2020 Red Hat, Inc.
-- This program and the accompanying materials are made
-- available under the terms of the Eclipse Public License 2.0
-- which is available at https://www.eclipse.org/legal/epl-2.0/
--
-- SPDX-License-Identifier: EPL-2.0
--
-- Contributors:
-- Red Hat, Inc. - initial API and implementation
--
-- User devfile permissions -----------------------------------------------------------
CREATE TABLE che_userdevfile_permissions (
id VARCHAR(255) NOT NULL,
user_id VARCHAR(255),
userdevfile_id VARCHAR(255),
PRIMARY KEY (id)
);
-- indexes
CREATE UNIQUE INDEX che_index_userdevfile_permissions_user_id_userdevfile_id ON che_userdevfile_permissions (user_id, userdevfile_id);
CREATE INDEX che_index_userdevfile_permissions_userdevfile_id ON che_userdevfile_permissions (userdevfile_id);
-- constraints
ALTER TABLE che_userdevfile_permissions ADD CONSTRAINT che_fk_userdevfile_permissions_user_id FOREIGN KEY (user_id) REFERENCES usr (id);
ALTER TABLE che_userdevfile_permissions ADD CONSTRAINT che_fk_userdevfile_permissions_workspace_id FOREIGN KEY (userdevfile_id) REFERENCES userdevfile (id);
--------------------------------------------------------------------------------
-- User devfile permission actions --------------------------------------------------------------
CREATE TABLE che_userdevfile_permissions_actions (
userdevfile_permissions_id VARCHAR(255),
actions VARCHAR(255)
);
-- indexes
CREATE INDEX che_index_userdevfile_permissions_actions_actions ON che_userdevfile_permissions_actions (actions);
CREATE INDEX che_index_userdevfile_permissions_actions_userdevfile_id ON che_userdevfile_permissions_actions (userdevfile_permissions_id);
-- constraints
ALTER TABLE che_userdevfile_permissions_actions ADD CONSTRAINT che_fk_userdevfile_permissions_actions_id FOREIGN KEY (userdevfile_permissions_id) REFERENCES che_userdevfile_permissions(id);
--------------------------------------------------------------------------------

22
pom.xml
View File

@ -701,6 +701,22 @@
<artifactId>che-core-api-core</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<version>${che.version}</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile-shared</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
@ -1118,6 +1134,12 @@
<artifactId>che-multiuser-permission-devfile</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
<version>${che.version}</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-logger</artifactId>

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2018 Red Hat, Inc.
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
Red Hat, Inc. - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-master-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.20.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-devfile-shared</artifactId>
<packaging>jar</packaging>
<name>Che Core :: API :: Devfile :: Shared</name>
<properties>
<dto-generator-out-directory>${project.build.directory}/generated-sources/dto/</dto-generator-out-directory>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile-shared</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<dtoPackages>
<package>org.eclipse.che.api.devfile.shared.dto</package>
</dtoPackages>
<outputDirectory>${dto-generator-out-directory}</outputDirectory>
<genClassName>org.eclipse.che.api.devfile.server.dto.DtoServerImpls</genClassName>
<impl>server</impl>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>pre-compile</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-resource</id>
<phase>process-sources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${dto-generator-out-directory}/META-INF</directory>
<targetPath>META-INF</targetPath>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>add-source</id>
<phase>process-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${dto-generator-out-directory}</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.shared;
import com.google.common.annotations.Beta;
/** Constants for Devfile API */
@Beta
public final class Constants {
public static final String LINK_REL_SELF = "self";
private Constants() {}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.shared.dto;
import com.google.common.annotations.Beta;
import java.util.List;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.rest.shared.dto.Hyperlinks;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.dto.shared.DTO;
@DTO
@Beta
public interface UserDevfileDto extends UserDevfile, Hyperlinks {
void setId(String id);
UserDevfileDto withId(String id);
String getId();
void setName(String name);
UserDevfileDto withName(String name);
String getName();
void setDescription(String name);
UserDevfileDto withDescription(String name);
String getDescription();
@Override
DevfileDto getDevfile();
void setDevfile(DevfileDto devfile);
@Override
String getNamespace();
void setNamespace(String namespace);
UserDevfileDto withNamespace(String namespace);
UserDevfileDto withDevfile(DevfileDto devfile);
UserDevfileDto withLinks(List<Link> links);
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.shared.event;
import com.google.common.annotations.Beta;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventOrigin;
/** Informs about persisted devfile creation. */
@EventOrigin("devfile")
@Beta
public class DevfileCreatedEvent {
private final UserDevfile userDevfile;
public DevfileCreatedEvent(UserDevfile userDevfile) {
this.userDevfile = userDevfile;
}
public UserDevfile getUserDevfile() {
return userDevfile;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.shared.event;
import com.google.common.annotations.Beta;
import org.eclipse.che.api.core.notification.EventOrigin;
/** Informs about persisted devfile removal. */
@EventOrigin("devfile")
@Beta
public class DevfileDeletedEvent {
private final String id;
public DevfileDeletedEvent(String id) {
this.id = id;
}
public String getId() {
return id;
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.shared.event;
import com.google.common.annotations.Beta;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventOrigin;
/** Informs about persisted devfile update. */
@EventOrigin("devfile")
@Beta
public class DevfileUpdatedEvent {
private final UserDevfile userDevfile;
public DevfileUpdatedEvent(UserDevfile userDevfile) {
this.userDevfile = userDevfile;
}
public UserDevfile getUserDevfile() {
return userDevfile;
}
}

View File

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2018 Red Hat, Inc.
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
Red Hat, Inc. - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-master-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.20.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-devfile</artifactId>
<packaging>jar</packaging>
<name>Che Core :: API :: Devfile</name>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-persist</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-account</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-user</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-db</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-json</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-db-vendor-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-sql-schema</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Create the test jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
<configuration>
<includes>
<include>**/spi/tck/*.*</include>
<include>**/devfile/server/TestObjectGenerator.*</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,304 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static java.util.stream.Collectors.toList;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.api.devfile.server.DtoConverter.asDto;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
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 java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
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.Response;
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.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.lang.URLEncodedUtils;
import org.eclipse.che.dto.server.DtoFactory;
/** Defines Devfile REST API. */
@Api(value = "/devfile", description = "Devfile REST API")
@Path("/devfile")
public class DevfileService extends Service {
private final DevfileSchemaProvider schemaCachedProvider;
private final UserDevfileManager userDevfileManager;
private final DevfileServiceLinksInjector linksInjector;
@Inject
public DevfileService(
DevfileSchemaProvider schemaCachedProvider,
UserDevfileManager userDevfileManager,
DevfileServiceLinksInjector linksInjector) {
this.userDevfileManager = userDevfileManager;
this.linksInjector = linksInjector;
this.schemaCachedProvider = schemaCachedProvider;
}
/**
* Retrieves the json schema.
*
* @return json schema
*/
@GET
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Retrieves current version of devfile JSON schema")
@ApiResponses({
@ApiResponse(code = 200, message = "The schema successfully retrieved"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public Response getSchema() throws ServerException {
try {
return Response.ok(schemaCachedProvider.getSchemaContent(CURRENT_API_VERSION)).build();
} catch (IOException e) {
throw new ServerException(e);
}
}
@Path("/devfile")
@POST
@Consumes({APPLICATION_JSON, "text/yaml", "text/x-yaml"})
@Produces(APPLICATION_JSON)
@ApiOperation(
value = "Creates a new persistent Devfile from yaml representation",
consumes = "application/json, text/yaml, text/x-yaml",
produces = APPLICATION_JSON,
nickname = "createFromDevfileYaml",
response = UserDevfileDto.class)
@ApiResponses({
@ApiResponse(code = 201, message = "The devfile successfully created"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 403, message = "The user does not have access to create a new devfile"),
@ApiResponse(code = 409, message = "Conflict error occurred during the devfile creation"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public Response createFromDevfileYaml(
@ApiParam(value = "The devfile to create", required = true) DevfileDto devfile)
throws ConflictException, BadRequestException, ForbiddenException, NotFoundException,
ServerException {
requiredNotNull(devfile, "Devfile");
return Response.status(201)
.entity(
linksInjector.injectLinks(
asDto(
userDevfileManager.createDevfile(
DtoFactory.newDto(UserDevfileDto.class)
.withDevfile(devfile)
.withName(NameGenerator.generate("devfile-", 16)))),
getServiceContext()))
.build();
}
@POST
@Consumes({APPLICATION_JSON})
@Produces(APPLICATION_JSON)
@ApiOperation(
value = "Creates a new persistent Devfile",
consumes = "application/json",
produces = APPLICATION_JSON,
nickname = "create",
response = UserDevfileDto.class)
@ApiResponses({
@ApiResponse(code = 201, message = "The devfile successfully created"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 403, message = "The user does not have access to create a new devfile"),
@ApiResponse(
code = 409,
message =
"Conflict error occurred during the devfile creation"
+ "(e.g. The devfile with such name already exists)"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public Response createFromUserDevfile(
@ApiParam(value = "The devfile to create", required = true) UserDevfileDto userDevfileDto)
throws ConflictException, BadRequestException, ForbiddenException, NotFoundException,
ServerException {
requiredNotNull(userDevfileDto, "Devfile");
return Response.status(201)
.entity(
linksInjector.injectLinks(
asDto(userDevfileManager.createDevfile(userDevfileDto)), getServiceContext()))
.build();
}
@GET
@Path("/{id}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Get devfile by its identifier")
@ApiResponses({
@ApiResponse(code = 200, message = "The response contains requested workspace entity"),
@ApiResponse(code = 404, message = "The devfile with specified id does not exist"),
@ApiResponse(code = 403, message = "The user is not allowed to read devfile"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public UserDevfileDto getById(
@ApiParam(value = "UserDevfile identifier") @PathParam("id") String id)
throws NotFoundException, ServerException, ForbiddenException, BadRequestException {
requiredNotNull(id, "id");
return linksInjector.injectLinks(asDto(userDevfileManager.getById(id)), getServiceContext());
}
@GET
@Path("search")
@Produces(APPLICATION_JSON)
@ApiOperation(
value = "Get devfiles which user can read",
notes =
"This operation can be performed only by authorized user. "
+ "It is possible to add additional constraints for the desired devfiles by specifying\n"
+ "multiple query parameters that is representing fields of the devfile. All constrains\n"
+ "would be combined with \"And\" condition. Also, it is possible to specify 'like:' prefix\n"
+ "for the query parameters. In this case instead of an exact match would be used SQL pattern like search.\n"
+ "Examples id=sdfsdf5&devfile.meta.name=like:%dfdf&",
response = UserDevfileDto.class,
responseContainer = "List")
@ApiResponses({
@ApiResponse(code = 200, message = "The devfiles successfully fetched"),
@ApiResponse(code = 500, message = "Internal server error occurred during devfiles fetching")
})
public Response getUserDevfiles(
@ApiParam("The number of the items to skip") @DefaultValue("0") @QueryParam("skipCount")
Integer skipCount,
@ApiParam("The limit of the items in the response, default is 30, maximum 60")
@DefaultValue("30")
@QueryParam("maxItems")
Integer maxItems,
@ApiParam(
"A list of fields and directions of sort. By default items would be sorted by id. Example id:asc,name:desc.")
@QueryParam("order")
String order)
throws ServerException, BadRequestException {
final Set<String> skip = ImmutableSet.of("token", "skipCount", "maxItems", "order");
Map<String, Set<String>> queryParams = URLEncodedUtils.parse(uriInfo.getRequestUri());
final List<Pair<String, String>> query =
queryParams
.entrySet()
.stream()
.filter(param -> !param.getValue().isEmpty())
.filter(param -> !skip.contains(param.getKey()))
.map(entry -> Pair.of(entry.getKey(), entry.getValue().iterator().next()))
.collect(toList());
List<Pair<String, String>> searchOrder = Collections.emptyList();
if (order != null && !order.isEmpty()) {
try {
searchOrder =
Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.withKeyValueSeparator(":")
.split(order)
.entrySet()
.stream()
.map(e -> Pair.of(e.getKey(), e.getValue()))
.collect(toList());
} catch (IllegalArgumentException e) {
throw new BadRequestException("Invalid `order` query parameter format." + e.getMessage());
}
}
Page<? extends UserDevfile> userDevfilesPage =
userDevfileManager.getUserDevfiles(maxItems, skipCount, query, searchOrder);
List<UserDevfileDto> list =
userDevfilesPage
.getItems()
.stream()
.map(DtoConverter::asDto)
.map(dto -> linksInjector.injectLinks(asDto(dto), getServiceContext()))
.collect(toList());
return Response.ok().entity(list).header("Link", createLinkHeader(userDevfilesPage)).build();
}
@PUT
@Path("/{id}")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Update the devfile by replacing all the existing data with update")
@ApiResponses({
@ApiResponse(code = 200, message = "The devfile successfully updated"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 403, message = "The user does not have access to update the devfile"),
@ApiResponse(
code = 409,
message =
"Conflict error occurred during devfile update"
+ "(e.g. Workspace with such name already exists)"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public UserDevfileDto update(
@ApiParam("The devfile id") @PathParam("id") String id,
@ApiParam(value = "The devfile update", required = true) UserDevfileDto update)
throws BadRequestException, ServerException, ForbiddenException, NotFoundException,
ConflictException {
requiredNotNull(update, "User Devfile configuration");
update.setId(id);
return linksInjector.injectLinks(
asDto(userDevfileManager.updateUserDevfile(update)), getServiceContext());
}
@DELETE
@Path("/{id}")
@ApiOperation(value = "Removes the devfile")
@ApiResponses({
@ApiResponse(code = 204, message = "The devfile successfully removed"),
@ApiResponse(code = 403, message = "The user does not have access to remove the devfile"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public void delete(@ApiParam("The devfile id") @PathParam("id") String id)
throws BadRequestException, ServerException, ForbiddenException {
userDevfileManager.removeUserDevfile(id);
}
/**
* Checks object reference is not {@code null}
*
* @param object object reference to check
* @param subject used as subject of exception message "{subject} required"
* @throws BadRequestException when object reference is {@code null}
*/
private void requiredNotNull(Object object, String subject) throws BadRequestException {
if (object == null) {
throw new BadRequestException(subject + " required");
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableList;
import javax.inject.Singleton;
import javax.ws.rs.HttpMethod;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.api.core.util.LinksHelper;
import org.eclipse.che.api.devfile.shared.Constants;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
/** Helps to inject {@link DevfileService} related links. */
@Beta
@Singleton
public class DevfileServiceLinksInjector {
public UserDevfileDto injectLinks(UserDevfileDto userDevfileDto, ServiceContext serviceContext) {
return userDevfileDto.withLinks(
ImmutableList.of(
LinksHelper.createLink(
HttpMethod.GET,
serviceContext
.getBaseUriBuilder()
.clone()
.path(DevfileService.class)
.path(DevfileService.class, "getById")
.build(userDevfileDto.getId())
.toString(),
null,
APPLICATION_JSON,
Constants.LINK_REL_SELF)));
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
/** Helps to convert to/from DTOs related to user devfile. */
public class DtoConverter {
public static UserDevfileDto asDto(UserDevfile userDevfile) {
DevfileDto devfileDto =
org.eclipse.che.api.workspace.server.DtoConverter.asDto(userDevfile.getDevfile());
return newDto(UserDevfileDto.class)
.withId(userDevfile.getId())
.withDevfile(devfileDto)
.withNamespace(userDevfile.getNamespace())
.withName(userDevfile.getName())
.withDescription(userDevfile.getDescription());
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static org.eclipse.che.api.core.Pages.iterate;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.account.event.BeforeAccountRemovedEvent;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.core.db.cascade.CascadeEventSubscriber;
/**
* An event listener that is removing all {@link UserDevfile}s that belong to the account that is
* going to be removed.
*/
@Singleton
public class RemoveUserDevfileBeforeAccountRemovedEventSubscriber
extends CascadeEventSubscriber<BeforeAccountRemovedEvent> {
private final EventService eventService;
private final UserDevfileManager userDevfileManager;
@Inject
public RemoveUserDevfileBeforeAccountRemovedEventSubscriber(
EventService eventService, UserDevfileManager userDevfileManager) {
this.eventService = eventService;
this.userDevfileManager = userDevfileManager;
}
@PostConstruct
public void subscribe() {
eventService.subscribe(this, BeforeAccountRemovedEvent.class);
}
@PreDestroy
public void unsubscribe() {
eventService.unsubscribe(this, BeforeAccountRemovedEvent.class);
}
@Override
public void onCascadeEvent(BeforeAccountRemovedEvent event) throws Exception {
for (UserDevfile userDevfile :
iterate(
(maxItems, skipCount) ->
userDevfileManager.getByNamespace(
event.getAccount().getName(), maxItems, skipCount))) {
userDevfileManager.removeUserDevfile(userDevfile.getId());
}
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileFormatException;
import org.eclipse.che.dto.server.DtoFactory;
/**
* Entity provider for {@link UserDevfileDto}. Performs schema validation of devfile part of the
* user devfile before actual {@link UserDevfileDto} creation.
*/
@Singleton
@Provider
@Produces({APPLICATION_JSON})
@Consumes({APPLICATION_JSON})
public class UserDevfileEntityProvider
implements MessageBodyReader<UserDevfileDto>, MessageBodyWriter<UserDevfileDto> {
private final DevfileParser devfileParser;
private final ObjectMapper mapper = new ObjectMapper();
@Inject
public UserDevfileEntityProvider(DevfileParser devfileParser) {
this.devfileParser = devfileParser;
}
@Override
public boolean isReadable(
Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == UserDevfileDto.class;
}
@Override
public UserDevfileDto readFrom(
Class<UserDevfileDto> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream)
throws IOException, WebApplicationException {
try {
JsonNode wsNode = mapper.readTree(entityStream);
JsonNode devfileNode = wsNode.path("devfile");
if (!devfileNode.isNull() && !devfileNode.isMissingNode()) {
devfileParser.parseJson(devfileNode.toString());
} else {
throw new BadRequestException("Mandatory field `devfile` is not defined.");
}
return DtoFactory.getInstance().createDtoFromJson(wsNode.toString(), UserDevfileDto.class);
} catch (DevfileFormatException e) {
throw new BadRequestException(e.getMessage());
} catch (IOException e) {
throw new WebApplicationException(e.getMessage(), e);
}
}
@Override
public boolean isWriteable(
Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return UserDevfileDto.class.isAssignableFrom(type);
}
@Override
public long getSize(
UserDevfileDto userDevfileDto,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return -1;
}
@Override
public void writeTo(
UserDevfileDto userDevfileDto,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)
throws IOException, WebApplicationException {
httpHeaders.putSingle(HttpHeaders.CACHE_CONTROL, "public, no-cache, no-store, no-transform");
try (Writer w = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)) {
w.write(DtoFactory.getInstance().toJson(userDevfileDto));
w.flush();
}
}
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.account.api.AccountManager;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.Devfile;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.devfile.shared.event.DevfileCreatedEvent;
import org.eclipse.che.api.devfile.shared.event.DevfileUpdatedEvent;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Facade for {@link UserDevfile} related operations. */
@Beta
@Singleton
public class UserDevfileManager {
private static final Logger LOG = LoggerFactory.getLogger(UserDevfileManager.class);
private final UserDevfileDao userDevfileDao;
private final EventService eventService;
private final AccountManager accountManager;
@Inject
public UserDevfileManager(
AccountManager accountManager, UserDevfileDao userDevfileDao, EventService eventService) {
this.accountManager = accountManager;
this.userDevfileDao = userDevfileDao;
this.eventService = eventService;
}
/**
* Stores {@link Devfile} instance
*
* @param userDevfile instance of user devfile which would be stored
* @return new persisted devfile instance
* @throws ConflictException when any conflict occurs (e.g Devfile with such name already exists
* for {@code owner})
* @throws NullPointerException when {@code devfile} is null
* @throws ServerException when any other error occurs
*/
public UserDevfile createDevfile(UserDevfile userDevfile)
throws ServerException, NotFoundException, ConflictException {
requireNonNull(userDevfile, "Required non-null userdevfile");
requireNonNull(userDevfile.getDevfile(), "Required non-null devfile");
String name =
userDevfile.getName() != null
? userDevfile.getName()
: NameGenerator.generate("devfile-", 5);
UserDevfile result =
userDevfileDao.create(
new UserDevfileImpl(
NameGenerator.generate("id-", 16),
accountManager.getByName(
EnvironmentContext.getCurrent().getSubject().getUserName()),
name,
userDevfile.getDescription(),
userDevfile.getDevfile()));
LOG.debug(
"UserDevfile '{}' with id '{}' created by user '{}'",
result.getName(),
result.getId(),
EnvironmentContext.getCurrent().getSubject().getUserName());
eventService.publish(new DevfileCreatedEvent(result));
return result;
}
/**
* Gets UserDevfile by given id.
*
* @param id userdevfile identifier
* @return userdevfile instance
* @throws NullPointerException when {@code id} is null
* @throws NotFoundException when userdevfile with given id not found
* @throws ServerException when any server errors occurs
*/
public UserDevfile getById(String id) throws NotFoundException, ServerException {
requireNonNull(id);
Optional<UserDevfile> result = userDevfileDao.getById(id);
return result.orElseThrow(
() -> new NotFoundException(format("Devfile with id '%s' doesn't exist", id)));
}
/**
* Gets list of UserDevfiles in given namespace.
*
* @param namespace devfiles namespace
* @return list of devfiles in given namespace. Always returns list(even when there are no devfile
* in given namespace), never null
* @throws NullPointerException when {@code namespace} is null
* @throws ServerException when any other error occurs during workspaces fetching
*/
public Page<UserDevfile> getByNamespace(String namespace, int maxItems, long skipCount)
throws ServerException {
requireNonNull(namespace, "Required non-null namespace");
final Page<UserDevfile> devfilesPage =
userDevfileDao.getByNamespace(namespace, maxItems, skipCount);
return devfilesPage;
}
/**
* Updates an existing user devfile in accordance to the new configuration.
*
* <p>Note: Replace strategy is used for user devfile update, it means that existing devfile data
* will be replaced with given {@code update}.
*
* @param update user devfile update
* @return updated user devfile
* @throws NullPointerException when {@code update} is null
* @throws ConflictException when any conflict occurs.
* @throws NotFoundException when user devfile with given id not found
* @throws ServerException when any server error occurs
*/
public UserDevfile updateUserDevfile(UserDevfile update)
throws ConflictException, NotFoundException, ServerException {
requireNonNull(update);
Optional<UserDevfile> result = userDevfileDao.update(update);
UserDevfile devfile =
result.orElseThrow(
() ->
new NotFoundException(
format("Devfile with id '%s' doesn't exist", update.getId())));
LOG.debug(
"UserDevfile '{}' with id '{}' update by user '{}'",
devfile.getName(),
devfile.getId(),
EnvironmentContext.getCurrent().getSubject().getUserName());
eventService.publish(new DevfileUpdatedEvent(devfile));
return devfile;
}
/**
* Removes stored {@link UserDevfile} by given id.
*
* @param id user devfile identifier
* @throws NullPointerException when {@code id} is null
* @throws ServerException when any server errors occurs
*/
public void removeUserDevfile(String id) throws ServerException {
requireNonNull(id);
userDevfileDao.remove(id);
LOG.debug(
"UserDevfile with id '{}' removed by user '{}'",
id,
EnvironmentContext.getCurrent().getSubject().getUserName());
}
/**
* Gets list of devfiles. Parameters, returned values and possible exceptions are the same as in
* {@link UserDevfileDao#getDevfiles(int, int, List, List)}
*/
public Page<UserDevfile> getUserDevfiles(
int maxItems,
int skipCount,
List<Pair<String, String>> filter,
List<Pair<String, String>> order)
throws ServerException {
return userDevfileDao.getDevfiles(maxItems, skipCount, filter, order);
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.event;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventOrigin;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.core.db.cascade.event.RemoveEvent;
/** Published before {@link UserDevfile user devfile} removed. */
@EventOrigin("user")
public class BeforeDevfileRemovedEvent extends RemoveEvent {
private final UserDevfileImpl userDevfile;
public BeforeDevfileRemovedEvent(UserDevfileImpl userDevfile) {
this.userDevfile = userDevfile;
}
/** Returns user which is going to be removed. */
public UserDevfileImpl getUserDevfile() {
return userDevfile;
}
}

View File

@ -0,0 +1,404 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.jpa;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao.UserDevfileSearchQueryBuilder.newBuilder;
import com.google.common.annotations.Beta;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.inject.persist.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import org.eclipse.che.account.shared.model.Account;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.devfile.server.event.BeforeDevfileRemovedEvent;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.core.db.jpa.DuplicateKeyException;
import org.eclipse.che.core.db.jpa.IntegrityConstraintViolationException;
/** JPA based implementation of {@link UserDevfileDao}. */
@Singleton
@Beta
public class JpaUserDevfileDao implements UserDevfileDao {
protected final Provider<EntityManager> managerProvider;
protected final AccountDao accountDao;
protected final EventService eventService;
/** sorting order that would be used by default during search. */
public static final List<Pair<String, String>> DEFAULT_ORDER =
ImmutableList.of(new Pair<>("id", "ASC"));
/** Set of field that is eligible to use for search. */
public static final Set<String> VALID_SEARCH_FIELDS = ImmutableSet.of("name");
/** Set of field that is eligible to use for sorting during search. */
public static final Set<String> VALID_ORDER_FIELDS = ImmutableSet.of("id", "name");
@Inject
public JpaUserDevfileDao(
Provider<EntityManager> managerProvider, AccountDao accountDao, EventService eventService) {
this.managerProvider = managerProvider;
this.accountDao = accountDao;
this.eventService = eventService;
}
@Override
public UserDevfile create(UserDevfile userDevfile) throws ConflictException, ServerException {
requireNonNull(userDevfile);
try {
Account account = accountDao.getByName(userDevfile.getNamespace());
UserDevfileImpl userDevfileImpl = new UserDevfileImpl(userDevfile, account);
doCreate(userDevfileImpl);
return userDevfileImpl;
} catch (DuplicateKeyException ex) {
throw new ConflictException(
format(
"Devfile with name '%s' already exists in the specified account '%s'",
userDevfile.getName(), userDevfile.getNamespace()));
} catch (IntegrityConstraintViolationException ex) {
throw new ConflictException(
"Could not create devfile with creator that refers to a non-existent user");
} catch (RuntimeException ex) {
throw new ServerException(ex.getMessage(), ex);
} catch (NotFoundException e) {
throw new ConflictException(
format(
"Not able to create devfile in requested namespace %s bacause it is not found",
userDevfile.getNamespace()));
}
}
@Override
public Optional<UserDevfile> update(UserDevfile userDevfile)
throws ConflictException, ServerException, NotFoundException {
requireNonNull(userDevfile);
try {
Account account = accountDao.getByName(userDevfile.getNamespace());
return doUpdate(new UserDevfileImpl(userDevfile, account)).map(UserDevfileImpl::new);
} catch (DuplicateKeyException ex) {
throw new ConflictException(
format(
"Devfile with name '%s' already exists in current account '%s'",
userDevfile.getName(), userDevfile.getNamespace()));
} catch (RuntimeException ex) {
throw new ServerException(ex.getLocalizedMessage(), ex);
}
}
@Override
public void remove(String id) throws ServerException {
requireNonNull(id);
try {
doRemove(id);
} catch (RuntimeException ex) {
throw new ServerException(ex.getLocalizedMessage(), ex);
}
}
@Override
@Transactional(rollbackOn = {ServerException.class, RuntimeException.class})
public Optional<UserDevfile> getById(String id) throws ServerException {
requireNonNull(id);
try {
final UserDevfileImpl devfile = managerProvider.get().find(UserDevfileImpl.class, id);
if (devfile == null) {
return Optional.empty();
}
return Optional.of(new UserDevfileImpl(devfile));
} catch (RuntimeException ex) {
throw new ServerException(ex.getLocalizedMessage(), ex);
}
}
@Transactional(rollbackOn = {ServerException.class, RuntimeException.class})
@Override
public Page<UserDevfile> getByNamespace(String namespace, int maxItems, long skipCount)
throws ServerException {
requireNonNull(namespace, "Required non-null namespace");
try {
final EntityManager manager = managerProvider.get();
final List<UserDevfileImpl> list =
manager
.createNamedQuery("UserDevfile.getByNamespace", UserDevfileImpl.class)
.setParameter("namespace", namespace)
.setMaxResults(maxItems)
.setFirstResult((int) skipCount)
.getResultList()
.stream()
.map(UserDevfileImpl::new)
.collect(Collectors.toList());
final long count =
manager
.createNamedQuery("UserDevfile.getByNamespaceCount", Long.class)
.setParameter("namespace", namespace)
.getSingleResult();
return new Page<>(list, skipCount, maxItems, count);
} catch (RuntimeException x) {
throw new ServerException(x.getLocalizedMessage(), x);
}
}
@Override
@Transactional(rollbackOn = {ServerException.class})
public Page<UserDevfile> getDevfiles(
int maxItems,
int skipCount,
List<Pair<String, String>> filter,
List<Pair<String, String>> order)
throws ServerException {
checkArgument(maxItems > 0, "The number of items has to be positive.");
checkArgument(
skipCount >= 0,
"The number of items to skip can't be negative or greater than " + Integer.MAX_VALUE);
return doGetDevfiles(
maxItems, skipCount, filter, order, () -> newBuilder(managerProvider.get()));
}
@Transactional(rollbackOn = {ServerException.class})
protected Page<UserDevfile> doGetDevfiles(
int maxItems,
int skipCount,
List<Pair<String, String>> filter,
List<Pair<String, String>> order,
Supplier<UserDevfileSearchQueryBuilder> queryBuilderSupplier)
throws ServerException {
if (filter != null && !filter.isEmpty()) {
List<Pair<String, String>> invalidFilter =
filter
.stream()
.filter(p -> !VALID_SEARCH_FIELDS.contains(p.first.toLowerCase()))
.collect(toList());
if (!invalidFilter.isEmpty()) {
throw new IllegalArgumentException(
"Filtering allowed only by " + VALID_SEARCH_FIELDS + " but got: " + invalidFilter);
}
}
List<Pair<String, String>> effectiveOrder = DEFAULT_ORDER;
if (order != null && !order.isEmpty()) {
List<Pair<String, String>> invalidOrder =
order
.stream()
.filter(p -> !VALID_ORDER_FIELDS.contains(p.first.toLowerCase()))
.collect(toList());
if (!invalidOrder.isEmpty()) {
throw new IllegalArgumentException(
"Order allowed only by " + VALID_ORDER_FIELDS + "¬ but got: " + invalidOrder);
}
List<Pair<String, String>> invalidSortOrder =
order
.stream()
.filter(p -> !p.second.equalsIgnoreCase("asc") && !p.second.equalsIgnoreCase("desc"))
.collect(Collectors.toList());
if (!invalidSortOrder.isEmpty()) {
throw new IllegalArgumentException(
"Invalid sort order direction. Possible values are 'asc' or 'desc' but got: "
+ invalidSortOrder);
}
effectiveOrder = order;
}
try {
final long count =
queryBuilderSupplier.get().withFilter(filter).buildCountQuery().getSingleResult();
if (count == 0) {
return new Page<>(emptyList(), skipCount, maxItems, count);
}
List<UserDevfileImpl> result =
queryBuilderSupplier
.get()
.withFilter(filter)
.withOrder(effectiveOrder)
.withMaxItems(maxItems)
.withSkipCount(skipCount)
.buildSelectItemsQuery()
.getResultList()
.stream()
.map(UserDevfileImpl::new)
.collect(toList());
return new Page<>(result, skipCount, maxItems, count);
} catch (RuntimeException x) {
throw new ServerException(x.getLocalizedMessage(), x);
}
}
@Override
@Transactional
public long getTotalCount() throws ServerException {
try {
return managerProvider
.get()
.createNamedQuery("UserDevfile.getTotalCount", Long.class)
.getSingleResult();
} catch (RuntimeException x) {
throw new ServerException(x.getLocalizedMessage(), x);
}
}
@Transactional
protected void doCreate(UserDevfileImpl devfile) {
final EntityManager manager = managerProvider.get();
manager.persist(devfile);
manager.flush();
}
@Transactional
protected Optional<UserDevfileImpl> doUpdate(UserDevfileImpl update) {
final EntityManager manager = managerProvider.get();
if (manager.find(UserDevfileImpl.class, update.getId()) == null) {
return Optional.empty();
}
UserDevfileImpl merged = manager.merge(update);
manager.flush();
return Optional.of(merged);
}
@Transactional(rollbackOn = {RuntimeException.class, ServerException.class})
protected void doRemove(String id) throws ServerException {
final EntityManager manager = managerProvider.get();
final UserDevfileImpl devfile = manager.find(UserDevfileImpl.class, id);
if (devfile != null) {
eventService
.publish(new BeforeDevfileRemovedEvent(new UserDevfileImpl(devfile)))
.propagateException();
manager.remove(devfile);
manager.flush();
}
}
public static class UserDevfileSearchQueryBuilder {
protected EntityManager entityManager;
protected int maxItems;
protected int skipCount;
protected String filter;
protected Map<String, String> params;
protected String order;
public UserDevfileSearchQueryBuilder(EntityManager entityManager) {
this.entityManager = entityManager;
this.params = new HashMap<>();
this.filter = "";
this.order = "";
}
public static UserDevfileSearchQueryBuilder newBuilder(EntityManager entityManager) {
return new JpaUserDevfileDao.UserDevfileSearchQueryBuilder(entityManager);
}
public UserDevfileSearchQueryBuilder withMaxItems(int maxItems) {
this.maxItems = maxItems;
return this;
}
public UserDevfileSearchQueryBuilder withSkipCount(int skipCount) {
this.skipCount = skipCount;
return this;
}
public UserDevfileSearchQueryBuilder withFilter(List<Pair<String, String>> filter) {
if (filter == null || filter.isEmpty()) {
return this;
}
final StringJoiner matcher = new StringJoiner(" AND ", " WHERE ", " ");
int i = 0;
for (Pair<String, String> attribute : filter) {
if (!VALID_SEARCH_FIELDS.contains(attribute.first.toLowerCase())) {
throw new IllegalArgumentException(
"Filtering allowed only by " + VALID_SEARCH_FIELDS + " but got: " + attribute.first);
}
final String parameterName = "parameterName" + i++;
if (attribute.second.startsWith("like:")) {
params.put(parameterName, attribute.second.substring(5));
matcher.add("userdevfile." + attribute.first + " LIKE :" + parameterName);
} else {
params.put(parameterName, attribute.second);
matcher.add("userdevfile." + attribute.first + " = :" + parameterName);
}
}
this.filter = matcher.toString();
return this;
}
public UserDevfileSearchQueryBuilder withOrder(List<Pair<String, String>> order) {
if (order == null || order.isEmpty()) {
return this;
}
final StringJoiner matcher = new StringJoiner(", ", " ORDER BY ", " ");
for (Pair<String, String> pair : order) {
if (!VALID_ORDER_FIELDS.contains(pair.first.toLowerCase())) {
throw new IllegalArgumentException(
"Order allowed only by " + VALID_ORDER_FIELDS + " but got: " + pair.first);
}
matcher.add("userdevfile." + pair.first + " " + pair.second);
}
this.order = matcher.toString();
return this;
}
public TypedQuery<Long> buildCountQuery() {
StringBuilder query =
new StringBuilder()
.append("SELECT ")
.append(" COUNT(userdevfile) ")
.append("FROM UserDevfile userdevfile")
.append(filter);
TypedQuery<Long> typedQuery = entityManager.createQuery(query.toString(), Long.class);
params.forEach((k, v) -> typedQuery.setParameter(k, v));
return typedQuery;
}
public TypedQuery<UserDevfileImpl> buildSelectItemsQuery() {
StringBuilder query =
new StringBuilder()
.append("SELECT ")
.append(" userdevfile ")
.append("FROM UserDevfile userdevfile")
.append(filter)
.append(order);
TypedQuery<UserDevfileImpl> typedQuery =
entityManager
.createQuery(query.toString(), UserDevfileImpl.class)
.setFirstResult(skipCount)
.setMaxResults(maxItems);
params.forEach((k, v) -> typedQuery.setParameter(k, v));
return typedQuery;
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.jpa;
import com.google.common.annotations.Beta;
import com.google.inject.AbstractModule;
import org.eclipse.che.api.devfile.server.RemoveUserDevfileBeforeAccountRemovedEventSubscriber;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
@Beta
public class UserDevfileJpaModule extends AbstractModule {
@Override
protected void configure() {
bind(UserDevfileDao.class).to(JpaUserDevfileDao.class);
bind(RemoveUserDevfileBeforeAccountRemovedEventSubscriber.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,217 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.model.impl;
import com.google.common.annotations.Beta;
import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.eclipse.che.account.shared.model.Account;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.model.workspace.devfile.Devfile;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl;
@Entity(name = "UserDevfile")
@Table(name = "userdevfile")
@NamedQueries({
@NamedQuery(
name = "UserDevfile.getByNamespace",
query = "SELECT d FROM UserDevfile d WHERE d.account.name = :namespace"),
@NamedQuery(
name = "UserDevfile.getByNamespaceCount",
query = "SELECT COUNT(d) FROM UserDevfile d WHERE d.account.name = :namespace "),
@NamedQuery(name = "UserDevfile.getAll", query = "SELECT d FROM UserDevfile d ORDER BY d.id"),
@NamedQuery(name = "UserDevfile.getTotalCount", query = "SELECT COUNT(d) FROM UserDevfile d"),
})
@Beta
public class UserDevfileImpl implements UserDevfile {
/**
* In {@MetadataImpl} name is mandatory and generateName is transient. That is not suitable for
* UserDevfile because we need to handle situations when the name is not defined and generateName
* is defined. To workaround that original name and generateName stored in individual fields
* meta_name and meta_generated_name. But at the same time, we can't leave metadata filed null in
* devfile because of database hard constrain. To replace that FAKE_META is used.
*/
private static final MetadataImpl FAKE_META = new MetadataImpl("name");
@Id
@Column(name = "id", nullable = false)
private String id;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "devfile_id")
private DevfileImpl devfile;
@Column(name = "meta_generated_name")
private String metaGeneratedName;
@Column(name = "meta_name")
private String metaName;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "description")
private String description;
@ManyToOne
@JoinColumn(name = "accountid", nullable = false)
private AccountImpl account;
public UserDevfileImpl() {}
public UserDevfileImpl(String id, Account account, UserDevfile userDevfile) {
this(
id, account, userDevfile.getName(), userDevfile.getDescription(), userDevfile.getDevfile());
}
public UserDevfileImpl(UserDevfile userDevfile, Account account) {
this(userDevfile.getId(), account, userDevfile);
}
public UserDevfileImpl(UserDevfileImpl userDevfile) {
this(
userDevfile.id,
userDevfile.account,
userDevfile.getName(),
userDevfile.getDescription(),
userDevfile.getDevfile());
}
public UserDevfileImpl(
String id, Account account, String name, String description, Devfile devfile) {
this.id = id;
this.account = new AccountImpl(account);
this.name = name;
this.description = description;
this.devfile = new DevfileImpl(devfile);
syncMeta();
}
@Override
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
@Override
public String getNamespace() {
return account.getName();
}
public void setName(String name) {
this.name = name;
}
@Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public Devfile getDevfile() {
return new DevfileImpl(
devfile.getApiVersion(),
devfile.getProjects(),
devfile.getComponents(),
devfile.getCommands(),
devfile.getAttributes(),
new MetadataImpl(metaName, metaGeneratedName));
}
public void setDevfile(DevfileImpl devfile) {
this.devfile = devfile;
syncMeta();
}
public AccountImpl getAccount() {
return account;
}
public void setAccount(AccountImpl account) {
this.account = account;
}
private void syncMeta() {
MetadataImpl metadata = devfile.getMetadata();
metaGeneratedName = metadata.getGenerateName();
metaName = metadata.getName();
devfile.setMetadata(FAKE_META);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserDevfileImpl that = (UserDevfileImpl) o;
return Objects.equals(id, that.id)
&& Objects.equals(devfile, that.devfile)
&& Objects.equals(metaGeneratedName, that.metaGeneratedName)
&& Objects.equals(metaName, that.metaName)
&& Objects.equals(name, that.name)
&& Objects.equals(description, that.description)
&& Objects.equals(account, that.account);
}
@Override
public int hashCode() {
return Objects.hash(id, devfile, metaGeneratedName, metaName, name, description, account);
}
@Override
public String toString() {
return "UserDevfileImpl{"
+ "id='"
+ id
+ '\''
+ ", devfile="
+ devfile
+ ", metaGeneratedName='"
+ metaGeneratedName
+ '\''
+ ", metaName='"
+ metaName
+ '\''
+ ", name='"
+ name
+ '\''
+ ", description='"
+ description
+ '\''
+ ", account="
+ account
+ '}';
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.spi;
import com.google.common.annotations.Beta;
import java.util.List;
import java.util.Optional;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.commons.lang.Pair;
/** Defines data access object contract for {@code UserDevfileImpl}. */
@Beta
public interface UserDevfileDao {
/**
* Creates Devfile.
*
* @param devfile devfile to create
* @return created devfile
* @throws NullPointerException when {@code devfile} is null
* @throws ServerException when any other error occurs
* @throws ConflictException when required namespace is not found.
*/
UserDevfile create(UserDevfile devfile) throws ServerException, ConflictException;
/**
* Updates devfile to the new entity, using replacement strategy.
*
* @param devfile devfile to update
* @return updated devfile
* @throws NullPointerException when {@code devfile} is null
* @throws ConflictException when any conflict situation occurs
* @throws ServerException when any other error occurs
*/
Optional<UserDevfile> update(UserDevfile devfile)
throws ConflictException, ServerException, NotFoundException;
/**
* Removes devfile.
*
* @param id devfile identifier
* @throws NullPointerException when {@code id} is null
* @throws ServerException when any other error occurs
*/
void remove(String id) throws ServerException;
/**
* Gets devfile by identifier.
*
* @param id devfile identifier
* @return devfile instance, never null
* @throws NullPointerException when {@code id} is null
* @throws ServerException when any other error occurs
*/
Optional<UserDevfile> getById(String id) throws ServerException;
/**
* Gets list of UserDevfiles in given namespace.
*
* @param namespace devfiles namespace
* @return list of devfiles in given namespace. Always returns list(even when there are no devfile
* in given namespace), never null
* @throws NullPointerException when {@code namespace} is null
* @throws ServerException when any other error occurs during workspaces fetching
*/
Page<UserDevfile> getByNamespace(String namespace, int maxItems, long skipCount)
throws ServerException;
/**
* Gets all devfiles which user can read filtered by given parameters in a given order
*
* @param maxItems the maximum number of workspaces to return
* @param skipCount the number of workspaces to skip
* @param filter additional conditions for the desired devfiles. Conditions represented as pairs
* of the filed and the value. All pairs would be joined with <b>AND</b> condition. Value of
* the pair can start with 'like:' prefix. In this case would be used <i>LIKE</i> query,
* otherwise <b>=</b> condition.
* @param order - a list of fields and directions of sort. By default items would be sorted by id.
* @return list of devfiles which user can read, never null
* @throws ServerException when any other error occurs during devfile fetching
* @throws IllegalArgumentException when maxItems < 1 or skipCount < 0 or sort order is not 'asc'
* or 'desc'.
*/
Page<UserDevfile> getDevfiles(
int maxItems,
int skipCount,
List<Pair<String, String>> filter,
List<Pair<String, String>> order)
throws ServerException;
/**
* Get the count of all user devfiles from the persistent layer.
*
* @return workspace count
* @throws ServerException when any error occurs
*/
long getTotalCount() throws ServerException;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.api.devfile.shared.Constants;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.dto.server.DtoFactory;
import org.everrest.core.impl.uri.UriBuilderImpl;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class DevfileServiceLinksInjectorTest {
private static final String URI_BASE = "http://localhost:8080";
private static final String SERVICE_PATH = "/devfile";
@Mock ServiceContext context;
@BeforeMethod
public void setUp() {
final UriBuilder uriBuilder = new UriBuilderImpl();
uriBuilder.uri(URI_BASE);
when(context.getBaseUriBuilder()).thenReturn(uriBuilder);
}
@Test
public void shouldInjectLinks() {
// given
final UserDevfileDto userDevfileDto = DtoFactory.newDto(UserDevfileDto.class).withId("id123");
DevfileServiceLinksInjector linksInjector = new DevfileServiceLinksInjector();
// when
final UserDevfileDto withLinks = linksInjector.injectLinks(userDevfileDto, context);
// then
assertEquals(withLinks.getLinks().size(), 1);
assertNotNull(withLinks.getLink(Constants.LINK_REL_SELF));
assertEquals(withLinks.getLinks().get(0).getMethod(), HttpMethod.GET);
assertEquals(withLinks.getLinks().get(0).getHref(), URI_BASE + SERVICE_PATH + "/id123");
assertEquals(withLinks.getLinks().get(0).getProduces(), MediaType.APPLICATION_JSON);
}
}

View File

@ -0,0 +1,539 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static com.jayway.restassured.RestAssured.given;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.TEST_ACCOUNT;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.TEST_SUBJECT;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.USER_DEVFILE_ID;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jayway.restassured.response.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.api.core.rest.WebApplicationExceptionMapper;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileEntityProvider;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.Pair;
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.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
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;
@Listeners({EverrestJetty.class, MockitoTestNGListener.class})
public class DevfileServiceTest {
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
ApiExceptionMapper exceptionMapper = new ApiExceptionMapper();
WebApplicationExceptionMapper exceptionMapper2 = new WebApplicationExceptionMapper();
private DevfileSchemaProvider schemaProvider = new DevfileSchemaProvider();
private static final EnvironmentFilter FILTER = new EnvironmentFilter();
private DevfileParser devfileParser =
new DevfileParser(
new DevfileSchemaValidator(new DevfileSchemaProvider()),
new DevfileIntegrityValidator(Collections.emptyMap()));
DevfileEntityProvider devfileEntityProvider = new DevfileEntityProvider(devfileParser);
UserDevfileEntityProvider userDevfileEntityProvider =
new UserDevfileEntityProvider(devfileParser);
private CheJsonProvider jsonProvider = new CheJsonProvider(new HashSet<>());
@Mock UserDevfileDao userDevfileDao;
@Mock UserDevfileManager userDevfileManager;
@Mock EventService eventService;
@Mock DevfileServiceLinksInjector linksInjector;
DevfileService userDevfileService;
@Test
public void shouldRetrieveSchema() throws Exception {
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 200);
assertEquals(
response.getBody().asString(), schemaProvider.getSchemaContent(CURRENT_API_VERSION));
}
@BeforeMethod
public void setup() {
this.userDevfileService = new DevfileService(schemaProvider, userDevfileManager, linksInjector);
lenient()
.when(linksInjector.injectLinks(any(UserDevfileDto.class), any(ServiceContext.class)))
.thenAnswer((Answer<UserDevfileDto>) invocation -> invocation.getArgument(0));
}
@Test(dataProvider = "validUserDevfiles")
public void shouldCreateUserDevfileFromJson(UserDevfileDto userDevfileDto) throws Exception {
final UserDevfileImpl userDevfileImpl =
new UserDevfileImpl("id-123123", TEST_ACCOUNT, userDevfileDto);
when(userDevfileManager.createDevfile(any(UserDevfile.class))).thenReturn(userDevfileImpl);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(DtoFactory.getInstance().toJson(userDevfileDto))
.when()
.post(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 201);
UserDevfileDto dto = unwrapDto(response, UserDevfileDto.class);
assertEquals(dto.getNamespace(), TEST_ACCOUNT.getName());
assertEquals(new UserDevfileImpl(dto, TEST_ACCOUNT), userDevfileImpl);
verify(userDevfileManager).createDevfile(any(UserDevfile.class));
}
@Test(dataProvider = "invalidUserDevfiles")
public void shouldFailToCreateInvalidUserDevfileFromJson(
UserDevfileDto userDevfileDto, String expectedErrorMessage) throws Exception {
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(DtoFactory.getInstance().toJson(userDevfileDto))
.when()
.post(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 400);
ServiceError error = unwrapDto(response, ServiceError.class);
assertNotNull(error);
assertEquals(error.getMessage(), expectedErrorMessage);
verifyNoMoreInteractions(userDevfileManager);
}
@Test
public void shouldGetUserDevfileById() throws Exception {
final UserDevfileImpl userDevfile = TestObjectGenerator.createUserDevfile();
when(userDevfileManager.getById(eq("id-22323"))).thenReturn(userDevfile);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.expect()
.statusCode(200)
.get(SECURE_PATH + "/devfile/id-22323");
assertEquals(
new UserDevfileImpl(unwrapDto(response, UserDevfileDto.class), TEST_ACCOUNT), userDevfile);
verify(userDevfileManager).getById(eq("id-22323"));
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldThrowNotFoundExceptionWhenUserDevfileIsNotExistOnGetById() throws Exception {
final String errMessage = format("UserDevfile with id %s is not found", USER_DEVFILE_ID);
doThrow(new NotFoundException(errMessage)).when(userDevfileManager).getById(anyString());
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.expect()
.statusCode(404)
.when()
.get(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), errMessage);
}
@Test
public void shouldThrowNotFoundExceptionWhenUpdatingNonExistingUserDevfile() throws Exception {
// given
final UserDevfile userDevfile =
DtoConverter.asDto(TestObjectGenerator.createUserDevfile("devfile-name"));
doThrow(new NotFoundException(format("User devfile with id %s is not found.", USER_DEVFILE_ID)))
.when(userDevfileManager)
.updateUserDevfile(any(UserDevfile.class));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(userDevfile))
.when()
.put(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
// then
assertEquals(response.getStatusCode(), 404);
assertEquals(
unwrapDto(response, ServiceError.class).getMessage(),
format("User devfile with id %s is not found.", USER_DEVFILE_ID));
}
@Test
public void shouldBeAbleToUpdateUserDevfile() throws Exception {
// given
final UserDevfileDto devfileDto = TestObjectGenerator.createUserDevfileDto();
final UserDevfileImpl userDevfileImpl = new UserDevfileImpl(devfileDto, TEST_ACCOUNT);
when(userDevfileManager.updateUserDevfile(any(UserDevfile.class))).thenReturn(userDevfileImpl);
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(devfileDto))
.when()
.put(SECURE_PATH + "/devfile/" + devfileDto.getId());
// then
assertEquals(response.getStatusCode(), 200);
assertEquals(
new UserDevfileImpl(unwrapDto(response, UserDevfileDto.class), TEST_ACCOUNT),
userDevfileImpl);
verify(userDevfileManager).updateUserDevfile(devfileDto);
verify(linksInjector).injectLinks(any(), any());
}
@Test(dataProvider = "invalidUserDevfiles")
public void shouldFailToUpdateWithInvalidUserDevfile(
UserDevfileDto userDevfileDto, String expectedErrorMessage) throws Exception {
// given
userDevfileDto = userDevfileDto.withId("id-123");
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(userDevfileDto))
.when()
.put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 400);
ServiceError error = unwrapDto(response, ServiceError.class);
assertNotNull(error);
assertEquals(error.getMessage(), expectedErrorMessage);
verifyZeroInteractions(userDevfileManager);
verifyZeroInteractions(linksInjector);
}
@Test
public void shouldOverrideIdOnUpdateUserDevfile() throws Exception {
// given
final UserDevfileDto devfileDto = TestObjectGenerator.createUserDevfileDto();
final UserDevfileImpl userDevfileImpl = new UserDevfileImpl(devfileDto, TEST_ACCOUNT);
final String newID = NameGenerator.generate("id", 24);
final UserDevfileImpl expectedUserDevfileImpl =
new UserDevfileImpl(newID, TEST_ACCOUNT, userDevfileImpl);
final UserDevfileDto expectedDto =
org.eclipse.che.api.devfile.server.DtoConverter.asDto(expectedUserDevfileImpl);
when(userDevfileManager.updateUserDevfile(any(UserDevfile.class)))
.thenReturn(expectedUserDevfileImpl);
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(devfileDto))
.when()
.put(SECURE_PATH + "/devfile/" + newID);
// then
assertEquals(response.getStatusCode(), 200);
assertEquals(
new UserDevfileImpl(unwrapDto(response, UserDevfileDto.class), TEST_ACCOUNT),
expectedUserDevfileImpl);
verify(userDevfileManager).updateUserDevfile(expectedDto);
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldRemoveUserDevfileByGivenIdentifier() throws Exception {
// given
// when
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.expect()
.statusCode(204)
.when()
.delete(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
// then
verify(userDevfileManager).removeUserDevfile(USER_DEVFILE_ID);
}
@Test
public void shouldNotThrowAnyExceptionWhenRemovingNonExistingUserDevfile() throws Exception {
// given
Mockito.doNothing().when(userDevfileManager).removeUserDevfile(anyString());
// when
Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
// then
assertEquals(response.getStatusCode(), 204);
}
@Test
public void shouldGetUserDevfilesAvailableToUser() throws Exception {
// given
final UserDevfileDto devfileDto = TestObjectGenerator.createUserDevfileDto();
final UserDevfileImpl userDevfileImpl = new UserDevfileImpl(devfileDto, TEST_ACCOUNT);
doReturn(new Page<>(ImmutableList.of(userDevfileImpl), 0, 1, 1))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.expect()
.statusCode(200)
.get(SECURE_PATH + "/devfile/search");
// then
final List<UserDevfileDto> res = unwrapDtoList(response, UserDevfileDto.class);
assertEquals(res.size(), 1);
assertEquals(res.get(0).withLinks(emptyList()), devfileDto);
verify(userDevfileManager).getUserDevfiles(eq(30), eq(0), anyList(), anyList());
}
@Test
public void shouldBeAbleToSetLimitAndOffsetOnUserDevfileSearch() throws Exception {
// given
doReturn(new Page<>(Collections.emptyList(), 0, 1, 0))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.queryParam("maxItems", 5)
.queryParam("skipCount", 52)
.when()
.expect()
.statusCode(200)
.get(SECURE_PATH + "/devfile/search");
// then
verify(userDevfileManager).getUserDevfiles(eq(5), eq(52), anyList(), anyList());
}
@Test
public void shouldBeAbleToSetFiltertOnUserDevfileSearch() throws Exception {
// given
doReturn(new Page<>(Collections.emptyList(), 0, 1, 0))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
Map<String, String> parameters =
ImmutableMap.of("id", "sdfsdf5", "devfile.meta.name", "like:%dfdf");
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.queryParameter("id", "sdfsdf5")
.queryParameter("devfile.meta.name", "like:%dfdf")
.when()
.expect()
.statusCode(200)
.get(SECURE_PATH + "/devfile/search");
// then
Class<List<Pair<String, String>>> listClass =
(Class<List<Pair<String, String>>>) (Class) ArrayList.class;
ArgumentCaptor<List<Pair<String, String>>> filterCaptor = ArgumentCaptor.forClass(listClass);
verify(userDevfileManager).getUserDevfiles(eq(30), eq(0), filterCaptor.capture(), anyList());
assertEquals(
filterCaptor.getValue(),
ImmutableList.of(new Pair("devfile.meta.name", "like:%dfdf"), new Pair("id", "sdfsdf5")));
}
@Test
public void shouldBeAbleToSetOrderOnUserDevfileSearch() throws Exception {
// given
doReturn(new Page<>(Collections.emptyList(), 0, 1, 0))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.queryParameter("order", "id:asc,name:desc")
.when()
.expect()
.statusCode(200)
.get(SECURE_PATH + "/devfile/search");
// then
Class<List<Pair<String, String>>> listClass =
(Class<List<Pair<String, String>>>) (Class) ArrayList.class;
ArgumentCaptor<List<Pair<String, String>>> orderCaptor = ArgumentCaptor.forClass(listClass);
verify(userDevfileManager).getUserDevfiles(eq(30), eq(0), anyList(), orderCaptor.capture());
assertEquals(
orderCaptor.getValue(), ImmutableList.of(new Pair("id", "asc"), new Pair("name", "desc")));
}
@DataProvider
public Object[][] validUserDevfiles() {
return new Object[][] {
{
newDto(UserDevfileDto.class)
.withName("My devfile")
.withDescription("Devfile description")
.withDevfile(
newDto(DevfileDto.class)
.withApiVersion("1.0.0")
.withMetadata(newDto(MetadataDto.class).withName("name")))
},
{
newDto(UserDevfileDto.class)
.withName(null)
.withDescription("Devfile description")
.withDevfile(
newDto(DevfileDto.class)
.withApiVersion("1.0.0")
.withMetadata(newDto(MetadataDto.class).withName("name")))
},
{
newDto(UserDevfileDto.class)
.withName("My devfile")
.withDevfile(
newDto(DevfileDto.class)
.withApiVersion("1.0.0")
.withMetadata(newDto(MetadataDto.class).withName("name")))
},
{
newDto(UserDevfileDto.class)
.withName("My devfile")
.withDescription("Devfile description")
.withDevfile(
newDto(DevfileDto.class)
.withApiVersion("1.0.0")
.withMetadata(newDto(MetadataDto.class).withGenerateName("gen-")))
},
{DtoConverter.asDto(TestObjectGenerator.createUserDevfile())}
};
}
@DataProvider
public Object[][] invalidUserDevfiles() {
return new Object[][] {
{
newDto(UserDevfileDto.class)
.withName("My devfile")
.withDescription("Devfile description")
.withDevfile(null),
"Mandatory field `devfile` is not defined."
},
{
newDto(UserDevfileDto.class)
.withName("My devfile")
.withDescription("Devfile description")
.withDevfile(
newDto(DevfileDto.class)
.withApiVersion(null)
.withMetadata(newDto(MetadataDto.class).withName("name"))),
"Devfile schema validation failed. Error: The object must have a property whose name is \"apiVersion\"."
}
};
}
private static <T> T unwrapDto(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance().createDtoFromJson(response.asString(), dtoClass);
}
private static <T> List<T> unwrapDtoList(Response response, Class<T> dtoClass)
throws IOException {
return new ArrayList<>(
DtoFactory.getInstance().createListDtoFromJson(response.body().asInputStream(), dtoClass));
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(TEST_SUBJECT);
}
}
}

View File

@ -0,0 +1,217 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import org.eclipse.che.account.shared.model.Account;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
public class TestObjectGenerator {
public static final String TEST_CHE_NAMESPACE = "user";
public static final String CURRENT_USER_ID = NameGenerator.generate("usrid", 6);
public static final Subject TEST_SUBJECT =
new SubjectImpl(TEST_CHE_NAMESPACE, CURRENT_USER_ID, "token", false);
public static final String USER_DEVFILE_ID = NameGenerator.generate("usrd", 16);
public static final AccountImpl TEST_ACCOUNT =
new AccountImpl("acc-id042u3ui3oi", TEST_CHE_NAMESPACE, "test");
public static UserDevfileDto createUserDevfileDto() {
return DtoConverter.asDto(createUserDevfile(NameGenerator.generate("name", 6)));
}
public static UserDevfileImpl createUserDevfile() {
return createUserDevfile(NameGenerator.generate("name", 6));
}
public static UserDevfileImpl createUserDevfile(String name) {
return createUserDevfile(NameGenerator.generate("id", 6), name);
}
public static UserDevfileImpl createUserDevfile(String id, String name) {
return new UserDevfileImpl(id, TEST_ACCOUNT, name, "devfile description", createDevfile(name));
}
public static UserDevfileImpl createUserDevfile(String id, Account account, String name) {
return new UserDevfileImpl(id, account, name, "devfile description", createDevfile(name));
}
public static UserDevfileImpl createUserDevfile(Account account) {
return createUserDevfile(
NameGenerator.generate("id", 6), account, NameGenerator.generate("name", 6));
}
public static DevfileImpl createDevfile(String generatedName) {
return createDevfile(null, generatedName);
}
public static DevfileImpl createDevfileWithName(String name) {
return createDevfile(name, null);
}
private static DevfileImpl createDevfile(String name, String generatedName) {
String effectiveName = MoreObjects.firstNonNull(name, generatedName);
SourceImpl source1 =
new SourceImpl(
"type1",
"http://location",
"branch1",
"point1",
"tag1",
"commit1",
"sparseCheckoutDir1");
ProjectImpl project1 = new ProjectImpl("project1", source1, "path1");
SourceImpl source2 =
new SourceImpl(
"type2",
"http://location",
"branch2",
"point2",
"tag2",
"commit2",
"sparseCheckoutDir2");
ProjectImpl project2 = new ProjectImpl("project2", source2, "path2");
ActionImpl action1 =
new ActionImpl("exec1", "component1", "run.sh", "/home/user/1", null, null);
ActionImpl action2 =
new ActionImpl("exec2", "component2", "run.sh", "/home/user/2", null, null);
CommandImpl command1 =
new CommandImpl(
effectiveName + "-1", singletonList(action1), singletonMap("attr1", "value1"), null);
CommandImpl command2 =
new CommandImpl(
effectiveName + "-2", singletonList(action2), singletonMap("attr2", "value2"), null);
EntrypointImpl entrypoint1 =
new EntrypointImpl(
"parentName1",
singletonMap("parent1", "selector1"),
"containerName1",
asList("command1", "command2"),
asList("arg1", "arg2"));
EntrypointImpl entrypoint2 =
new EntrypointImpl(
"parentName2",
singletonMap("parent2", "selector2"),
"containerName2",
asList("command3", "command4"),
asList("arg3", "arg4"));
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume1 =
new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name1", "path1");
org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume2 =
new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name2", "path2");
EnvImpl env1 = new EnvImpl("name1", "value1");
EnvImpl env2 = new EnvImpl("name2", "value2");
EndpointImpl endpoint1 = new EndpointImpl("name1", 1111, singletonMap("key1", "value1"));
EndpointImpl endpoint2 = new EndpointImpl("name2", 2222, singletonMap("key2", "value2"));
ComponentImpl component1 =
new ComponentImpl(
"kubernetes",
"component1",
null,
null,
null,
null,
"refcontent1",
ImmutableMap.of("app.kubernetes.io/component", "db"),
null,
null,
null,
null,
null,
null,
false,
false,
null,
null,
null,
asList(env1, env2),
null);
component1.setSelector(singletonMap("key1", "value1"));
ComponentImpl component2 =
new ComponentImpl(
"dockerimage",
"component2",
null,
null,
null,
null,
null,
null,
null,
"image",
"256G",
null,
"3",
"180m",
false,
false,
singletonList("command"),
singletonList("arg"),
asList(volume1, volume2),
asList(env1, env2),
asList(endpoint1, endpoint2));
ComponentImpl component3 =
new ComponentImpl(
"chePlugin",
"check/terminal-sample/0.0.1",
ImmutableMap.of(
"java.home",
"/home/user/jdk11aertwertert",
"java.boolean",
"true",
"java.long",
"123444L"));
MetadataImpl metadata = new MetadataImpl(name);
metadata.setGenerateName(generatedName);
DevfileImpl devfile =
new DevfileImpl(
"1.0.0",
asList(project1, project2),
asList(component1, component2, component3),
asList(command1, command2),
singletonMap("attribute1", "value1"),
metadata);
return devfile;
}
}

View File

@ -0,0 +1,194 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Arrays.asList;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.TEST_ACCOUNT;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.TEST_CHE_NAMESPACE;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.createUserDevfile;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import java.util.Collections;
import java.util.Optional;
import org.eclipse.che.account.api.AccountManager;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.devfile.shared.event.DevfileCreatedEvent;
import org.eclipse.che.api.devfile.shared.event.DevfileDeletedEvent;
import org.eclipse.che.api.devfile.shared.event.DevfileUpdatedEvent;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(value = MockitoTestNGListener.class)
public class UserDevfileManagerTest {
@Mock UserDevfileDao userDevfileDao;
@Mock EventService eventService;
@Mock AccountManager accountManager;
@InjectMocks UserDevfileManager userDevfileManager;
@Captor private ArgumentCaptor<UserDevfileImpl> userDevfileArgumentCaptor;
@Captor private ArgumentCaptor<DevfileCreatedEvent> devfileCreatedEventCaptor;
@Captor private ArgumentCaptor<DevfileDeletedEvent> devfileDeletedEventCaptor;
@Captor private ArgumentCaptor<DevfileUpdatedEvent> devfileUpdatedEventCaptor;
@BeforeMethod
public void setUp() throws Exception {
EnvironmentContext.getCurrent().setSubject(TestObjectGenerator.TEST_SUBJECT);
lenient().doReturn(TEST_ACCOUNT).when(accountManager).getByName(eq(TEST_CHE_NAMESPACE));
}
@Test
public void shouldGenerateUserDevfileIdOnCreation() throws Exception {
// given
final UserDevfileImpl userDevfile =
new UserDevfileImpl(null, TEST_ACCOUNT, createUserDevfile());
when(userDevfileDao.create(any(UserDevfileImpl.class)))
.thenAnswer(invocationOnMock -> invocationOnMock.getArguments()[0]);
// when
UserDevfile actual = userDevfileManager.createDevfile(userDevfile);
// then
verify(userDevfileDao).create(userDevfileArgumentCaptor.capture());
assertFalse(isNullOrEmpty(userDevfileArgumentCaptor.getValue().getId()));
assertEquals(new UserDevfileImpl(null, TEST_ACCOUNT, actual), userDevfile);
}
@Test
public void shouldSendDevfileCreatedEventOnCreation() throws Exception {
// given
final UserDevfileImpl userDevfile =
new UserDevfileImpl(null, TEST_ACCOUNT, createUserDevfile());
when(userDevfileDao.create(any(UserDevfileImpl.class)))
.thenAnswer(invocationOnMock -> invocationOnMock.getArguments()[0]);
// when
UserDevfile expected = userDevfileManager.createDevfile(userDevfile);
// then
verify(eventService).publish(devfileCreatedEventCaptor.capture());
assertEquals(expected, devfileCreatedEventCaptor.getValue().getUserDevfile());
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGettingUserDevfileByNullId() throws Exception {
userDevfileManager.getById(null);
}
@Test
public void shouldGetUserDevfileById() throws Exception {
// given
final Optional<UserDevfile> toFetch = Optional.of(createUserDevfile());
when(userDevfileDao.getById(eq("id123"))).thenReturn(toFetch);
// when
final UserDevfile fetched = userDevfileManager.getById("id123");
// then
assertEquals(fetched, toFetch.get());
verify(userDevfileDao).getById("id123");
}
@Test(
expectedExceptions = NotFoundException.class,
expectedExceptionsMessageRegExp = "Devfile with id 'id123' doesn't exist")
public void shouldThrowNotFoundExceptionOnGetUserDevfileByIdIfNotFound() throws Exception {
// given
doReturn(Optional.empty()).when(userDevfileDao).getById(eq("id123"));
// when
userDevfileManager.getById("id123");
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenUpdatingUserDevfileByNullId() throws Exception {
userDevfileManager.updateUserDevfile(null);
}
@Test
public void shouldUpdateUserDevfile() throws Exception {
// given
final UserDevfileImpl userDevfile = createUserDevfile();
when(userDevfileDao.update(any(UserDevfileImpl.class)))
.thenAnswer(invocationOnMock -> Optional.of(invocationOnMock.getArguments()[0]));
// when
userDevfileManager.updateUserDevfile(userDevfile);
// then
verify(userDevfileDao).update(eq(userDevfile));
}
@Test(expectedExceptions = NotFoundException.class)
public void shouldThrowNotFoundIfUserDevfileIsNotFoundOnUpdate() throws Exception {
// given
final UserDevfileImpl userDevfile = createUserDevfile();
Mockito.doReturn(Optional.empty()).when(userDevfileDao).update(any(UserDevfileImpl.class));
// when
userDevfileManager.updateUserDevfile(userDevfile);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhendeleteUserDevfileByNullId() throws Exception {
userDevfileManager.removeUserDevfile(null);
}
@Test
public void shouldRemoveUserDevfile() throws Exception {
// given
final UserDevfileImpl userDevfile = createUserDevfile();
// when
userDevfileManager.removeUserDevfile(userDevfile.getId());
// then
verify(userDevfileDao).remove(userDevfile.getId());
}
@Test
public void shouldSendDevfileUpdatedEventOnUpdateDevfile() throws Exception {
// given
final UserDevfileImpl userDevfile = createUserDevfile();
when(userDevfileDao.update(any(UserDevfileImpl.class)))
.thenAnswer(invocationOnMock -> Optional.of(invocationOnMock.getArguments()[0]));
// when
userDevfileManager.updateUserDevfile(userDevfile);
// then
verify(eventService).publish(devfileUpdatedEventCaptor.capture());
assertEquals(userDevfile, devfileUpdatedEventCaptor.getValue().getUserDevfile());
}
@Test
public void shouldBeAbleToGetUserDevfilesAvailableToUser() throws ServerException {
// given
final UserDevfileImpl userDevfile = createUserDevfile();
final UserDevfileImpl userDevfile2 = createUserDevfile();
when(userDevfileDao.getDevfiles(2, 30, Collections.emptyList(), Collections.emptyList()))
.thenReturn(new Page<>(asList(userDevfile, userDevfile2), 0, 2, 2));
// when
Page<UserDevfile> actual =
userDevfileManager.getUserDevfiles(2, 30, Collections.emptyList(), Collections.emptyList());
// then
assertEquals(actual.getItems().size(), 2);
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.jpa;
import com.google.inject.TypeLiteral;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.account.spi.jpa.JpaAccountDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.workspace.server.devfile.SerializableConverter;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl;
import org.eclipse.che.commons.test.db.H2DBTestServer;
import org.eclipse.che.commons.test.db.H2JpaCleaner;
import org.eclipse.che.commons.test.db.PersistTestModuleBuilder;
import org.eclipse.che.commons.test.tck.TckModule;
import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
import org.eclipse.che.commons.test.tck.repository.JpaTckRepository;
import org.eclipse.che.commons.test.tck.repository.TckRepository;
import org.eclipse.che.core.db.DBInitializer;
import org.eclipse.che.core.db.h2.jpa.eclipselink.H2ExceptionHandler;
import org.eclipse.che.core.db.schema.SchemaInitializer;
import org.eclipse.che.core.db.schema.impl.flyway.FlywaySchemaInitializer;
import org.h2.Driver;
/** Tck module for UserDevfile test. */
public class UserDevfileTckModule extends TckModule {
@Override
protected void configure() {
H2DBTestServer server = H2DBTestServer.startDefault();
install(
new PersistTestModuleBuilder()
.setDriver(Driver.class)
.runningOn(server)
.addEntityClasses(
UserImpl.class,
AccountImpl.class,
UserDevfileImpl.class,
DevfileImpl.class,
ActionImpl.class,
CommandImpl.class,
ComponentImpl.class,
DevfileImpl.class,
EndpointImpl.class,
EntrypointImpl.class,
EnvImpl.class,
ProjectImpl.class,
SourceImpl.class,
VolumeImpl.class)
.addClass(SerializableConverter.class)
.setExceptionHandler(H2ExceptionHandler.class)
.setProperty("eclipselink.logging.level", "OFF")
.build());
bind(DBInitializer.class).asEagerSingleton();
bind(SchemaInitializer.class)
.toInstance(new FlywaySchemaInitializer(server.getDataSource(), "che-schema"));
bind(TckResourcesCleaner.class).toInstance(new H2JpaCleaner(server));
bind(UserDevfileDao.class).to(JpaUserDevfileDao.class);
bind(AccountDao.class).to(JpaAccountDao.class);
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
bind(new TypeLiteral<TckRepository<UserImpl>>() {})
.toInstance(new JpaTckRepository<>(UserImpl.class));
bind(new TypeLiteral<TckRepository<AccountImpl>>() {})
.toInstance(new JpaTckRepository<>(AccountImpl.class));
}
}

View File

@ -0,0 +1,589 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server.spi.tck;
import static java.lang.Math.min;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.createUserDevfile;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Optional;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.persistence.EntityManager;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.devfile.server.TestObjectGenerator;
import org.eclipse.che.api.devfile.server.event.BeforeDevfileRemovedEvent;
import org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.test.tck.TckListener;
import org.eclipse.che.commons.test.tck.repository.TckRepository;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(TckListener.class)
@Test(suiteName = UserDevfileDaoTest.SUITE_NAME)
public class UserDevfileDaoTest {
public static final String SUITE_NAME = "DevfileDaoTck";
private static final int ENTRY_COUNT = 10;
private static final int COUNT_OF_ACCOUNTS = 6;
private UserDevfileImpl[] devfiles;
private AccountImpl[] accounts;
@Inject private EventService eventService;
@Inject private UserDevfileDao userDevfileDaoDao;
@Inject private TckRepository<UserDevfileImpl> devfileTckRepository;
@Inject private TckRepository<UserImpl> userTckRepository;
@Inject private TckRepository<AccountImpl> accountRepo;
@Inject private Provider<EntityManager> entityManagerProvider;
@BeforeMethod
public void setUp() throws Exception {
accounts = new AccountImpl[COUNT_OF_ACCOUNTS];
for (int i = 0; i < COUNT_OF_ACCOUNTS; i++) {
accounts[i] = new AccountImpl("accountId" + i, "accountName" + i, "test");
}
devfiles = new UserDevfileImpl[ENTRY_COUNT];
for (int i = 0; i < ENTRY_COUNT; i++) {
AccountImpl account = accounts[i / 2];
devfiles[i] =
createUserDevfile(
NameGenerator.generate("id-" + i + "-", 6),
account,
NameGenerator.generate("devfileName-" + i, 6));
}
accountRepo.createAll(Stream.of(accounts).map(AccountImpl::new).collect(toList()));
devfileTckRepository.createAll(Stream.of(devfiles).map(UserDevfileImpl::new).collect(toList()));
}
@AfterMethod
public void cleanUp() throws Exception {
devfileTckRepository.removeAll();
accountRepo.removeAll();
}
@Test
public void shouldGetUserDevfileById() throws Exception {
final UserDevfileImpl devfile = devfiles[0];
assertEquals(userDevfileDaoDao.getById(devfile.getId()), Optional.of(devfile));
}
@Test(dependsOnMethods = "shouldGetUserDevfileById")
public void shouldCreateUserDevfile() throws Exception {
// given
final UserDevfileImpl devfile = createUserDevfile(accounts[0]);
// when
userDevfileDaoDao.create(devfile);
assertEquals(
userDevfileDaoDao.getById(devfile.getId()), Optional.of(new UserDevfileImpl(devfile)));
}
@Test
public void shouldCreateUserDevfileWithNullDescription() throws Exception {
// given
final UserDevfileImpl devfile = createUserDevfile(accounts[0]);
devfile.setDescription(null);
// when
userDevfileDaoDao.create(devfile);
Optional<UserDevfile> devfileOptional = userDevfileDaoDao.getById(devfile.getId());
assertTrue(devfileOptional.isPresent());
assertNull(devfileOptional.get().getDescription());
assertEquals(devfileOptional, Optional.of(new UserDevfileImpl(devfile)));
}
@Test
public void shouldCreateUserDevfileWithEmptyMataName() throws Exception {
// given
final UserDevfileImpl devfile = createUserDevfile(accounts[0]);
DevfileImpl newDevfile = new DevfileImpl(devfile.getDevfile());
MetadataImpl newMeta = new MetadataImpl();
newMeta.setGenerateName("gener-");
newDevfile.setMetadata(newMeta);
devfile.setDevfile(newDevfile);
// when
userDevfileDaoDao.create(devfile);
Optional<UserDevfile> devfileOptional = userDevfileDaoDao.getById(devfile.getId());
assertTrue(devfileOptional.isPresent());
UserDevfile actual = devfileOptional.get();
assertNull(actual.getDevfile().getMetadata().getName());
assertNotNull(actual.getDevfile().getMetadata().getGenerateName());
assertEquals(devfileOptional, Optional.of(new UserDevfileImpl(devfile)));
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenCreateNullDevfile() throws Exception {
userDevfileDaoDao.create(null);
}
@Test(expectedExceptions = ConflictException.class)
public void shouldThrowConflictExceptionWhenCreatingUserDevfileWithExistingId() throws Exception {
// given
final UserDevfileImpl devfile = createUserDevfile(accounts[0]);
final UserDevfileImpl existing = devfiles[0];
devfile.setId(existing.getId());
// when
userDevfileDaoDao.create(devfile);
// then
}
@Test
public void shouldUpdateUserDevfile() throws Exception {
// given
DevfileImpl newDevfile = TestObjectGenerator.createDevfile("newUpdate");
newDevfile.setApiVersion("V15.0");
newDevfile.setProjects(
ImmutableList.of(
new ProjectImpl(
"projectUp2",
new SourceImpl(
"typeUp2",
"http://location",
"branch2",
"point2",
"tag2",
"commit2",
"sparseCheckoutDir2"),
"path2")));
newDevfile.setComponents(ImmutableList.of(new ComponentImpl("type3", "id54")));
newDevfile.setCommands(
ImmutableList.of(
new CommandImpl(
new CommandImpl(
"cmd1",
Collections.singletonList(
new ActionImpl(
"exe44", "compo2nent2", "run.sh", "/home/user/2", null, null)),
Collections.singletonMap("attr1", "value1"),
null))));
newDevfile.setAttributes(ImmutableMap.of("key2", "val34"));
newDevfile.setMetadata(new MetadataImpl("myNewName"));
final UserDevfileImpl update = devfiles[0];
update.setDevfile(newDevfile);
// when
userDevfileDaoDao.update(update);
// then
assertEquals(userDevfileDaoDao.getById(update.getId()), Optional.of(update));
}
@Test
public void shouldNotUpdateWorkspaceWhichDoesNotExist() throws Exception {
// given
final UserDevfileImpl userDevfile = devfiles[0];
userDevfile.setId("non-existing-devfile");
// when
Optional<UserDevfile> result = userDevfileDaoDao.update(userDevfile);
// then
assertFalse(result.isPresent());
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenUpdatingNull() throws Exception {
userDevfileDaoDao.update(null);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGetByIdNull() throws Exception {
userDevfileDaoDao.getById(null);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenDeleteNull() throws Exception {
userDevfileDaoDao.getById(null);
}
@Test(dependsOnMethods = "shouldGetUserDevfileById")
public void shouldRemoveDevfile() throws Exception {
final String userDevfileId = devfiles[0].getId();
userDevfileDaoDao.remove(userDevfileId);
Optional<UserDevfile> result = userDevfileDaoDao.getById(userDevfileId);
assertFalse(result.isPresent());
}
@Test
public void shouldDoNothingWhenRemovingNonExistingUserDevfile() throws Exception {
userDevfileDaoDao.remove("non-existing");
}
@Test
public void shouldBeAbleToGetAvailableToUserDevfiles() throws ServerException {
// given
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(30, 0, Collections.emptyList(), Collections.emptyList());
// then
assertEquals(new HashSet<>(result.getItems()), new HashSet<>(asList(devfiles)));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void shouldThrowIllegalStateExceptionOnNegativeLimit() throws Exception {
userDevfileDaoDao.getDevfiles(0, -2, Collections.emptyList(), Collections.emptyList());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void shouldThrowIllegalStateExceptionOnNegativeSkipCount() throws Exception {
userDevfileDaoDao.getDevfiles(-2, 0, Collections.emptyList(), Collections.emptyList());
}
@Test
public void shouldBeAbleToGetAvailableToUserDevfilesWithFilter() throws ServerException {
// given
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
30,
0,
ImmutableList.of(new Pair<>("name", "like:devfileName%")),
Collections.emptyList());
// then
assertEquals(new HashSet<>(result.getItems()), new HashSet<>(asList(devfiles)));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void shouldNotAllowSearchWithInvalidFilter() throws ServerException {
// given
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
30,
0,
ImmutableList.of(
new Pair<>("id", "like:dev%"),
new Pair<>("devfile.metadata.something", "like:devfileName%")),
Collections.emptyList());
// then
}
@Test
public void shouldBeAbleToGetAvailableToUserDevfilesWithFilter2()
throws ServerException, NotFoundException, ConflictException {
// given
final UserDevfileImpl update = devfiles[0];
update.setName("New345Name");
userDevfileDaoDao.update(update);
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
30, 0, ImmutableList.of(new Pair<>("name", "like:%w345N%")), Collections.emptyList());
// then
assertEquals(new HashSet<>(result.getItems()), ImmutableSet.of(update));
}
@Test
public void shouldBeAbleToGetAvailableToUserDevfilesWithFilterAndLimit()
throws ServerException, NotFoundException, ConflictException {
// given
final UserDevfileImpl update = devfiles[0];
update.setName("New345Name");
userDevfileDaoDao.update(update);
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
12,
0,
ImmutableList.of(new Pair<>("name", "like:devfileName%")),
Collections.emptyList());
// then
assertEquals(result.getItems().size(), 9);
}
@Test
public void shouldBeAbleToGetDevfilesSortedById()
throws ServerException, NotFoundException, ConflictException {
// given
UserDevfileImpl[] expected =
Arrays.stream(devfiles)
.sorted(Comparator.comparing(UserDevfileImpl::getId))
.toArray(UserDevfileImpl[]::new);
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
devfiles.length, 0, Collections.emptyList(), ImmutableList.of(new Pair<>("id", "asc")));
// then
assertEquals(result.getItems().stream().toArray(UserDevfileImpl[]::new), expected);
}
@Test
public void shouldBeAbleToGetDevfilesSortedByIdReverse()
throws ServerException, NotFoundException, ConflictException {
// given
UserDevfileImpl[] expected =
Arrays.stream(devfiles)
.sorted(Comparator.comparing(UserDevfileImpl::getId).reversed())
.toArray(UserDevfileImpl[]::new);
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
devfiles.length,
0,
Collections.emptyList(),
ImmutableList.of(new Pair<>("id", "desc")));
// then
assertEquals(result.getItems().stream().toArray(UserDevfileImpl[]::new), expected);
}
@Test
public void shouldBeAbleToGetDevfilesSortedByName()
throws ServerException, NotFoundException, ConflictException {
// given
UserDevfileImpl[] expected =
Arrays.stream(devfiles)
.sorted(Comparator.comparing(UserDevfileImpl::getName))
.toArray(UserDevfileImpl[]::new);
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
devfiles.length,
0,
Collections.emptyList(),
ImmutableList.of(new Pair<>("name", "asc")));
// then
assertEquals(result.getItems().stream().toArray(UserDevfileImpl[]::new), expected);
}
@Test
public void shouldSendDevfileDeletedEventOnRemoveUserDevfile() throws Exception {
// given
final String userDevfileId = devfiles[0].getId();
final boolean[] isNotified = new boolean[] {false};
eventService.subscribe(event -> isNotified[0] = true, BeforeDevfileRemovedEvent.class);
// when
userDevfileDaoDao.remove(userDevfileId);
// then
assertTrue(isNotified[0], "Event subscriber notified");
}
@Test(dataProvider = "boundsdataprovider")
public void shouldBeAbleToGetDevfilesSortedByNameWithSpecificMaxItemsAndSkipCount(
int maxitems, int skipCount) throws ServerException, NotFoundException, ConflictException {
// given
UserDevfileImpl[] expected =
Arrays.stream(
Arrays.copyOfRange(devfiles, skipCount, min(devfiles.length, skipCount + maxitems)))
.sorted(Comparator.comparing(UserDevfileImpl::getId))
.toArray(UserDevfileImpl[]::new);
// when
final Page<UserDevfile> result =
userDevfileDaoDao.getDevfiles(
maxitems,
skipCount,
Collections.emptyList(),
ImmutableList.of(new Pair<>("id", "asc")));
// then
assertEquals(result.getItems().stream().toArray(UserDevfileImpl[]::new), expected);
}
@DataProvider
public Object[][] boundsdataprovider() {
return new Object[][] {
{1, 1},
{1, 4},
{4, 5},
{6, 8},
{1, ENTRY_COUNT},
{ENTRY_COUNT, ENTRY_COUNT},
{ENTRY_COUNT, 1},
{ENTRY_COUNT, 8}
};
}
@Test(
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = "The number of items has to be positive.")
public void shouldNotAllowZeroMaxItemsToSearch()
throws ServerException, NotFoundException, ConflictException {
// given
// when
userDevfileDaoDao.getDevfiles(0, 0, Collections.emptyList(), Collections.emptyList());
// then
}
@Test(
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = "The number of items has to be positive.")
public void shouldNotAllowNegativeMaxItemsToSearch()
throws ServerException, NotFoundException, ConflictException {
// given
// when
userDevfileDaoDao.getDevfiles(-5, 0, Collections.emptyList(), Collections.emptyList());
// then
}
@Test(
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp =
"The number of items to skip can't be negative or greater than 2147483647")
public void shouldNotAllowNegativeItemsToSkipToSearch()
throws ServerException, NotFoundException, ConflictException {
// given
// when
userDevfileDaoDao.getDevfiles(5, -1, Collections.emptyList(), Collections.emptyList());
// then
}
@Test(
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp =
"Invalid sort order direction\\. Possible values are 'asc' or 'desc'"
+ " but got: \\[\\{first=name, second=ddd}, \\{first=id, second=bla}]")
public void shouldFailOnInvalidSortOrder()
throws ServerException, NotFoundException, ConflictException {
// given
// when
userDevfileDaoDao.getDevfiles(
5,
4,
Collections.emptyList(),
ImmutableList.of(
new Pair<>("id", "asc"),
new Pair<>("name", "ddd"),
new Pair<>("name", "DesC"),
new Pair<>("id", "bla")));
// then
}
@Test
public void shouldGetDevfilesByNamespace() throws Exception {
final UserDevfileImpl devfile1 = devfiles[0];
final UserDevfileImpl devfile2 = devfiles[1];
assertEquals(devfile1.getNamespace(), devfile2.getNamespace(), "Namespaces must be the same");
final Page<UserDevfile> found = userDevfileDaoDao.getByNamespace(devfile1.getNamespace(), 6, 0);
assertEquals(found.getTotalItemsCount(), 2);
assertEquals(found.getItemsCount(), 2);
assertEquals(new HashSet<>(found.getItems()), new HashSet<>(asList(devfile1, devfile2)));
}
@Test
public void emptyListShouldBeReturnedWhenThereAreNoDevfilesInGivenNamespace() throws Exception {
assertTrue(userDevfileDaoDao.getByNamespace("non-existing-namespace", 30, 0).isEmpty());
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGettingDevfilesByNullNamespace() throws Exception {
userDevfileDaoDao.getByNamespace(null, 30, 0);
}
@Test(dataProvider = "validOrderFiled")
public void shouldTestValidOrderFileds(String filed) {
JpaUserDevfileDao.UserDevfileSearchQueryBuilder queryBuilder =
new JpaUserDevfileDao.UserDevfileSearchQueryBuilder(null);
try {
queryBuilder.withOrder(ImmutableList.of(new Pair<>(filed, "blah")));
} catch (IllegalArgumentException e) {
Assert.fail(filed + " is valid but failed");
}
}
@Test(
dataProvider = "invalidOrderField",
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = "Order allowed only by \\[id, name\\] but got: .*")
public void shouldTestInvalidOrderFileds(String filed) {
JpaUserDevfileDao.UserDevfileSearchQueryBuilder queryBuilder =
new JpaUserDevfileDao.UserDevfileSearchQueryBuilder(null);
queryBuilder.withOrder(ImmutableList.of(new Pair<>(filed, "blah")));
}
@Test(dataProvider = "validSearchFiled")
public void shouldTestValidSearchFileds(String filed) {
JpaUserDevfileDao.UserDevfileSearchQueryBuilder queryBuilder =
new JpaUserDevfileDao.UserDevfileSearchQueryBuilder(null);
try {
queryBuilder.withFilter(ImmutableList.of(new Pair<>(filed, "blah")));
} catch (IllegalArgumentException e) {
Assert.fail(filed + " is valid but failed");
}
}
@Test(
dataProvider = "invalidSearchField",
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = "Filtering allowed only by \\[name\\] but got: .*")
public void shouldTestInvalidSearchFileds(String filed) {
JpaUserDevfileDao.UserDevfileSearchQueryBuilder queryBuilder =
new JpaUserDevfileDao.UserDevfileSearchQueryBuilder(null);
queryBuilder.withFilter(ImmutableList.of(new Pair<>(filed, "blah")));
}
@DataProvider
public Object[][] validOrderFiled() {
return new Object[][] {{"id"}, {"Id"}, {"name"}, {"nAmE"}};
}
@DataProvider
public Object[][] invalidOrderField() {
return new Object[][] {{"devfile"}, {"meta_name"}, {"description"}, {"meta_generated_name"}};
}
@DataProvider
public Object[][] validSearchFiled() {
return new Object[][] {
{"name"}, {"NaMe"},
};
}
@DataProvider
public Object[][] invalidSearchField() {
return new Object[][] {
{"id"}, {"devfile"}, {"ID"}, {"meta_name"}, {"description"}, {"meta_generated_name"}
};
}
}

View File

@ -0,0 +1 @@
org.eclipse.che.api.devfile.server.jpa.UserDevfileTckModule

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2018 Red Hat, Inc.
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
Red Hat, Inc. - initial API and implementation
-->
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</configuration>

View File

@ -64,7 +64,7 @@ public class JpaFactoryDao implements FactoryDao {
format("Factory with name '%s' already exists for current user", factory.getName()));
} catch (IntegrityConstraintViolationException ex) {
throw new ConflictException(
"Could not create factory with creator that refers on non-existent user");
"Could not create factory with creator that refers to a non-existent user");
} catch (RuntimeException ex) {
throw new ServerException(ex.getLocalizedMessage(), ex);
}

View File

@ -28,7 +28,7 @@ import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl.DevfileLocation;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.server.DtoConverter;
import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
@ -53,18 +53,18 @@ public class URLFactoryBuilder {
private final String defaultChePlugins;
private final URLFetcher urlFetcher;
private final DevfileManager devfileManager;
private final DevfileParser devfileParser;
@Inject
public URLFactoryBuilder(
@Named("che.factory.default_editor") String defaultCheEditor,
@Named("che.factory.default_plugins") String defaultChePlugins,
URLFetcher urlFetcher,
DevfileManager devfileManager) {
DevfileParser devfileParser) {
this.defaultCheEditor = defaultCheEditor;
this.defaultChePlugins = defaultChePlugins;
this.urlFetcher = urlFetcher;
this.devfileManager = devfileManager;
this.devfileParser = devfileParser;
}
/**
@ -115,8 +115,8 @@ public class URLFactoryBuilder {
continue;
}
try {
DevfileImpl devfile = devfileManager.parseYaml(devfileYamlContent, overrideProperties);
devfileManager.resolveReference(devfile, fileContentProvider);
DevfileImpl devfile = devfileParser.parseYaml(devfileYamlContent, overrideProperties);
devfileParser.resolveReference(devfile, fileContentProvider);
devfile = ensureToUseGenerateName(devfile);
FactoryDto factoryDto =

View File

@ -31,7 +31,7 @@ import java.util.Map;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.URLFileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
@ -76,10 +76,10 @@ public class DefaultFactoryParameterResolverTest {
DevfileIntegrityValidator integrityValidator = new DevfileIntegrityValidator(validators);
DevfileManager devfileManager = new DevfileManager(validator, integrityValidator);
DevfileParser devfileParser = new DevfileParser(validator, integrityValidator);
URLFactoryBuilder factoryBuilder =
new URLFactoryBuilder("editor", "plugin", urlFetcher, devfileManager);
new URLFactoryBuilder("editor", "plugin", urlFetcher, devfileParser);
DefaultFactoryParameterResolver res =
new DefaultFactoryParameterResolver(factoryBuilder, urlFetcher);

View File

@ -35,7 +35,7 @@ import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl.DevfileLocation;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
@ -68,7 +68,7 @@ public class URLFactoryBuilderTest {
/** Grab content of URLs */
@Mock private URLFetcher urlFetcher;
@Mock private DevfileManager devfileManager;
@Mock private DevfileParser devfileParser;
/** Tested instance. */
private URLFactoryBuilder urlFactoryBuilder;
@ -76,7 +76,7 @@ public class URLFactoryBuilderTest {
@BeforeClass
public void setUp() {
this.urlFactoryBuilder =
new URLFactoryBuilder(defaultEditor, defaultPlugin, urlFetcher, devfileManager);
new URLFactoryBuilder(defaultEditor, defaultPlugin, urlFetcher, devfileParser);
}
@Test
@ -132,7 +132,7 @@ public class URLFactoryBuilderTest {
workspaceConfigImpl.setDefaultEnv("name");
when(urlFetcher.fetchSafely(anyString())).thenReturn("random_content");
when(devfileManager.parseYaml(anyString(), anyMap())).thenReturn(devfile);
when(devfileParser.parseYaml(anyString(), anyMap())).thenReturn(devfile);
FactoryDto factory =
urlFactoryBuilder
@ -192,7 +192,7 @@ public class URLFactoryBuilderTest {
return "http://foo.bar/anything";
}
}));
when(devfileManager.parseYaml(anyString(), anyMap())).thenReturn(devfile);
when(devfileParser.parseYaml(anyString(), anyMap())).thenReturn(devfile);
when(urlFetcher.fetchSafely(anyString())).thenReturn("anything");
FactoryDto factory =
urlFactoryBuilder

View File

@ -35,7 +35,7 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileFormatException;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
@ -54,12 +54,12 @@ import org.eclipse.che.dto.server.DtoFactory;
public class WorkspaceEntityProvider
implements MessageBodyReader<WorkspaceDto>, MessageBodyWriter<WorkspaceDto> {
private DevfileManager devfileManager;
private DevfileParser devfileParser;
private ObjectMapper mapper = new ObjectMapper();
@Inject
public WorkspaceEntityProvider(DevfileManager devfileManager) {
this.devfileManager = devfileManager;
public WorkspaceEntityProvider(DevfileParser devfileParser) {
this.devfileParser = devfileParser;
}
@Override
@ -81,7 +81,7 @@ public class WorkspaceEntityProvider
JsonNode wsNode = mapper.readTree(entityStream);
JsonNode devfileNode = wsNode.path("devfile");
if (!devfileNode.isNull() && !devfileNode.isMissingNode()) {
devfileManager.parseJson(devfileNode.toString());
devfileParser.parseJson(devfileNode.toString());
}
return DtoFactory.getInstance().createDtoFromJson(wsNode.toString(), WorkspaceDto.class);
} catch (DevfileFormatException e) {

View File

@ -55,11 +55,11 @@ import org.eclipse.che.dto.server.DtoFactory;
public class DevfileEntityProvider
implements MessageBodyReader<DevfileDto>, MessageBodyWriter<DevfileDto> {
private DevfileManager devfileManager;
private DevfileParser devfileParser;
@Inject
public DevfileEntityProvider(DevfileManager devfileManager) {
this.devfileManager = devfileManager;
public DevfileEntityProvider(DevfileParser devfileParser) {
this.devfileParser = devfileParser;
}
@Override
@ -81,13 +81,13 @@ public class DevfileEntityProvider
try {
if (mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE)) {
return asDto(
devfileManager.parseJson(
devfileParser.parseJson(
CharStreams.toString(
new InputStreamReader(entityStream, getCharsetOrUtf8(mediaType)))));
} else if (mediaType.isCompatible(MediaType.valueOf("text/yaml"))
|| mediaType.isCompatible(MediaType.valueOf("text/x-yaml"))) {
return asDto(
devfileManager.parseYaml(
devfileParser.parseYaml(
CharStreams.toString(
new InputStreamReader(entityStream, getCharsetOrUtf8(mediaType)))));
}

View File

@ -24,7 +24,6 @@ public class DevfileModule extends AbstractModule {
@Override
protected void configure() {
bind(DevfileService.class);
bind(DevfileEntityProvider.class);
DevfileBindings.onWorkspaceApplierBinder(

View File

@ -45,7 +45,7 @@ import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
*/
@Beta
@Singleton
public class DevfileManager {
public class DevfileParser {
private ObjectMapper yamlMapper;
private ObjectMapper jsonMapper;
@ -54,7 +54,7 @@ public class DevfileManager {
private final OverridePropertiesApplier overridePropertiesApplier;
@Inject
public DevfileManager(
public DevfileParser(
DevfileSchemaValidator schemaValidator, DevfileIntegrityValidator integrityValidator) {
this(
schemaValidator,
@ -64,7 +64,7 @@ public class DevfileManager {
}
@VisibleForTesting
DevfileManager(
DevfileParser(
DevfileSchemaValidator schemaValidator,
DevfileIntegrityValidator integrityValidator,
ObjectMapper yamlMapper,

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server.devfile;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.io.IOException;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
@Api(value = "/devfile", description = "Devfile REST API")
@Path("/devfile")
public class DevfileService extends Service {
private DevfileSchemaProvider schemaCachedProvider;
@Inject
public DevfileService(DevfileSchemaProvider schemaCachedProvider) {
this.schemaCachedProvider = schemaCachedProvider;
}
/**
* Retrieves the json schema.
*
* @return json schema
*/
@GET
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Retrieves current version of devfile JSON schema")
@ApiResponses({
@ApiResponse(code = 200, message = "The schema successfully retrieved"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
public Response getSchema() throws ServerException {
try {
return Response.ok(schemaCachedProvider.getSchemaContent(CURRENT_API_VERSION)).build();
} catch (IOException e) {
throw new ServerException(e);
}
}
}

View File

@ -35,6 +35,11 @@ public class MetadataImpl implements Metadata {
this.name = name;
}
public MetadataImpl(String name, String generateName) {
this.name = name;
this.generateName = generateName;
}
public MetadataImpl(Metadata metadata) {
this.name = metadata.getName();
this.generateName = metadata.getGenerateName();

View File

@ -20,7 +20,7 @@ import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
@ -34,14 +34,14 @@ import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class WorkspaceEntityProviderTest {
@Mock private DevfileManager devfileManager;
@Mock private DevfileParser devfileParser;
@InjectMocks private WorkspaceEntityProvider workspaceEntityProvider;
@Test
public void shouldBuildDtoFromValidJson() throws Exception {
when(devfileManager.parseJson(anyString())).thenReturn(new DevfileImpl());
when(devfileParser.parseJson(anyString())).thenReturn(new DevfileImpl());
WorkspaceDto actual = newDto(WorkspaceDto.class).withDevfile(newDto(DevfileDto.class));
@ -54,6 +54,6 @@ public class WorkspaceEntityProviderTest {
new ByteArrayInputStream(
DtoFactory.getInstance().toJson(actual).getBytes(StandardCharsets.UTF_8)));
verify(devfileManager).parseJson(anyString());
verify(devfileParser).parseJson(anyString());
}
}

View File

@ -69,7 +69,12 @@ import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.workspace.server.devfile.DevfileEntityProvider;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
import org.eclipse.che.api.workspace.server.model.impl.CommandImpl;
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
@ -139,6 +144,13 @@ public class WorkspaceServiceTest {
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
private CheJsonProvider jsonProvider = new CheJsonProvider(Collections.emptySet());
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
private DevfileEntityProvider devfileEntityProvider =
new DevfileEntityProvider(
new DevfileParser(
new DevfileSchemaValidator(new DevfileSchemaProvider()),
new DevfileIntegrityValidator(Collections.emptyMap())));
@Mock private WorkspaceManager wsManager;
@Mock private MachineTokenProvider machineTokenProvider;
@Mock private WorkspaceLinksGenerator linksGenerator;

View File

@ -29,14 +29,14 @@ import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class DevfileEntityProviderTest {
@Mock private DevfileManager devfileManager;
@Mock private DevfileParser devfileParser;
@InjectMocks private DevfileEntityProvider devfileEntityProvider;
@Test
public void shouldBuildDtoFromValidYaml() throws Exception {
when(devfileManager.parseYaml(anyString())).thenReturn(new DevfileImpl());
when(devfileParser.parseYaml(anyString())).thenReturn(new DevfileImpl());
devfileEntityProvider.readFrom(
DevfileDto.class,
@ -46,13 +46,13 @@ public class DevfileEntityProviderTest {
new MultivaluedHashMap<>(),
getClass().getClassLoader().getResourceAsStream("devfile/devfile.yaml"));
verify(devfileManager).parseYaml(anyString());
verify(devfileParser).parseYaml(anyString());
}
@Test
public void shouldBuildDtoFromValidJson() throws Exception {
when(devfileManager.parseJson(anyString())).thenReturn(new DevfileImpl());
when(devfileParser.parseJson(anyString())).thenReturn(new DevfileImpl());
devfileEntityProvider.readFrom(
DevfileDto.class,
@ -62,7 +62,7 @@ public class DevfileEntityProviderTest {
new MultivaluedHashMap<>(),
getClass().getClassLoader().getResourceAsStream("devfile/devfile.json"));
verify(devfileManager).parseJson(anyString());
verify(devfileParser).parseJson(anyString());
}
@Test(

View File

@ -43,7 +43,7 @@ import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class DevfileManagerTest {
public class DevfileParserTest {
private static final String DEVFILE_YAML_CONTENT = "devfile yaml stub";
@ -56,13 +56,12 @@ public class DevfileManagerTest {
@Mock private JsonNode devfileJsonNode;
private DevfileImpl devfile;
private DevfileManager devfileManager;
private DevfileParser devfileParser;
@BeforeMethod
public void setUp() throws Exception {
devfile = new DevfileImpl();
devfileManager =
new DevfileManager(schemaValidator, integrityValidator, yamlMapper, jsonMapper);
devfileParser = new DevfileParser(schemaValidator, integrityValidator, yamlMapper, jsonMapper);
lenient().when(jsonMapper.treeToValue(any(), eq(DevfileImpl.class))).thenReturn(devfile);
lenient().when(yamlMapper.treeToValue(any(), eq(DevfileImpl.class))).thenReturn(devfile);
@ -72,7 +71,7 @@ public class DevfileManagerTest {
@Test
public void testValidateAndParse() throws Exception {
// when
DevfileImpl parsed = devfileManager.parseYaml(DEVFILE_YAML_CONTENT);
DevfileImpl parsed = devfileParser.parseYaml(DEVFILE_YAML_CONTENT);
// then
assertEquals(parsed, devfile);
@ -93,7 +92,7 @@ public class DevfileManagerTest {
devfile.getComponents().add(component);
// when
DevfileImpl parsed = devfileManager.parseYaml(DEVFILE_YAML_CONTENT);
DevfileImpl parsed = devfileParser.parseYaml(DEVFILE_YAML_CONTENT);
// then
assertNotNull(parsed.getCommands().get(0).getAttributes());
@ -113,7 +112,7 @@ public class DevfileManagerTest {
devfile.getComponents().add(component);
// when
devfileManager.resolveReference(devfile, contentProvider);
devfileParser.resolveReference(devfile, contentProvider);
// then
verify(contentProvider).fetchContent(eq("myfile.yaml"));
@ -125,7 +124,7 @@ public class DevfileManagerTest {
expectedExceptionsMessageRegExp = "Unable to parse Devfile - provided source is empty")
public void shouldThrowDevfileExceptionWhenEmptyObjectProvided() throws Exception {
// when
devfileManager.parseJson("{}");
devfileParser.parseJson("{}");
}
@Test(
@ -133,7 +132,7 @@ public class DevfileManagerTest {
expectedExceptionsMessageRegExp = "Unable to parse Devfile - provided source is empty")
public void shouldThrowDevfileExceptionWhenEmptySourceProvided() throws Exception {
// when
devfileManager.parseJson("");
devfileParser.parseJson("");
}
@Test(
@ -150,7 +149,7 @@ public class DevfileManagerTest {
devfile.getComponents().add(component);
// when
devfileManager.resolveReference(devfile, contentProvider);
devfileParser.resolveReference(devfile, contentProvider);
// then exception is thrown
}
@ -163,7 +162,7 @@ public class DevfileManagerTest {
doThrow(new DevfileFormatException("non valid")).when(schemaValidator).validate(any());
// when
devfileManager.parseYaml(DEVFILE_YAML_CONTENT);
devfileParser.parseYaml(DEVFILE_YAML_CONTENT);
}
@Test(
@ -177,6 +176,6 @@ public class DevfileManagerTest {
doThrow(jsonException).when(jsonMapper).treeToValue(any(), any());
// when
devfileManager.parseJson(DEVFILE_YAML_CONTENT);
devfileParser.parseJson(DEVFILE_YAML_CONTENT);
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server.devfile;
import static com.jayway.restassured.RestAssured.given;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import static org.everrest.assured.JettyHttpServer.*;
import static org.testng.Assert.assertEquals;
import com.jayway.restassured.response.Response;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.everrest.assured.EverrestJetty;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners({EverrestJetty.class, MockitoTestNGListener.class})
public class DevfileServiceTest {
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
private ApiExceptionMapper exceptionMapper;
private DevfileSchemaProvider schemaProvider = new DevfileSchemaProvider();
private static final Subject SUBJECT = new SubjectImpl("user", "user123", "token", false);
@SuppressWarnings("unused")
private DevfileService devFileService;
@BeforeMethod
public void initService() {
this.devFileService = new DevfileService(schemaProvider);
}
@Test
public void shouldRetrieveSchema() throws Exception {
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 200);
assertEquals(
response.getBody().asString(), schemaProvider.getSchemaContent(CURRENT_API_VERSION));
}
}

View File

@ -0,0 +1,29 @@
--
-- Copyright (c) 2012-2020 Red Hat, Inc.
-- This program and the accompanying materials are made
-- available under the terms of the Eclipse Public License 2.0
-- which is available at https://www.eclipse.org/legal/epl-2.0/
--
-- SPDX-License-Identifier: EPL-2.0
--
-- Contributors:
-- Red Hat, Inc. - initial API and implementation
--
-- add userdevfile table
CREATE TABLE userdevfile (
id VARCHAR(255) NOT NULL UNIQUE,
accountid VARCHAR(255) NOT NULL,
devfile_id BIGINT NOT NULL UNIQUE,
meta_generated_name VARCHAR(255) ,
meta_name VARCHAR(255) ,
name VARCHAR(255) NOT NULL ,
description TEXT ,
PRIMARY KEY (id)
);
CREATE INDEX index_userdevfile_devfile_id ON userdevfile (devfile_id);
CREATE INDEX index_userdevfile_name ON userdevfile(name);
ALTER TABLE userdevfile ADD CONSTRAINT unq_userdevfile_0 UNIQUE (name, accountid);
ALTER TABLE userdevfile ADD CONSTRAINT fx_userdevfile_accountid FOREIGN KEY (accountid) REFERENCES account (id);
ALTER TABLE userdevfile ADD CONSTRAINT fk_userdevfile_devfile_id FOREIGN KEY (devfile_id) REFERENCES devfile (id);

View File

@ -90,6 +90,17 @@
<artifactId>che-core-api-account</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>

View File

@ -24,6 +24,9 @@ import javax.persistence.EntityManager;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.account.spi.jpa.JpaAccountDao;
import org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.ssh.server.jpa.JpaSshDao;
import org.eclipse.che.api.ssh.server.model.impl.SshPairImpl;
import org.eclipse.che.api.ssh.server.spi.SshDao;
@ -135,6 +138,7 @@ public class MySqlTckModule extends TckModule {
SignatureKeyImpl.class,
SignatureKeyPairImpl.class,
// devfile
UserDevfileImpl.class,
ActionImpl.class,
org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl.class,
ComponentImpl.class,
@ -196,6 +200,10 @@ public class MySqlTckModule extends TckModule {
bind(WorkspaceActivityDao.class).to(JpaWorkspaceActivityDao.class);
bind(new TypeLiteral<TckRepository<WorkspaceImpl>>() {}).toInstance(new WorkspaceRepository());
bind(UserDevfileDao.class).to(JpaUserDevfileDao.class);
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
// sign keys
bind(SignatureKeyDao.class).to(JpaSignatureKeyDao.class);
bind(new TypeLiteral<TckRepository<SignatureKeyPairImpl>>() {})

View File

@ -80,6 +80,17 @@
<artifactId>che-core-api-account</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-devfile</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>

View File

@ -24,6 +24,9 @@ import javax.persistence.EntityManager;
import org.eclipse.che.account.spi.AccountDao;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.account.spi.jpa.JpaAccountDao;
import org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.ssh.server.jpa.JpaSshDao;
import org.eclipse.che.api.ssh.server.model.impl.SshPairImpl;
import org.eclipse.che.api.ssh.server.spi.SshDao;
@ -130,6 +133,7 @@ public class PostgreSqlTckModule extends TckModule {
WorkspaceActivity.class,
VolumeImpl.class,
// devfile
UserDevfileImpl.class,
ActionImpl.class,
org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl.class,
ComponentImpl.class,
@ -191,6 +195,9 @@ public class PostgreSqlTckModule extends TckModule {
bind(WorkspaceActivityDao.class).to(JpaWorkspaceActivityDao.class);
bind(new TypeLiteral<TckRepository<WorkspaceImpl>>() {}).toInstance(new WorkspaceRepository());
bind(UserDevfileDao.class).to(JpaUserDevfileDao.class);
bind(new TypeLiteral<TckRepository<UserDevfileImpl>>() {})
.toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
// k8s runtimes
bind(new TypeLiteral<TckRepository<KubernetesRuntimeState>>() {})
.toInstance(new JpaTckRepository<>(KubernetesRuntimeState.class));

View File

@ -33,6 +33,8 @@
<module>che-core-api-workspace</module>
<module>che-core-api-workspace-activity</module>
<module>che-core-api-user-shared</module>
<module>che-core-api-devfile-shared</module>
<module>che-core-api-devfile</module>
<module>che-core-api-account</module>
<module>che-core-api-user</module>
<module>che-core-api-factory-shared</module>