Factory support of devfile

6.19.x
Max Shaposhnik 2018-12-26 16:20:28 +02:00 committed by GitHub
parent b872b317cb
commit cea3f482db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 1123 additions and 1105 deletions

View File

@ -369,10 +369,6 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-machine-ext-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-url-factory</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId>

View File

@ -30,8 +30,8 @@ import org.eclipse.che.api.core.notification.RemoteSubscriptionStorage;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.core.rest.MessageBodyAdapter;
import org.eclipse.che.api.core.rest.MessageBodyAdapterInterceptor;
import org.eclipse.che.api.devfile.server.DevfileSchemaValidator;
import org.eclipse.che.api.devfile.server.DevfileService;
import org.eclipse.che.api.devfile.server.validator.DevfileSchemaValidator;
import org.eclipse.che.api.factory.server.FactoryAcceptValidator;
import org.eclipse.che.api.factory.server.FactoryCreateValidator;
import org.eclipse.che.api.factory.server.FactoryEditValidator;
@ -141,6 +141,7 @@ public class WsMasterModule extends AbstractModule {
bind(org.eclipse.che.api.factory.server.FactoryService.class);
install(new org.eclipse.che.api.factory.server.jpa.FactoryJpaModule());
// Service-specific factory resolvers.
Multibinder<FactoryParametersResolver> factoryParametersResolverMultibinder =
Multibinder.newSetBinder(binder(), FactoryParametersResolver.class);
factoryParametersResolverMultibinder.addBinding().to(GithubFactoryParametersResolver.class);

View File

@ -574,3 +574,11 @@ che.wsagent.cors.allowed_origins=NULL
che.wsagent.cors.allow_credentials=true
# This property is used to provide value for WS Agent CORS enabling.
che.wsagent.cors.enabled=true
## Factory defaults.
# Editor and plugin which will be used for factories which are created from remote git repository
# which doesn't contain any Che-specific workspace descriptors (like .devfile of .factory.json)
che.factory.default_editor=org.eclipse.che.editor.theia:1.0.0
# multiple plugins must be comma-separated, for example:
# pluginFooName:pluginFooVersion,pluginBarName:pluginBarVersion
che.factory.default_plugins=che-machine-exec-plugin:0.0.1

View File

@ -87,7 +87,6 @@ import org.eclipse.che.api.workspace.server.spi.WorkspaceDao;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.commons.test.db.H2DBTestServer;
import org.eclipse.che.commons.test.db.H2JpaCleaner;
@ -342,10 +341,7 @@ public class JpaEntitiesCascadeRemovalTest {
assertTrue(preferenceDao.getPreferences(user.getId()).isEmpty());
assertTrue(sshDao.get(user.getId()).isEmpty());
assertTrue(workspaceDao.getByNamespace(account.getName(), 30, 0).isEmpty());
assertTrue(
factoryDao
.getByAttribute(0, 0, singletonList(Pair.of("creator.userId", user.getId())))
.isEmpty());
assertTrue(factoryDao.getByUser(user.getId(), 30, 0).isEmpty());
// Check workers and parent entity is removed
assertTrue(workspaceDao.getByNamespace(user2.getId(), 30, 0).isEmpty());
assertEquals(workerDao.getWorkers(workspace3.getId(), 1, 0).getTotalItemsCount(), 0);

View File

@ -30,10 +30,6 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
@ -62,14 +58,6 @@
<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-inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-url-factory</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>

View File

@ -1,24 +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.plugin.github.factory.resolver;
import com.google.inject.AbstractModule;
import org.eclipse.che.inject.DynaModule;
/** @author Max Shaposhnik (mshaposhnik@codenvy.com) */
@DynaModule
public class GitHubFactoryModule extends AbstractModule {
@Override
protected void configure() {
bind(GithubURLParser.class).to(LegacyGithubURLParser.class);
}
}

View File

@ -11,39 +11,54 @@
*/
package org.eclipse.che.plugin.github.factory.resolver;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.plugin.urlfactory.ProjectConfigDtoMerger;
import org.eclipse.che.plugin.urlfactory.URLFactoryBuilder;
/**
* Provides Factory Parameters resolver for github repositories.
*
* @author Florent Benoit
*/
@Singleton
public class GithubFactoryParametersResolver implements FactoryParametersResolver {
/** Parameter name. */
protected static final String URL_PARAMETER_NAME = "url";
/** Parser which will allow to check validity of URLs and create objects. */
@Inject private GithubURLParser githubUrlParser;
private GithubURLParser githubUrlParser;
/** Builder allowing to build objects from github URL. */
@Inject private GithubSourceStorageBuilder githubSourceStorageBuilder;
private GithubSourceStorageBuilder githubSourceStorageBuilder;
@Inject private URLFactoryBuilder urlFactoryBuilder;
/** Builds factory by fetching json/devfile content from given URL */
private URLFactoryBuilder urlFactoryBuilder;
/** ProjectDtoMerger */
@Inject private ProjectConfigDtoMerger projectConfigDtoMerger;
@Inject
public GithubFactoryParametersResolver(
GithubURLParser githubUrlParser,
GithubSourceStorageBuilder githubSourceStorageBuilder,
URLFactoryBuilder urlFactoryBuilder,
ProjectConfigDtoMerger projectConfigDtoMerger) {
this.githubUrlParser = githubUrlParser;
this.githubSourceStorageBuilder = githubSourceStorageBuilder;
this.urlFactoryBuilder = urlFactoryBuilder;
this.projectConfigDtoMerger = projectConfigDtoMerger;
}
/**
* Check if this resolver can be used with the given parameters.
*
@ -66,30 +81,35 @@ public class GithubFactoryParametersResolver implements FactoryParametersResolve
*/
@Override
public FactoryDto createFactory(@NotNull final Map<String, String> factoryParameters)
throws BadRequestException {
throws BadRequestException, ServerException {
// no need to check null value of url parameter as accept() method has performed the check
final GithubUrl githubUrl = githubUrlParser.parse(factoryParameters.get("url"));
final GithubUrl githubUrl = githubUrlParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
// create factory from the following location if location exists, else create default factory
FactoryDto factory = urlFactoryBuilder.createFactory(githubUrl.factoryJsonFileLocation());
FactoryDto factory =
urlFactoryBuilder
.createFactoryFromDevfile(githubUrl.devfileFileLocation())
.orElseGet(
() ->
urlFactoryBuilder
.createFactoryFromJson(githubUrl.factoryFileLocation())
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION)));
// add workspace configuration if not defined
if (factory.getWorkspace() == null) {
factory.setWorkspace(
urlFactoryBuilder.buildWorkspaceConfig(
githubUrl.getRepository(), githubUrl.getUsername(), githubUrl.dockerFileLocation()));
urlFactoryBuilder.buildDefaultWorkspaceConfig(githubUrl.getRepository()));
}
// Compute project configuration
ProjectConfigDto projectConfigDto =
newDto(ProjectConfigDto.class)
.withSource(githubSourceStorageBuilder.build(githubUrl))
.withName(githubUrl.getRepository())
.withType("blank")
.withPath("/".concat(githubUrl.getRepository()));
// apply merging operation from existing and computed settings
return projectConfigDtoMerger.merge(factory, projectConfigDto);
// apply merging operation from existing and computed settings if needed
return projectConfigDtoMerger.merge(
factory,
() -> {
// Compute project configuration
return newDto(ProjectConfigDto.class)
.withSource(githubSourceStorageBuilder.build(githubUrl))
.withName(githubUrl.getRepository())
.withPath("/".concat(githubUrl.getRepository()));
});
}
}

View File

@ -16,6 +16,7 @@ import static org.eclipse.che.dto.server.DtoFactory.newDto;
import com.google.common.base.Strings;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Singleton;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto;
@ -24,6 +25,7 @@ import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto;
*
* @author Florent Benoit
*/
@Singleton
public class GithubSourceStorageBuilder {
/**
@ -42,7 +44,7 @@ public class GithubSourceStorageBuilder {
}
return newDto(SourceStorageDto.class)
.withLocation(githubUrl.repositoryLocation())
.withType("git")
.withType("github")
.withParameters(parameters);
}
}

View File

@ -11,26 +11,86 @@
*/
package org.eclipse.che.plugin.github.factory.resolver;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import org.eclipse.che.api.factory.server.urlfactory.URLFetcher;
/**
* Interface for Gitlab repository URL parsers.
* Parser of String Github URLs and provide {@link GithubUrl} objects.
*
* @author Max Shaposhnik
* @author Florent Benoit
*/
public interface GithubURLParser {
@Singleton
public class GithubURLParser {
/** Fetcher to grab PR data */
@Inject private URLFetcher urlFetcher;
/**
* Check if the URL is a valid Github url for the given provider.
*
* @param url a not null string representation of URL
* @return {@code true} if the URL is a valid url for the given provider.
* Regexp to find repository details (repository name, project name and branch and subfolder)
* Examples of valid URLs are in the test class.
*/
boolean isValid(String url);
protected static final Pattern GITHUB_PATTERN =
Pattern.compile(
"^(?:http)(?:s)?(?:\\:\\/\\/)github.com/(?<repoUser>[^/]++)/(?<repoName>[^/]++)((?:/tree/(?<branchName>[^/]++)(?:/(?<subFolder>.*))?)|(/pull/(?<pullRequestId>[^/]++)))?$");
/**
* Provides a parsed URL object of the given provider type.
*
* @param url URL to transform into a managed object
* @return managed url object
*/
GithubUrl parse(String url);
/** Regexp to find repository and branch name from PR link */
protected static final Pattern PR_DATA_PATTERN =
Pattern.compile(
".*<div class=\"State[\\s|\\S]+(?<prState>Closed|Open|Merged)[\\s|\\S]+<\\/div>[\\s|\\S]+into[\\s]+(from[\\s]*)*<span title=\"(?<prRepoUser>[^\\\\/]+)\\/(?<prRepoName>[^\\:]+):(?<prBranch>[^\\\"]+).*",
Pattern.DOTALL);
public boolean isValid(@NotNull String url) {
return GITHUB_PATTERN.matcher(url).matches();
}
public GithubUrl parse(String url) {
// Apply github url to the regexp
Matcher matcher = GITHUB_PATTERN.matcher(url);
if (!matcher.matches()) {
throw new IllegalArgumentException(
String.format(
"The given github url %s is not a valid URL github url. It should start with https://github.com/<user>/<repo>",
url));
}
String repoUser = matcher.group("repoUser");
String repoName = matcher.group("repoName");
String branchName = matcher.group("branchName");
String pullRequestId = matcher.group("pullRequestId");
if (pullRequestId != null) {
// there is a Pull Request ID, analyze content to extract repository and branch to use
String prData = this.urlFetcher.fetch(url);
Matcher prMatcher = PR_DATA_PATTERN.matcher(prData);
if (prMatcher.matches()) {
String prState = prMatcher.group("prState");
if (!"open".equalsIgnoreCase(prState)) {
throw new IllegalArgumentException(
String.format(
"The given Pull Request url %s is not Opened, (found %s), thus it can't be opened as branch may have been removed.",
url, prState));
}
repoUser = prMatcher.group("prRepoUser");
repoName = prMatcher.group("prRepoName");
branchName = prMatcher.group("prBranch");
} else {
throw new IllegalArgumentException(
String.format(
"The given Pull Request github url %s is not a valid Pull Request URL github url. Unable to extract the data",
url));
}
}
return new GithubUrl()
.withUsername(repoUser)
.withRepository(repoName)
.withBranch(branchName)
.withSubfolder(matcher.group("subFolder"))
.withDevfileFilename(".devfile")
.withFactoryFilename(".factory.json");
}
}

View File

@ -1,96 +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.plugin.github.factory.resolver;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import org.eclipse.che.plugin.urlfactory.URLFetcher;
/**
* Parser of String Github URLs and provide {@link GithubUrl} objects.
*
* @author Florent Benoit
*/
public class GithubURLParserImpl implements GithubURLParser {
/** Fetcher to grab PR data */
@Inject private URLFetcher urlFetcher;
/**
* Regexp to find repository details (repository name, project name and branch and subfolder)
* Examples of valid URLs are in the test class.
*/
protected static final Pattern GITHUB_PATTERN =
Pattern.compile(
"^(?:http)(?:s)?(?:\\:\\/\\/)github.com/(?<repoUser>[^/]++)/(?<repoName>[^/]++)((?:/tree/(?<branchName>[^/]++)(?:/(?<subFolder>.*))?)|(/pull/(?<pullRequestId>[^/]++)))?$");
/** Regexp to find repository and branch name from PR link */
protected static final Pattern PR_DATA_PATTERN =
Pattern.compile(
".*<div class=\"State[\\s|\\S]+(?<prState>Closed|Open|Merged)[\\s|\\S]+<\\/div>[\\s|\\S]+from[\\s|\\S]+<span title=\"(?<prRepoUser>[^\\\\/]+)\\/(?<prRepoName>[^\\:]+):(?<prBranch>[^\\\"]+).*",
Pattern.DOTALL);
@Override
public boolean isValid(@NotNull String url) {
return GITHUB_PATTERN.matcher(url).matches();
}
@Override
public GithubUrl parse(String url) {
// Apply github url to the regexp
Matcher matcher = GITHUB_PATTERN.matcher(url);
if (!matcher.matches()) {
throw new IllegalArgumentException(
String.format(
"The given github url %s is not a valid URL github url. It should start with https://github.com/<user>/<repo>",
url));
}
String repoUser = matcher.group("repoUser");
String repoName = matcher.group("repoName");
String branchName = matcher.group("branchName");
String pullRequestId = matcher.group("pullRequestId");
if (pullRequestId != null) {
// there is a Pull Request ID, analyze content to extract repository and branch to use
String prData = this.urlFetcher.fetch(url);
Matcher prMatcher = PR_DATA_PATTERN.matcher(prData);
if (prMatcher.matches()) {
String prState = prMatcher.group("prState");
if (!"open".equalsIgnoreCase(prState)) {
throw new IllegalArgumentException(
String.format(
"The given Pull Request url %s is not Opened, (found %s), thus it can't be opened as branch may have been removed.",
url, prState));
}
repoUser = prMatcher.group("prRepoUser");
repoName = prMatcher.group("prRepoName");
branchName = prMatcher.group("prBranch");
} else {
throw new IllegalArgumentException(
String.format(
"The given Pull Request github url %s is not a valid Pull Request URL github url. Unable to extract the data",
url));
}
}
return new GithubUrl()
.withUsername(repoUser)
.withRepository(repoName)
.withBranch(branchName)
.withSubfolder(matcher.group("subFolder"))
.withDockerfileFilename(".factory.dockerfile")
.withFactoryFilename(".factory.json");
}
}

View File

@ -39,12 +39,12 @@ public class GithubUrl {
/** Subfolder if any */
private String subfolder;
/** Dockerfile filename */
private String dockerfileFilename;
/** Factory json filename */
private String factoryFilename;
/** Devfile filename */
private String devfileFilename;
/**
* Creation of this instance is made by the parser so user may not need to create a new instance
* directly
@ -80,16 +80,16 @@ public class GithubUrl {
}
/**
* Gets dockerfile file name of this github url
* Gets devfile file name of this github url
*
* @return the dockerfile file name
* @return the devfile file name
*/
public String getDockerfileFilename() {
return this.dockerfileFilename;
public String getDevfileFilename() {
return this.devfileFilename;
}
protected GithubUrl withDockerfileFilename(String dockerfileFilename) {
this.dockerfileFilename = dockerfileFilename;
protected GithubUrl withDevfileFilename(String devfileFilename) {
this.devfileFilename = devfileFilename;
return this;
}
@ -143,27 +143,12 @@ public class GithubUrl {
return this;
}
/**
* Provides the location to dockerfile
*
* @return location of dockerfile in a repository
*/
protected String dockerFileLocation() {
return new StringJoiner("/")
.add("https://raw.githubusercontent.com")
.add(username)
.add(repository)
.add(branch)
.add(dockerfileFilename)
.toString();
}
/**
* Provides the location to factory json file
*
* @return location of factory json file in a repository
*/
protected String factoryJsonFileLocation() {
protected String factoryFileLocation() {
return new StringJoiner("/")
.add("https://raw.githubusercontent.com")
.add(username)
@ -173,6 +158,21 @@ public class GithubUrl {
.toString();
}
/**
* Provides the location to devfile yaml file
*
* @return location of devfile yaml file in a repository
*/
protected String devfileFileLocation() {
return new StringJoiner("/")
.add("https://raw.githubusercontent.com")
.add(username)
.add(repository)
.add(branch)
.add(devfileFilename)
.toString();
}
/**
* Provides location to the repository part of the full github URL.
*

View File

@ -1,43 +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.plugin.github.factory.resolver;
import javax.inject.Inject;
import org.eclipse.che.plugin.urlfactory.URLChecker;
/**
* Support old dockerfila and factory filenames;
*
* @author Max Shaposhnik
*/
public class LegacyGithubURLParser extends GithubURLParserImpl {
private URLChecker urlChecker;
@Inject
public LegacyGithubURLParser(URLChecker urlChecker) {
this.urlChecker = urlChecker;
}
@Override
public GithubUrl parse(String url) {
GithubUrl githubUrl = super.parse(url);
if (!urlChecker.exists(githubUrl.dockerFileLocation())) {
githubUrl.withDockerfileFilename(".codenvy.dockerfile");
}
if (!urlChecker.exists(githubUrl.factoryJsonFileLocation())) {
githubUrl.withFactoryFilename(".codenvy.json");
}
return githubUrl;
}
}

View File

@ -12,8 +12,9 @@
package org.eclipse.che.plugin.github.factory.resolver;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.plugin.github.factory.resolver.GithubFactoryParametersResolver.URL_PARAMETER_NAME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -25,12 +26,13 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import org.eclipse.che.api.core.BadRequestException;
import java.util.Optional;
import java.util.function.Supplier;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto;
import org.eclipse.che.plugin.urlfactory.ProjectConfigDtoMerger;
import org.eclipse.che.plugin.urlfactory.URLFactoryBuilder;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
@ -49,7 +51,7 @@ import org.testng.annotations.Test;
public class GithubFactoryParametersResolverTest {
/** Parser which will allow to check validity of URLs and create objects. */
@Spy private GithubURLParserImpl githubUrlParser = new GithubURLParserImpl();
@Spy private GithubURLParser githubUrlParser = new GithubURLParser();
/** Converter allowing to convert github URL to other objects. */
@Spy
@ -61,18 +63,21 @@ public class GithubFactoryParametersResolverTest {
/** Parser which will allow to check validity of URLs and create objects. */
@Mock private URLFactoryBuilder urlFactoryBuilder;
/** Capturing the project config DTO parameter. */
@Captor private ArgumentCaptor<ProjectConfigDto> projectConfigDtoArgumentCaptor;
/** Capturing the project config DTO supplier parameter. */
@Captor private ArgumentCaptor<Supplier<ProjectConfigDto>> projectConfigDtoArgumentCaptor;
/** Capturing the parameter when calling {@link URLFactoryBuilder#createFactory(String)} */
@Captor private ArgumentCaptor<String> jsonFileLocationArgumentCaptor;
/**
* Capturing the parameter when calling {@link URLFactoryBuilder#createFactoryFromJson(String)} or
* {@link URLFactoryBuilder#createFactoryFromDevfile(String)}
*/
@Captor private ArgumentCaptor<String> fileLocationArgumentCaptor;
/** Instance of resolver that will be tested. */
@InjectMocks private GithubFactoryParametersResolver githubFactoryParametersResolver;
/** Check missing parameter name can't be accepted by this resolver */
@Test
public void checkMissingParameter() throws BadRequestException {
public void checkMissingParameter() {
Map<String, String> parameters = singletonMap("foo", "this is a foo bar");
boolean accept = githubFactoryParametersResolver.accept(parameters);
// shouldn't be accepted
@ -81,7 +86,7 @@ public class GithubFactoryParametersResolverTest {
/** Check url which is not a github url can't be accepted by this resolver */
@Test
public void checkInvalidAcceptUrl() throws BadRequestException {
public void checkInvalidAcceptUrl() {
Map<String, String> parameters = singletonMap(URL_PARAMETER_NAME, "http://www.eclipse.org/che");
boolean accept = githubFactoryParametersResolver.accept(parameters);
// shouldn't be accepted
@ -90,7 +95,7 @@ public class GithubFactoryParametersResolverTest {
/** Check github url will be be accepted by this resolver */
@Test
public void checkValidAcceptUrl() throws BadRequestException {
public void checkValidAcceptUrl() {
Map<String, String> parameters =
singletonMap(URL_PARAMETER_NAME, "https://github.com/codenvy/codenvy.git");
boolean accept = githubFactoryParametersResolver.accept(parameters);
@ -100,37 +105,66 @@ public class GithubFactoryParametersResolverTest {
/** Check that with a simple valid URL github url it works */
@Test
public void shouldReturnGitHubSimpleFactory() throws Exception {
public void shouldReturnGitHubSimpleJsonFactory() throws Exception {
String githubUrl = "https://github.com/eclipse/che";
FactoryDto computedFactory = newDto(FactoryDto.class).withV("4.0");
when(urlFactoryBuilder.createFactory(anyString())).thenReturn(computedFactory);
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromJson(anyString()))
.thenReturn(Optional.of(computedFactory));
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following codenvy json file
verify(urlFactoryBuilder).createFactory(jsonFileLocationArgumentCaptor.capture());
// check we called the builder with the following factory json file
verify(urlFactoryBuilder).createFactoryFromJson(fileLocationArgumentCaptor.capture());
assertEquals(
jsonFileLocationArgumentCaptor.getValue(),
fileLocationArgumentCaptor.getValue(),
"https://raw.githubusercontent.com/eclipse/che/master/.factory.json");
// check we provide dockerfile and correct env
verify(urlFactoryBuilder)
.buildWorkspaceConfig(
eq("che"),
eq("eclipse"),
eq("https://raw.githubusercontent.com/eclipse/che/master/.factory.dockerfile"));
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue();
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "git");
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 1);
assertEquals(sourceParameters.get("branch"), "master");
}
/** Check that with a simple valid URL github url it works */
@Test
public void shouldReturnGitHubDevfileFactory() throws Exception {
String githubUrl = "https://github.com/eclipse/che";
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromDevfile(anyString()))
.thenReturn(Optional.of(computedFactory));
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following devfile
verify(urlFactoryBuilder).createFactoryFromDevfile(fileLocationArgumentCaptor.capture());
assertEquals(
fileLocationArgumentCaptor.getValue(),
"https://raw.githubusercontent.com/eclipse/che/master/.devfile");
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 1);
@ -145,32 +179,29 @@ public class GithubFactoryParametersResolverTest {
String githubCloneUrl = "https://github.com/eclipse/che";
String githubBranch = "4.2.x";
FactoryDto computedFactory = newDto(FactoryDto.class).withV("4.0");
when(urlFactoryBuilder.createFactory(anyString())).thenReturn(computedFactory);
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromJson(anyString()))
.thenReturn(Optional.of(computedFactory));
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following codenvy json file
verify(urlFactoryBuilder).createFactory(jsonFileLocationArgumentCaptor.capture());
// check we called the builder with the following factory json file
verify(urlFactoryBuilder).createFactoryFromJson(fileLocationArgumentCaptor.capture());
assertEquals(
jsonFileLocationArgumentCaptor.getValue(),
fileLocationArgumentCaptor.getValue(),
"https://raw.githubusercontent.com/eclipse/che/4.2.x/.factory.json");
// check we provide dockerfile and correct env
verify(urlFactoryBuilder)
.buildWorkspaceConfig(
eq("che"),
eq("eclipse"),
eq("https://raw.githubusercontent.com/eclipse/che/4.2.x/.factory.dockerfile"));
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue();
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "git");
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubCloneUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 1);
@ -186,32 +217,29 @@ public class GithubFactoryParametersResolverTest {
String githubBranch = "4.2.x";
String githubKeepdir = "dashboard";
FactoryDto computedFactory = newDto(FactoryDto.class).withV("4.0");
when(urlFactoryBuilder.createFactory(anyString())).thenReturn(computedFactory);
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromJson(anyString()))
.thenReturn(Optional.of(computedFactory));
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following codenvy json file
verify(urlFactoryBuilder).createFactory(jsonFileLocationArgumentCaptor.capture());
// check we called the builder with the following factory json file
verify(urlFactoryBuilder).createFactoryFromJson(fileLocationArgumentCaptor.capture());
assertEquals(
jsonFileLocationArgumentCaptor.getValue(),
fileLocationArgumentCaptor.getValue(),
"https://raw.githubusercontent.com/eclipse/che/4.2.x/.factory.json");
// check we provide dockerfile and correct env
verify(urlFactoryBuilder)
.buildWorkspaceConfig(
eq("che"),
eq("eclipse"),
eq("https://raw.githubusercontent.com/eclipse/che/4.2.x/.factory.dockerfile"));
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue();
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "git");
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubCloneUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 2);

View File

@ -17,7 +17,7 @@ import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.eclipse.che.plugin.urlfactory.URLFetcher;
import org.eclipse.che.api.factory.server.urlfactory.URLFetcher;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
@ -37,7 +37,7 @@ public class GithubURLParserTest {
@Mock private URLFetcher urlFetcher;
/** Instance of component that will be tested. */
@InjectMocks private GithubURLParserImpl githubUrlParser;
@InjectMocks private GithubURLParser githubUrlParser;
/** Check invalid url (not a github one) */
@Test(expectedExceptions = IllegalArgumentException.class)
@ -113,17 +113,18 @@ public class GithubURLParserTest {
"<div class=\"TableObject gh-header-meta\">\n"
+ " <div class=\"TableObject-item\">\n"
+ " <div class=\"State State--green\">\n"
+ " <svg class=\"octicon octicon-git-pull-request\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z\"/></svg>\n"
+ " Open\n"
+ " </div>\n"
+ " </div>"
+ " <svg class=\"octicon octicon-git-pull-request\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z\"/></svg>\n"
+ " Open\n"
+ " </div>\n"
+ "\n"
+ " </div>\n"
+ " <div class=\"TableObject-item TableObject-item--primary\">\n"
+ " <a class=\"author pull-header-username css-truncate css-truncate-target expandable\" data-hovercard-user-id=\"436777\" data-octo-click=\"hovercard-link-click\" data-octo-dimensions=\"link_type:self\" href=\"/benoitf\">benoitf</a>\n"
+ " merged 1 commit into\n"
+ " <a class=\"author pull-header-username css-truncate css-truncate-target expandable\" data-hovercard-type=\"user\" data-hovercard-url=\"/hovercards?user_id=1651062\" data-octo-click=\"hovercard-link-click\" data-octo-dimensions=\"link_type:self\" href=\"/mshaposhnik\">mshaposhnik</a>\n"
+ " wants to merge 1 commit into\n"
+ "\n"
+ "\n"
+ "\n"
+ " <span title=\"eclipse/che:master\" class=\"commit-ref css-truncate user-select-contain expandable \"><span class=\"css-truncate-target\">master</span></span>\n"
+ " <span title=\"eclipse/che:cleanup-e2e-theia\" class=\"commit-ref css-truncate user-select-contain expandable \"><span class=\"css-truncate-target\">master</span></span>\n"
+ "\n"
+ "from\n"
+ "\n"

View File

@ -29,7 +29,7 @@ import org.testng.annotations.Test;
public class GithubUrlTest {
/** Parser used to create the url. */
@InjectMocks private GithubURLParserImpl githubUrlParser;
@InjectMocks private GithubURLParser githubUrlParser;
/** Instance of the url created */
private GithubUrl githubUrl;
@ -41,19 +41,19 @@ public class GithubUrlTest {
assertNotNull(this.githubUrl);
}
/** Check when there is .codenvy.dockerfile in the repository */
/** Check when there is .devfile in the repository */
@Test
public void checkDockerfileLocation() {
public void checkDevfileLocation() {
assertEquals(
githubUrl.dockerFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/master/.factory.dockerfile");
githubUrl.devfileFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/master/.devfile");
}
/** Check when there is .codenvy.json file in the repository */
/** Check when there is .factory.json file in the repository */
@Test
public void checkCodenvyFactoryJsonFileLocation() {
public void checkFactoryJsonFileLocation() {
assertEquals(
githubUrl.factoryJsonFileLocation(),
githubUrl.factoryFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/master/.factory.json");
}

View File

@ -1,147 +0,0 @@
<?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-plugin-parent</artifactId>
<groupId>org.eclipse.che.plugin</groupId>
<version>6.17.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>che-plugin-url-factory</artifactId>
<packaging>jar</packaging>
<name>Che Plugin :: URL Factory</name>
<properties>
<findbugs.failonerror>true</findbugs.failonerror>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</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-factory-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-workspace-shared</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</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.jetty</groupId>
<artifactId>jetty-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</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.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</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.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,129 +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.plugin.urlfactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
import org.eclipse.che.api.workspace.shared.dto.MachineConfigDto;
import org.eclipse.che.api.workspace.shared.dto.RecipeDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.dto.server.DtoFactory;
/**
* Handle the creation of some elements used inside a {@link FactoryDto}.
*
* @author Florent Benoit
*/
@Singleton
public class URLFactoryBuilder {
/** Default docker image (if repository has no dockerfile) */
protected static final String DEFAULT_DOCKER_IMAGE = "eclipse/ubuntu_jdk8";
/** Default docker type (if repository has no dockerfile) */
protected static final String DEFAULT_MEMORY_LIMIT_BYTES = Long.toString(2000L * 1024L * 1024L);
protected static final String MACHINE_NAME = "ws-machine";
private final URLChecker urlChecker;
private final URLFetcher urlFetcher;
@Inject
public URLFactoryBuilder(URLChecker urlChecker, URLFetcher urlFetcher) {
this.urlChecker = urlChecker;
this.urlFetcher = urlFetcher;
}
/**
* Build a default factory using the provided json file or create default one
*
* @param jsonFileLocation location of factory json file
* @return a factory
*/
public FactoryDto createFactory(String jsonFileLocation) {
// Check if there is factory json file inside the repository
if (jsonFileLocation != null) {
String factoryJsonContent = urlFetcher.fetch(jsonFileLocation);
if (!Strings.isNullOrEmpty(factoryJsonContent)) {
// Adapt an old factory format to a new one if necessary
try {
final ByteArrayInputStream contentStream =
new ByteArrayInputStream(factoryJsonContent.getBytes(UTF_8));
factoryJsonContent = CharStreams.toString(new InputStreamReader(contentStream, UTF_8));
} catch (IOException x) {
throw new IllegalStateException(x.getLocalizedMessage(), x);
}
return DtoFactory.getInstance().createDtoFromJson(factoryJsonContent, FactoryDto.class);
}
}
// else return a default factory
return newDto(FactoryDto.class).withV("4.0");
}
/**
* Help to generate default workspace configuration
*
* @param environmentName the name of the environment to create
* @param name the name of the workspace
* @param dockerFileLocation the optional location for codenvy dockerfile to use
* @return a workspace configuration
*/
public WorkspaceConfigDto buildWorkspaceConfig(
String environmentName, String name, String dockerFileLocation) {
// if remote repository contains a codenvy docker file, use it
// else use the default image.
RecipeDto recipeDto;
if (dockerFileLocation != null && urlChecker.exists(dockerFileLocation)) {
recipeDto =
newDto(RecipeDto.class)
.withLocation(dockerFileLocation)
.withType("dockerfile")
.withContentType("text/x-dockerfile");
} else {
recipeDto = newDto(RecipeDto.class).withContent(DEFAULT_DOCKER_IMAGE).withType("dockerimage");
}
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(
ImmutableList.of(
"org.eclipse.che.ws-agent", "org.eclipse.che.exec", "org.eclipse.che.terminal"))
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, DEFAULT_MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =
newDto(EnvironmentDto.class)
.withRecipe(recipeDto)
.withMachines(singletonMap(MACHINE_NAME, machine));
// workspace configuration using the environment
return newDto(WorkspaceConfigDto.class)
.withDefaultEnv(environmentName)
.withEnvironments(singletonMap(environmentName, environmentDto))
.withName(name);
}
}

View File

@ -1,189 +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.plugin.urlfactory;
import static java.lang.Boolean.FALSE;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.plugin.urlfactory.URLFactoryBuilder.DEFAULT_DOCKER_IMAGE;
import static org.eclipse.che.plugin.urlfactory.URLFactoryBuilder.DEFAULT_MEMORY_LIMIT_BYTES;
import static org.eclipse.che.plugin.urlfactory.URLFactoryBuilder.MACHINE_NAME;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import com.google.common.collect.ImmutableList;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
import org.eclipse.che.api.workspace.shared.dto.MachineConfigDto;
import org.eclipse.che.api.workspace.shared.dto.RecipeDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.dto.server.DtoFactory;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Testing {@link URLFactoryBuilder}
*
* @author Florent Benoit
*/
@Listeners(MockitoTestNGListener.class)
public class URLFactoryBuilderTest {
/** Check if URL is existing or not */
@Mock private URLChecker urlChecker;
/** Grab content of URLs */
@Mock private URLFetcher urlFetcher;
/** Tested instance. */
@InjectMocks private URLFactoryBuilder urlFactoryBuilder;
/** Check if not specifying a custom docker file we have the default value */
@Test
public void checkDefaultImage() throws Exception {
RecipeDto recipeDto =
newDto(RecipeDto.class).withContent(DEFAULT_DOCKER_IMAGE).withType("dockerimage");
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(
ImmutableList.of(
"org.eclipse.che.ws-agent", "org.eclipse.che.exec", "org.eclipse.che.terminal"))
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, DEFAULT_MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =
newDto(EnvironmentDto.class)
.withRecipe(recipeDto)
.withMachines(singletonMap(MACHINE_NAME, machine));
// setup environment
WorkspaceConfigDto expectedWsConfig =
newDto(WorkspaceConfigDto.class)
.withDefaultEnv("foo")
.withEnvironments(singletonMap("foo", environmentDto))
.withName("dumm");
WorkspaceConfigDto actualWsConfigDto =
urlFactoryBuilder.buildWorkspaceConfig("foo", "dumm", null);
assertEquals(actualWsConfigDto, expectedWsConfig);
}
/**
* Check that by specifying a location of custom dockerfile it's stored in the machine source if
* URL is accessible
*/
@Test
public void checkWithCustomDockerfile() throws Exception {
String myLocation = "http://foo-location";
RecipeDto recipeDto =
newDto(RecipeDto.class)
.withLocation(myLocation)
.withType("dockerfile")
.withContentType("text/x-dockerfile");
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(
ImmutableList.of(
"org.eclipse.che.ws-agent", "org.eclipse.che.exec", "org.eclipse.che.terminal"))
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, DEFAULT_MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =
newDto(EnvironmentDto.class)
.withRecipe(recipeDto)
.withMachines(singletonMap(MACHINE_NAME, machine));
WorkspaceConfigDto expectedWsConfig =
newDto(WorkspaceConfigDto.class)
.withDefaultEnv("foo")
.withEnvironments(singletonMap("foo", environmentDto))
.withName("dumm");
when(urlChecker.exists(myLocation)).thenReturn(true);
WorkspaceConfigDto actualWsConfigDto =
urlFactoryBuilder.buildWorkspaceConfig("foo", "dumm", myLocation);
assertEquals(actualWsConfigDto, expectedWsConfig);
}
/**
* Check that by specifying a location of custom dockerfile it's stored in the machine source if
* URL is accessible
*/
@Test
public void checkWithNonAccessibleCustomDockerfile() throws Exception {
String myLocation = "http://foo-location";
RecipeDto recipeDto =
newDto(RecipeDto.class).withContent(DEFAULT_DOCKER_IMAGE).withType("dockerimage");
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(
ImmutableList.of(
"org.eclipse.che.ws-agent", "org.eclipse.che.exec", "org.eclipse.che.terminal"))
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, DEFAULT_MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =
newDto(EnvironmentDto.class)
.withRecipe(recipeDto)
.withMachines(singletonMap(MACHINE_NAME, machine));
WorkspaceConfigDto expectedWsConfig =
newDto(WorkspaceConfigDto.class)
.withDefaultEnv("foo")
.withEnvironments(singletonMap("foo", environmentDto))
.withName("dumm");
when(urlChecker.exists(myLocation)).thenReturn(false);
WorkspaceConfigDto actualWsConfigDto =
urlFactoryBuilder.buildWorkspaceConfig("foo", "dumm", myLocation);
assertEquals(actualWsConfigDto, expectedWsConfig);
}
/** Check that with a custom factory.json we've this factory being built */
@Test
public void checkWithCustomFactoryJsonFile() throws Exception {
WorkspaceConfigDto workspaceConfigDto = newDto(WorkspaceConfigDto.class);
FactoryDto templateFactory =
newDto(FactoryDto.class).withV("4.0").withName("florent").withWorkspace(workspaceConfigDto);
String jsonFactory = DtoFactory.getInstance().toJson(templateFactory);
String myLocation = "http://foo-location";
when(urlChecker.exists(myLocation)).thenReturn(FALSE);
when(urlFetcher.fetch(myLocation)).thenReturn(jsonFactory);
FactoryDto factory = urlFactoryBuilder.createFactory(myLocation);
assertEquals(templateFactory, factory);
}
/** Check that without specifying a custom factory.json we've default factory */
@Test
public void checkWithDefaultFactoryJsonFile() throws Exception {
FactoryDto factory = urlFactoryBuilder.createFactory(null);
assertNull(factory.getWorkspace());
assertEquals(factory.getV(), "4.0");
}
}

View File

@ -1,28 +0,0 @@
<?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>
<logger name="org.eclipse.jetty" level="INFO" />
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</configuration>

View File

@ -48,7 +48,6 @@
<module>plugin-nodejs-debugger</module>
<module>plugin-php</module>
<module>plugin-languageserver</module>
<module>plugin-urlfactory</module>
<module>plugin-json</module>
<module>plugin-composer</module>
<module>plugin-zend-debugger</module>

View File

@ -1836,11 +1836,6 @@
<artifactId>che-plugin-testing-testng-server</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-url-factory</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-web-ext-server</artifactId>

View File

@ -0,0 +1,148 @@
/*
* 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.Collections.emptyMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.annotations.Beta;
import javax.inject.Inject;
import javax.inject.Singleton;
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.ValidationException;
import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.server.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.devfile.server.validator.DevfileSchemaValidator;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.commons.env.EnvironmentContext;
/**
* Facade for devfile related operations.
*
* @author Max Shaposhnyk
*/
@Beta
@Singleton
public class DevfileManager {
private final ObjectMapper objectMapper;
private DevfileSchemaValidator schemaValidator;
private DevfileIntegrityValidator integrityValidator;
private DevfileConverter devfileConverter;
private WorkspaceManager workspaceManager;
@Inject
public DevfileManager(
DevfileSchemaValidator schemaValidator,
DevfileIntegrityValidator integrityValidator,
DevfileConverter devfileConverter,
WorkspaceManager workspaceManager) {
this.schemaValidator = schemaValidator;
this.integrityValidator = integrityValidator;
this.devfileConverter = devfileConverter;
this.workspaceManager = workspaceManager;
this.objectMapper = new ObjectMapper(new YAMLFactory());
}
/**
* Creates {@link Devfile} from given devfile content. Performs schema and integrity validation of
* input data.
*
* @param devfileContent raw content of devfile
* @param verbose when true, method returns more explained validation error messages if any
* @return Devfile object created from the source content
* @throws DevfileFormatException when any of schema or integrity validations fail
* @throws JsonProcessingException when parsing error occurs
*/
public Devfile parse(String devfileContent, boolean verbose)
throws DevfileFormatException, JsonProcessingException {
JsonNode parsed = schemaValidator.validateBySchema(devfileContent, verbose);
Devfile devfile = objectMapper.treeToValue(parsed, Devfile.class);
integrityValidator.validateDevfile(devfile);
return devfile;
}
/**
* Creates {@link WorkspaceImpl} from given devfile with available name search
*
* @param devfile source devfile
* @return created {@link WorkspaceImpl} instance
* @throws DevfileFormatException when devfile integrity validation fail
* @throws ValidationException when incoming configuration or attributes are not valid
* @throws ConflictException when any conflict occurs
* @throws NotFoundException when user account is not found
* @throws ServerException when other error occurs
*/
public WorkspaceImpl createWorkspace(Devfile devfile)
throws ServerException, DevfileFormatException, ConflictException, NotFoundException,
ValidationException {
WorkspaceConfigImpl workspaceConfig = createWorkspaceConfig(devfile);
final String namespace = EnvironmentContext.getCurrent().getSubject().getUserName();
return workspaceManager.createWorkspace(
findAvailableName(workspaceConfig), namespace, emptyMap());
}
/**
* Creates {@link WorkspaceConfigImpl} from given devfile with integrity validation
*
* @param devfile source devfile
* @return created {@link WorkspaceConfigImpl} instance
* @throws DevfileFormatException when devfile integrity validation fail
*/
public WorkspaceConfigImpl createWorkspaceConfig(Devfile devfile) throws DevfileFormatException {
integrityValidator.validateDevfile(devfile);
return devfileConverter.devFileToWorkspaceConfig(devfile);
}
/**
* Exports provided workspace into devfile
*
* @param key string composite workspace key
* @see WorkspaceManager#getByKey(String)
* @return devfile representation of given workspace
* @throws NotFoundException when no workspace can be found by given key
* @throws ConflictException when workspace cannot be exported into devfile
* @throws ServerException when other error occurs
*/
public Devfile exportWorkspace(String key)
throws NotFoundException, ServerException, ConflictException {
WorkspaceImpl workspace = workspaceManager.getWorkspace(key);
try {
return devfileConverter.workspaceToDevFile(workspace.getConfig());
} catch (WorkspaceExportException e) {
throw new ConflictException(e.getMessage());
}
}
private WorkspaceConfigImpl findAvailableName(WorkspaceConfigImpl config) throws ServerException {
String nameCandidate = config.getName();
String namespace = EnvironmentContext.getCurrent().getSubject().getUserName();
int counter = 0;
while (true) {
try {
workspaceManager.getWorkspace(nameCandidate, namespace);
nameCandidate = config.getName() + "_" + ++counter;
} catch (NotFoundException nf) {
config.setName(nameCandidate);
break;
}
}
return config;
}
}

View File

@ -11,13 +11,11 @@
*/
package org.eclipse.che.api.devfile.server;
import static java.util.Collections.emptyMap;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.api.workspace.server.DtoConverter.asDto;
import static org.eclipse.che.api.workspace.server.WorkspaceKeyValidator.validateKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.swagger.annotations.Api;
@ -45,39 +43,29 @@ import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.server.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.WorkspaceLinksGenerator;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.commons.env.EnvironmentContext;
@Api(value = "/devfile", description = "Devfile REST API")
@Path("/devfile")
public class DevfileService extends Service {
private WorkspaceLinksGenerator linksGenerator;
private DevfileSchemaValidator schemaValidator;
private DevfileIntegrityValidator integrityValidator;
private DevfileSchemaProvider schemaCachedProvider;
private WorkspaceManager workspaceManager;
private ObjectMapper objectMapper;
private DevfileConverter devfileConverter;
private DevfileManager devfileManager;
@Inject
public DevfileService(
WorkspaceLinksGenerator linksGenerator,
DevfileSchemaValidator schemaValidator,
DevfileIntegrityValidator integrityValidator,
DevfileSchemaProvider schemaCachedProvider,
WorkspaceManager workspaceManager) {
DevfileManager devfileManager) {
this.linksGenerator = linksGenerator;
this.schemaValidator = schemaValidator;
this.integrityValidator = integrityValidator;
this.schemaCachedProvider = schemaCachedProvider;
this.workspaceManager = workspaceManager;
this.devfileManager = devfileManager;
this.objectMapper = new ObjectMapper(new YAMLFactory());
this.devfileConverter = new DevfileConverter();
}
/**
@ -134,22 +122,15 @@ public class DevfileService extends Service {
throws ServerException, ConflictException, NotFoundException, ValidationException,
BadRequestException {
Devfile devFile;
WorkspaceConfigImpl workspaceConfig;
WorkspaceImpl workspace;
try {
JsonNode parsed = schemaValidator.validateBySchema(data, verbose);
devFile = objectMapper.treeToValue(parsed, Devfile.class);
integrityValidator.validateDevfile(devFile);
workspaceConfig = devfileConverter.devFileToWorkspaceConfig(devFile);
} catch (IOException e) {
throw new ServerException(e.getMessage());
Devfile devfile = devfileManager.parse(data, verbose);
workspace = devfileManager.createWorkspace(devfile);
} catch (DevfileFormatException e) {
throw new BadRequestException(e.getMessage());
} catch (JsonProcessingException e) {
throw new ServerException(e.getMessage());
}
final String namespace = EnvironmentContext.getCurrent().getSubject().getUserName();
WorkspaceImpl workspace =
workspaceManager.createWorkspace(findAvailableName(workspaceConfig), namespace, emptyMap());
return Response.status(201)
.entity(asDto(workspace).withLinks(linksGenerator.genLinks(workspace, getServiceContext())))
.build();
@ -158,8 +139,6 @@ public class DevfileService extends Service {
/**
* Generates the devfile based on an existing workspace. Key is workspace id or
* namespace/workspace_name
*
* @see WorkspaceManager#getByKey(String)
*/
@GET
@Path("/{key:.*}")
@ -185,33 +164,11 @@ public class DevfileService extends Service {
}))
@PathParam("key")
String key)
throws NotFoundException, ServerException, BadRequestException, ConflictException {
throws NotFoundException, ServerException, BadRequestException, ConflictException,
JsonProcessingException {
validateKey(key);
WorkspaceImpl workspace = workspaceManager.getWorkspace(key);
try {
Devfile workspaceDevFile = devfileConverter.workspaceToDevFile(workspace.getConfig());
// Write object as YAML
return Response.ok().entity(objectMapper.writeValueAsString(workspaceDevFile)).build();
} catch (JsonProcessingException e) {
throw new ServerException(e.getMessage(), e);
} catch (WorkspaceExportException e) {
throw new ConflictException(e.getMessage());
}
}
private WorkspaceConfigImpl findAvailableName(WorkspaceConfigImpl config) throws ServerException {
String nameCandidate = config.getName();
String namespace = EnvironmentContext.getCurrent().getSubject().getUserName();
int counter = 0;
while (true) {
try {
workspaceManager.getWorkspace(nameCandidate, namespace);
nameCandidate = config.getName() + "_" + ++counter;
} catch (NotFoundException nf) {
config.setName(nameCandidate);
break;
}
}
return config;
return Response.ok()
.entity(objectMapper.writeValueAsString(devfileManager.exportWorkspace(key)))
.build();
}
}

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
package org.eclipse.che.api.devfile.server.schema;
import static org.eclipse.che.api.devfile.server.Constants.SCHEMA_LOCATION;
import static org.eclipse.che.commons.lang.IoUtil.getResource;

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
package org.eclipse.che.api.devfile.server.validator;
import static java.lang.String.format;
import static java.util.stream.Collectors.toSet;
@ -25,6 +25,7 @@ import org.eclipse.che.api.devfile.model.Command;
import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.model.Project;
import org.eclipse.che.api.devfile.model.Tool;
import org.eclipse.che.api.devfile.server.DevfileFormatException;
/** Validates devfile logical integrity. */
@Singleton

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
package org.eclipse.che.api.devfile.server.validator;
import static java.lang.String.format;
@ -26,6 +26,8 @@ import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.devfile.server.DevfileFormatException;
import org.eclipse.che.api.devfile.server.schema.DevfileSchemaProvider;
/** Validates YAML devfile content against given JSON schema. */
@Singleton
@ -36,13 +38,14 @@ public class DevfileSchemaValidator {
private DevfileSchemaProvider schemaProvider;
@Inject
DevfileSchemaValidator(DevfileSchemaProvider schemaProvider) {
public DevfileSchemaValidator(DevfileSchemaProvider schemaProvider) {
this.schemaProvider = schemaProvider;
this.validator = JsonSchemaFactory.byDefault().getValidator();
this.yamlReader = new ObjectMapper(new YAMLFactory());
}
JsonNode validateBySchema(String yamlContent, boolean verbose) throws DevfileFormatException {
public JsonNode validateBySchema(String yamlContent, boolean verbose)
throws DevfileFormatException {
ProcessingReport report;
JsonNode data;
try {

View File

@ -12,7 +12,6 @@
"required": [
"specVersion",
"name",
"projects",
"tools",
"commands"
],

View File

@ -0,0 +1,136 @@
/*
* 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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.server.schema.DevfileSchemaProvider;
import org.eclipse.che.api.devfile.server.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.devfile.server.validator.DevfileSchemaValidator;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.json.JsonHelper;
import org.eclipse.che.commons.json.JsonParseException;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.testng.reporters.Files;
@Listeners(MockitoTestNGListener.class)
public class DevfileManagerTest {
private DevfileSchemaValidator schemaValidator;
private DevfileIntegrityValidator integrityValidator;
private DevfileConverter devfileConverter;
@Mock private WorkspaceManager workspaceManager;
private static final Subject TEST_SUBJECT = new SubjectImpl("name", "id", "token", false);
private DevfileManager devfileManager;
@BeforeMethod
public void setUp() throws Exception {
schemaValidator = spy(new DevfileSchemaValidator(new DevfileSchemaProvider()));
integrityValidator = spy(new DevfileIntegrityValidator());
devfileConverter = spy(new DevfileConverter());
devfileManager =
new DevfileManager(schemaValidator, integrityValidator, devfileConverter, workspaceManager);
}
@Test
public void testValidateAndConvert() throws Exception {
String yamlContent =
Files.readFile(getClass().getClassLoader().getResourceAsStream("devfile.yaml"));
devfileManager.parse(yamlContent, true);
verify(schemaValidator).validateBySchema(eq(yamlContent), eq(true));
verify(integrityValidator).validateDevfile(any(Devfile.class));
}
@Test(
expectedExceptions = JsonProcessingException.class,
expectedExceptionsMessageRegExp = "Unrecognized field \"foos\" [\\w\\W]+")
public void shouldThrowExceptionWhenUnconvertableContentProvided() throws Exception {
String yamlContent =
Files.readFile(getClass().getClassLoader().getResourceAsStream("devfile.yaml"))
.concat("foos:");
devfileManager.parse(yamlContent, true);
verify(schemaValidator).validateBySchema(eq(yamlContent), eq(true));
verifyNoMoreInteractions(integrityValidator);
}
@Test
public void shouldFindAvailableNameAndCreateWorkspace() throws Exception {
ArgumentCaptor<WorkspaceConfigImpl> captor = ArgumentCaptor.forClass(WorkspaceConfigImpl.class);
EnvironmentContext current = new EnvironmentContext();
current.setSubject(TEST_SUBJECT);
EnvironmentContext.setCurrent(current);
WorkspaceImpl ws = mock(WorkspaceImpl.class);
when(workspaceManager.createWorkspace(any(), anyString(), anyMap()))
.thenReturn(createWorkspace(WorkspaceStatus.STOPPED));
when(workspaceManager.getWorkspace(anyString(), anyString()))
.thenAnswer(
invocation -> {
String wsname = invocation.getArgument(0);
if ("petclinic-dev-environment".equals(wsname)
|| "petclinic-dev-environment_1".equals(wsname)) {
return ws;
}
throw new NotFoundException("ws not found");
});
String yamlContent =
Files.readFile(getClass().getClassLoader().getResourceAsStream("devfile.yaml"));
Devfile devfile = devfileManager.parse(yamlContent, true);
// when
devfileManager.createWorkspace(devfile);
// then
verify(workspaceManager).createWorkspace(captor.capture(), anyString(), anyMap());
assertEquals("petclinic-dev-environment_2", captor.getValue().getName());
}
private WorkspaceImpl createWorkspace(WorkspaceStatus status)
throws IOException, JsonParseException {
return WorkspaceImpl.builder()
.setConfig(createConfig())
.generateId()
.setAccount(new AccountImpl("anyId", TEST_SUBJECT.getUserName(), "test"))
.setStatus(status)
.build();
}
private WorkspaceConfigImpl createConfig() throws IOException, JsonParseException {
String jsonContent =
Files.readFile(getClass().getClassLoader().getResourceAsStream("workspace_config.json"));
return JsonHelper.fromJson(jsonContent, WorkspaceConfigImpl.class, null);
}
}

View File

@ -16,39 +16,31 @@ 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.anyMap;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.response.Response;
import java.io.IOException;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.server.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.WorkspaceLinksGenerator;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.json.JsonHelper;
import org.eclipse.che.commons.json.JsonParseException;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
@ -62,14 +54,9 @@ public class DevfileServiceTest {
@Mock private WorkspaceLinksGenerator linksGenerator;
@Mock private WorkspaceManager workspaceManager;
@Mock private EnvironmentContext environmentContext;
@Mock private DevfileIntegrityValidator integrityValidator;
@Mock private DevfileManager devfileManager;
private DevfileSchemaProvider schemaProvider = new DevfileSchemaProvider();
private DevfileSchemaValidator validator;
@SuppressWarnings("unused")
private static final EnvironmentFilter FILTER = new EnvironmentFilter();
private ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
private static final Subject SUBJECT = new SubjectImpl("user", "user123", "token", false);
@ -78,10 +65,7 @@ public class DevfileServiceTest {
@BeforeMethod
public void initService() {
this.validator = spy(new DevfileSchemaValidator(schemaProvider));
this.devFileService =
new DevfileService(
linksGenerator, validator, integrityValidator, schemaProvider, workspaceManager);
this.devFileService = new DevfileService(linksGenerator, schemaProvider, devfileManager);
}
@Test
@ -98,25 +82,14 @@ public class DevfileServiceTest {
}
@Test
public void shouldAcceptDevFileAndFindAvailableName() throws Exception {
ArgumentCaptor<WorkspaceConfigImpl> captor = ArgumentCaptor.forClass(WorkspaceConfigImpl.class);
EnvironmentContext.setCurrent(environmentContext);
WorkspaceImpl ws = mock(WorkspaceImpl.class);
when(workspaceManager.createWorkspace(any(), eq(SUBJECT.getUserName()), anyMap()))
.thenReturn(createWorkspace(WorkspaceStatus.STOPPED));
public void shouldAcceptDevFileContentAndCreateWorkspace() throws Exception {
ArgumentCaptor<Devfile> captor = ArgumentCaptor.forClass(Devfile.class);
String yamlContent =
Files.readFile(getClass().getClassLoader().getResourceAsStream("devfile.yaml"));
when(workspaceManager.getWorkspace(anyString(), anyString()))
.thenAnswer(
invocation -> {
String wsname = invocation.getArgument(0);
if (wsname.equals("petclinic-dev-environment")
|| wsname.equals("petclinic-dev-environment_1")) {
return ws;
}
throw new NotFoundException("ws not found");
});
Devfile devfile = createDevfile(yamlContent);
WorkspaceImpl ws = createWorkspace(WorkspaceStatus.STOPPED);
when(devfileManager.parse(anyString(), anyBoolean())).thenReturn(devfile);
when(devfileManager.createWorkspace(any(Devfile.class))).thenReturn(ws);
final Response response =
given()
.auth()
@ -127,16 +100,19 @@ public class DevfileServiceTest {
.post(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 201);
verify(validator).validateBySchema(eq(yamlContent), eq(false));
verify(workspaceManager).createWorkspace(captor.capture(), eq(SUBJECT.getUserName()), anyMap());
assertEquals("petclinic-dev-environment_2", captor.getValue().getName());
verify(devfileManager).createWorkspace(captor.capture());
assertEquals(devfile, captor.getValue());
}
private Devfile createDevfile(String yamlContent) throws IOException {
JsonNode node = mapper.readTree(yamlContent);
return mapper.treeToValue(node, Devfile.class);
}
@Test
public void shouldCreateDevFileFromWorkspace() throws Exception {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
when(workspaceManager.getWorkspace(anyString()))
.thenReturn(createWorkspace(WorkspaceStatus.STOPPED));
when(devfileManager.exportWorkspace(anyString())).thenReturn(new Devfile());
final Response response =
given()
@ -160,17 +136,9 @@ public class DevfileServiceTest {
.build();
}
private WorkspaceConfig createConfig() throws IOException, JsonParseException {
private WorkspaceConfigImpl createConfig() throws IOException, JsonParseException {
String jsonContent =
Files.readFile(getClass().getClassLoader().getResourceAsStream("workspace_config.json"));
return JsonHelper.fromJson(jsonContent, WorkspaceConfigImpl.class, null);
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
@Override
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(SUBJECT);
}
}
}

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
package org.eclipse.che.api.devfile.server.validator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
@ -21,6 +21,7 @@ import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.model.Project;
import org.eclipse.che.api.devfile.model.Source;
import org.eclipse.che.api.devfile.model.Tool;
import org.eclipse.che.api.devfile.server.DevfileFormatException;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.reporters.Files;

View File

@ -9,8 +9,10 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
package org.eclipse.che.api.devfile.server.validator;
import org.eclipse.che.api.devfile.server.DevfileFormatException;
import org.eclipse.che.api.devfile.server.schema.DevfileSchemaProvider;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.reporters.Files;

View File

@ -18,18 +18,16 @@ package org.eclipse.che.api.factory.shared;
*/
public final class Constants {
public static final String CURRENT_VERSION = "4.0";
// factory links rel attributes
public static final String IMAGE_REL_ATT = "image";
public static final String RETRIEVE_FACTORY_REL_ATT = "self";
public static final String SNIPPET_REL_ATT = "snippet";
public static final String FACTORY_ACCEPTANCE_REL_ATT = "accept";
public static final String NAMED_FACTORY_ACCEPTANCE_REL_ATT = "accept-named";
// factory snippet types
public static final String MARKDOWN_SNIPPET_TYPE = "markdown";
public static final String IFRAME_SNIPPET_TYPE = "iframe";
public static final String HTML_SNIPPET_TYPE = "html";
public static final String URL_SNIPPET_TYPE = "url";
// url factory parameter names
public static final String URL_PARAMETER_NAME = "url";
private Constants() {}
}

View File

@ -54,6 +54,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-dto</artifactId>
@ -150,6 +154,21 @@
<artifactId>che-core-sql-schema</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId>

View File

@ -0,0 +1,58 @@
/*
* 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.factory.server;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
/**
* Default {@link FactoryParametersResolver} implementation. Tries to resolve factory based on
* provided parameters. Presumes url parameters as direct URL to a devfile content.
*/
@Singleton
public class DefaultFactoryParameterResolver implements FactoryParametersResolver {
private URLFactoryBuilder urlFactoryBuilder;
@Inject
public DefaultFactoryParameterResolver(URLFactoryBuilder urlFactoryBuilder) {
this.urlFactoryBuilder = urlFactoryBuilder;
}
@Override
public boolean accept(Map<String, String> factoryParameters) {
return !factoryParameters.get(URL_PARAMETER_NAME).isEmpty();
}
/**
* Creates factory based on provided parameters. Presumes url parameter as direct URL to a devfile
* content.
*
* @param factoryParameters map containing factory data parameters provided through URL
*/
@Override
public FactoryDto createFactory(@NotNull final Map<String, String> factoryParameters)
throws BadRequestException, ServerException {
// create factory from the following devfile location
return urlFactoryBuilder
.createFactoryFromDevfile(factoryParameters.get(URL_PARAMETER_NAME))
.orElse(null);
}
}

View File

@ -19,6 +19,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
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.factory.Factory;
import org.eclipse.che.api.factory.server.model.impl.AuthorImpl;
@ -118,9 +119,8 @@ public class FactoryManager {
* @return stored data, if specified attributes is correct
* @throws ServerException when any server errors occurs
*/
@SuppressWarnings("unchecked")
public <T extends List<? extends Factory>> T getByAttribute(
public Page<? extends Factory> getByAttribute(
int maxItems, int skipCount, List<Pair<String, String>> attributes) throws ServerException {
return (T) factoryDao.getByAttribute(maxItems, skipCount, attributes);
return factoryDao.getByAttributes(maxItems, skipCount, attributes);
}
}

View File

@ -14,6 +14,7 @@ package org.eclipse.che.api.factory.server;
import java.util.Map;
import javax.validation.constraints.NotNull;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
/**
@ -39,5 +40,5 @@ public interface FactoryParametersResolver {
* @throws BadRequestException when data are invalid
*/
FactoryDto createFactory(@NotNull Map<String, String> factoryParameters)
throws BadRequestException;
throws BadRequestException, ServerException;
}

View File

@ -27,7 +27,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -51,6 +50,7 @@ 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.factory.Factory;
import org.eclipse.che.api.core.model.user.User;
@ -80,15 +80,12 @@ import org.eclipse.che.dto.server.DtoFactory;
public class FactoryService extends Service {
/** Error message if there is no plugged resolver. */
public static final String ERROR_NO_RESOLVER_AVAILABLE =
"Cannot build factory with any of the provided parameters.";
public static final String FACTORY_NOT_RESOLVABLE =
"Cannot build factory with any of the provided parameters. Please check parameters correctness, and resend query.";
/** Validate query parameter. If true, factory will be validated */
public static final String VALIDATE_QUERY_PARAMETER = "validate";
/** Set of resolvers for factories. Injected through an holder. */
private final Set<FactoryParametersResolver> factoryParametersResolvers;
private final FactoryManager factoryManager;
private final UserManager userManager;
private final PreferenceManager preferenceManager;
@ -97,6 +94,7 @@ public class FactoryService extends Service {
private final FactoryAcceptValidator acceptValidator;
private final FactoryBuilder factoryBuilder;
private final WorkspaceManager workspaceManager;
private final FactoryParametersResolverHolder factoryParametersResolverHolder;
@Inject
public FactoryService(
@ -117,8 +115,7 @@ public class FactoryService extends Service {
this.editValidator = editValidator;
this.factoryBuilder = factoryBuilder;
this.workspaceManager = workspaceManager;
this.factoryParametersResolvers =
factoryParametersResolverHolder.getFactoryParametersResolvers();
this.factoryParametersResolverHolder = factoryParametersResolverHolder;
}
@POST
@ -193,7 +190,7 @@ public class FactoryService extends Service {
@ApiResponse(code = 500, message = "Internal server error")
})
@Deprecated
public List<FactoryDto> getFactoryByAttribute(
public Response getFactoryByAttribute(
@DefaultValue("0") @QueryParam("skipCount") Integer skipCount,
@DefaultValue("30") @QueryParam("maxItems") Integer maxItems,
@Context UriInfo uriInfo)
@ -219,11 +216,13 @@ public class FactoryService extends Service {
}
}
final List<FactoryDto> factories = new ArrayList<>();
for (Factory factory : factoryManager.getByAttribute(maxItems, skipCount, query)) {
factories.add(injectLinks(asDto(factory)));
Page<? extends Factory> factoriesPage =
factoryManager.getByAttribute(maxItems, skipCount, query);
List<FactoryDto> list = new ArrayList<>();
for (Factory factory : factoriesPage.getItems()) {
list.add(injectLinks(asDto(factory)));
}
return factories;
return Response.ok().entity(list).header("Link", createLinkHeader(factoriesPage)).build();
}
@PUT
@ -276,7 +275,7 @@ public class FactoryService extends Service {
@ApiResponse(code = 500, message = "Internal server error")
})
public void removeFactory(@ApiParam(value = "Factory identifier") @PathParam("id") String id)
throws ForbiddenException, ServerException {
throws ServerException {
factoryManager.removeFactory(id);
}
@ -329,23 +328,23 @@ public class FactoryService extends Service {
@DefaultValue("false")
@QueryParam(VALIDATE_QUERY_PARAMETER)
Boolean validate)
throws ServerException, BadRequestException {
throws BadRequestException, ServerException {
// check parameter
requiredNotNull(parameters, "Factory build parameters");
// search matching resolver and create factory from matching resolver
for (FactoryParametersResolver resolver : factoryParametersResolvers) {
if (resolver.accept(parameters)) {
final FactoryDto factory = resolver.createFactory(parameters);
if (validate) {
acceptValidator.validateOnAccept(factory);
}
return injectLinks(factory);
}
FactoryDto resolvedFactory =
factoryParametersResolverHolder
.getFactoryParametersResolver(parameters)
.createFactory(parameters);
if (resolvedFactory == null) {
throw new BadRequestException(FACTORY_NOT_RESOLVABLE);
}
// no match
throw new BadRequestException(ERROR_NO_RESOLVER_AVAILABLE);
if (validate) {
acceptValidator.validateOnAccept(resolvedFactory);
}
return injectLinks(resolvedFactory);
}
/** Injects factory links. If factory is named then accept named link will be injected. */
@ -424,24 +423,32 @@ public class FactoryService extends Service {
}
}
/** Usage of a dedicated class to manage the optional resolvers */
/** Usage of a dedicated class to manage the optional service-specific resolvers */
protected static class FactoryParametersResolverHolder {
/** Optional inject for the resolvers. */
@com.google.inject.Inject(optional = true)
private Set<FactoryParametersResolver> factoryParametersResolvers;
@SuppressWarnings("unused")
private Set<FactoryParametersResolver> specificFactoryParametersResolvers;
@Inject private DefaultFactoryParameterResolver defaultFactoryResolver;
/**
* Provides the set of resolvers if there are some else return an empty set.
* Provides a suitable resolver for the given parameters
*
* @return a non null set
* @return suitable service-specific resolver or default one
*/
public Set<FactoryParametersResolver> getFactoryParametersResolvers() {
if (factoryParametersResolvers != null) {
return factoryParametersResolvers;
} else {
return Collections.emptySet();
public FactoryParametersResolver getFactoryParametersResolver(Map<String, String> parameters) {
if (specificFactoryParametersResolvers == null) {
return defaultFactoryResolver;
}
for (FactoryParametersResolver factoryParametersResolver :
specificFactoryParametersResolvers) {
if (factoryParametersResolver.accept(parameters)) {
return factoryParametersResolver;
}
}
return defaultFactoryResolver;
}
}

View File

@ -27,7 +27,7 @@ public class ValueHelper {
*/
public static boolean isEmpty(Object value) {
return (null == value)
|| (value.getClass().equals(String.class) && isNullOrEmpty((String) value)
|| ((value.getClass().equals(String.class) && isNullOrEmpty((String) value))
|| (Collection.class.isAssignableFrom(value.getClass())
&& ((Collection) value).isEmpty())
|| (Map.class.isAssignableFrom(value.getClass()) && ((Map) value).isEmpty()));

View File

@ -11,16 +11,19 @@
*/
package org.eclipse.che.api.factory.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.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.core.Pages.iterate;
import com.google.inject.persist.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
@ -30,6 +33,7 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
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.notification.EventService;
import org.eclipse.che.api.factory.server.model.impl.FactoryImpl;
@ -92,7 +96,7 @@ public class JpaFactoryDao implements FactoryDao {
}
@Override
@Transactional
@Transactional(rollbackOn = {ServerException.class})
public FactoryImpl getById(String id) throws NotFoundException, ServerException {
requireNonNull(id);
try {
@ -107,42 +111,94 @@ public class JpaFactoryDao implements FactoryDao {
}
@Override
@Transactional
public List<FactoryImpl> getByAttribute(
@Transactional(rollbackOn = {ServerException.class})
public Page<FactoryImpl> getByAttributes(
int maxItems, int skipCount, List<Pair<String, String>> attributes) throws ServerException {
checkArgument(maxItems >= 0, "The number of items to return can't be negative.");
checkArgument(
skipCount >= 0 && skipCount <= Integer.MAX_VALUE,
"The number of items to skip can't be negative or greater than " + Integer.MAX_VALUE);
try {
LOG.debug(
"FactoryDao#getByAttributes #maxItems: {} #skipCount: {}, #attributes: {}",
maxItems,
skipCount,
attributes);
final Map<String, String> params = new HashMap<>();
String query = "SELECT factory FROM Factory factory";
if (!attributes.isEmpty()) {
final StringJoiner matcher = new StringJoiner(" AND ", " WHERE ", " ");
int i = 0;
for (Pair<String, String> attribute : attributes) {
final String parameterName = "parameterName" + i++;
params.put(parameterName, attribute.second);
matcher.add("factory." + attribute.first + " = :" + parameterName);
}
query = query + matcher;
final long count = countFactoriesByAttributes(attributes);
if (count == 0) {
return new Page<>(emptyList(), skipCount, maxItems, count);
}
final TypedQuery<FactoryImpl> typedQuery =
managerProvider
.get()
.createQuery(query, FactoryImpl.class)
.setFirstResult(skipCount)
.setMaxResults(maxItems);
for (Map.Entry<String, String> entry : params.entrySet()) {
typedQuery.setParameter(entry.getKey(), entry.getValue());
}
return typedQuery.getResultList().stream().map(FactoryImpl::new).collect(Collectors.toList());
List<FactoryImpl> result = getFactoriesByAttributes(maxItems, skipCount, attributes);
return new Page<>(result, skipCount, maxItems, count);
} catch (RuntimeException ex) {
throw new ServerException(ex.getLocalizedMessage(), ex);
}
}
@Override
@Transactional(rollbackOn = {ServerException.class})
public Page<FactoryImpl> getByUser(String userId, int maxItems, long skipCount)
throws ServerException {
requireNonNull(userId);
final Pair<String, String> factoryCreator = Pair.of("creator.userId", userId);
try {
long totalCount = countFactoriesByAttributes(singletonList(factoryCreator));
return new Page<>(
getFactoriesByAttributes(maxItems, skipCount, singletonList(factoryCreator)),
skipCount,
maxItems,
totalCount);
} catch (RuntimeException ex) {
throw new ServerException(ex.getMessage(), ex);
}
}
private List<FactoryImpl> getFactoriesByAttributes(
int maxItems, long skipCount, List<Pair<String, String>> attributes) {
final Map<String, String> params = new HashMap<>();
StringBuilder query = new StringBuilder("SELECT factory FROM Factory factory");
if (!attributes.isEmpty()) {
final StringJoiner matcher = new StringJoiner(" AND ", " WHERE ", " ");
int i = 0;
for (Pair<String, String> attribute : attributes) {
final String parameterName = "parameterName" + i++;
params.put(parameterName, attribute.second);
matcher.add("factory." + attribute.first + " = :" + parameterName);
}
query.append(matcher);
}
TypedQuery<FactoryImpl> typedQuery =
managerProvider
.get()
.createQuery(query.toString(), FactoryImpl.class)
.setFirstResult((int) skipCount)
.setMaxResults(maxItems);
for (Map.Entry<String, String> entry : params.entrySet()) {
typedQuery.setParameter(entry.getKey(), entry.getValue());
}
return typedQuery.getResultList().stream().map(FactoryImpl::new).collect(toList());
}
private Long countFactoriesByAttributes(List<Pair<String, String>> attributes) {
final Map<String, String> params = new HashMap<>();
StringBuilder query = new StringBuilder("SELECT COUNT(factory) FROM Factory factory");
if (!attributes.isEmpty()) {
final StringJoiner matcher = new StringJoiner(" AND ", " WHERE ", " ");
int i = 0;
for (Pair<String, String> attribute : attributes) {
final String parameterName = "parameterName" + i++;
params.put(parameterName, attribute.second);
matcher.add("factory." + attribute.first + " = :" + parameterName);
}
query.append(matcher);
}
TypedQuery<Long> typedQuery = managerProvider.get().createQuery(query.toString(), Long.class);
for (Map.Entry<String, String> entry : params.entrySet()) {
typedQuery.setParameter(entry.getKey(), entry.getValue());
}
return typedQuery.getSingleResult();
}
@Transactional
protected void doCreate(FactoryImpl factory) {
final EntityManager manager = managerProvider.get();
@ -196,9 +252,10 @@ public class JpaFactoryDao implements FactoryDao {
@Override
public void onCascadeEvent(BeforeUserRemovedEvent event) throws ServerException {
final Pair<String, String> factoryCreator =
Pair.of("creator.userId", event.getUser().getId());
for (FactoryImpl factory : factoryDao.getByAttribute(0, 0, singletonList(factoryCreator))) {
for (FactoryImpl factory :
iterate(
(maxItems, skipCount) ->
factoryDao.getByUser(event.getUser().getId(), maxItems, skipCount))) {
factoryDao.remove(factory.getId());
}
}

View File

@ -14,6 +14,7 @@ package org.eclipse.che.api.factory.server.spi;
import java.util.List;
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.factory.server.model.impl.FactoryImpl;
import org.eclipse.che.commons.lang.Pair;
@ -70,6 +71,16 @@ public interface FactoryDao {
*/
FactoryImpl getById(String id) throws NotFoundException, ServerException;
/**
* Gets all factories of specified user.
*
* @param userId user identifier
* @return list factory instances, never null
* @throws NullPointerException when {@code userId} is null
* @throws ServerException when any other error occurs
*/
Page<FactoryImpl> getByUser(String userId, int maxItems, long skipCount) throws ServerException;
/**
* Gets the factories for the list of attributes.
*
@ -80,6 +91,6 @@ public interface FactoryDao {
* @throws IllegalArgumentException when {@code skipCount} or {@code maxItems} is negative
* @throws ServerException when any other error occurs
*/
List<FactoryImpl> getByAttribute(
Page<FactoryImpl> getByAttributes(
int maxItems, int skipCount, List<Pair<String, String>> attributes) throws ServerException;
}

View File

@ -9,11 +9,12 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.plugin.urlfactory;
package org.eclipse.che.api.factory.server.urlfactory;
import static java.util.Collections.singletonList;
import java.util.List;
import java.util.function.Supplier;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
@ -33,22 +34,22 @@ public class ProjectConfigDtoMerger {
*
* <ul>
* <li>no projects --> add whole project
* <li>if projects
* <li>if projects:
* <ul>
* <li>: if there is only one project: add source if missing
* <li>if there is only one project: add source if missing
* <li>if many projects: do nothing
* </ul>
* </ul>
*
* @param factory
* @param computedProjectConfig
* @return
* @param factory source factory
* @param configSupplier supplier which can compute project config on demand
* @return factory with merged project sources
*/
public FactoryDto merge(FactoryDto factory, ProjectConfigDto computedProjectConfig) {
public FactoryDto merge(FactoryDto factory, Supplier<ProjectConfigDto> configSupplier) {
final List<ProjectConfigDto> projects = factory.getWorkspace().getProjects();
if (projects == null || projects.isEmpty()) {
factory.getWorkspace().setProjects(singletonList(computedProjectConfig));
factory.getWorkspace().setProjects(singletonList(configSupplier.get()));
return factory;
}
@ -56,7 +57,7 @@ public class ProjectConfigDtoMerger {
if (projects.size() == 1) {
ProjectConfigDto projectConfig = projects.get(0);
if (projectConfig.getSource() == null)
projectConfig.setSource(computedProjectConfig.getSource());
projectConfig.setSource(configSupplier.get().getSource());
}
return factory;

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.plugin.urlfactory;
package org.eclipse.che.api.factory.server.urlfactory;
import static java.util.Objects.requireNonNull;

View File

@ -0,0 +1,127 @@
/*
* 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.factory.server.urlfactory;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import com.google.common.base.Strings;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.devfile.model.Devfile;
import org.eclipse.che.api.devfile.server.DevfileFormatException;
import org.eclipse.che.api.devfile.server.DevfileManager;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.server.DtoConverter;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.dto.server.DtoFactory;
/**
* Handle the creation of some elements used inside a {@link FactoryDto}.
*
* @author Florent Benoit
* @author Max Shaposhnyk
*/
@Singleton
public class URLFactoryBuilder {
private final String defaultCheEditor;
private final String defaultChePlugins;
private final URLFetcher urlFetcher;
private final DevfileManager devfileManager;
@Inject
public URLFactoryBuilder(
@Named("che.factory.default_editor") String defaultCheEditor,
@Named("che.factory.default_plugins") String defaultChePlugins,
URLFetcher urlFetcher,
DevfileManager devfileManager) {
this.defaultCheEditor = defaultCheEditor;
this.defaultChePlugins = defaultChePlugins;
this.urlFetcher = urlFetcher;
this.devfileManager = devfileManager;
}
/**
* Build a factory using the provided json file or create default one
*
* @param jsonFileLocation location of factory json file
* @return a factory or null if factory json in not found
*/
public Optional<FactoryDto> createFactoryFromJson(String jsonFileLocation) {
// Check if there is factory json file inside the repository
if (jsonFileLocation != null) {
final String factoryJsonContent = urlFetcher.fetch(jsonFileLocation);
if (!Strings.isNullOrEmpty(factoryJsonContent)) {
return Optional.of(
DtoFactory.getInstance().createDtoFromJson(factoryJsonContent, FactoryDto.class));
}
}
return Optional.empty();
}
/**
* Build a factory using the provided devfile
*
* @param devfileLocation location of devfile
* @return a factory or null if devfile is not found
*/
public Optional<FactoryDto> createFactoryFromDevfile(String devfileLocation)
throws BadRequestException, ServerException {
if (devfileLocation == null) {
return Optional.empty();
}
final String devfileYamlContent = urlFetcher.fetch(devfileLocation);
if (Strings.isNullOrEmpty(devfileYamlContent)) {
return Optional.empty();
}
try {
Devfile devfile = devfileManager.parse(devfileYamlContent, false);
WorkspaceConfigImpl wsConfig = devfileManager.createWorkspaceConfig(devfile);
return Optional.of(
newDto(FactoryDto.class)
.withV(CURRENT_VERSION)
.withWorkspace(DtoConverter.asDto(wsConfig)));
} catch (DevfileFormatException e) {
throw new BadRequestException(e.getMessage());
} catch (IOException x) {
throw new ServerException(x.getLocalizedMessage(), x);
}
}
/**
* Help to generate default workspace configuration
*
* @param name the name of the workspace
* @return a workspace configuration
*/
public WorkspaceConfigDto buildDefaultWorkspaceConfig(String name) {
Map<String, String> attributes = new HashMap<>();
attributes.put(WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, defaultCheEditor);
attributes.put(WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE, defaultChePlugins);
// workspace configuration using the environment
return newDto(WorkspaceConfigDto.class).withName(name).withAttributes(attributes);
}
}

View File

@ -9,11 +9,13 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.plugin.urlfactory;
package org.eclipse.che.api.factory.server.urlfactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.ByteStreams;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@ -25,7 +27,6 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import org.apache.commons.compress.utils.BoundedInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,8 +41,8 @@ public class URLFetcher {
/** Logger. */
private static final Logger LOG = LoggerFactory.getLogger(URLFetcher.class);
/** Maximum size of allowed data. (40KB) */
protected static final long MAXIMUM_READ_BYTES = 40 * 1000;
/** Maximum size of allowed data. (80KB) */
protected static final long MAXIMUM_READ_BYTES = 80 * 1024;
/** The Compiled REGEX PATTERN that can be used for http|https git urls */
final Pattern GIT_HTTP_URL_PATTERN = Pattern.compile("(?<sanitized>^http[s]?://.*)\\.git$");
@ -77,7 +78,7 @@ public class URLFetcher {
try (InputStream inputStream = urlConnection.getInputStream();
BufferedReader reader =
new BufferedReader(
new InputStreamReader(new BoundedInputStream(inputStream, getLimit()), UTF_8))) {
new InputStreamReader(ByteStreams.limit(inputStream, getLimit()), UTF_8))) {
value = reader.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
// we shouldn't fetch if check is done before
@ -104,6 +105,7 @@ public class URLFetcher {
* @return if the url ends with .git will return the url without .git otherwise return the url as
* it is
*/
@VisibleForTesting
String sanitized(String url) {
if (url != null) {
final Matcher matcher = GIT_HTTP_URL_PATTERN.matcher(url);

View File

@ -22,14 +22,17 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static org.eclipse.che.api.factory.server.DtoConverter.asDto;
import static org.eclipse.che.api.factory.server.FactoryService.VALIDATE_QUERY_PARAMETER;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyMapOf;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.spy;
@ -38,21 +41,20 @@ import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.response.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.UriInfo;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.model.factory.Factory;
import org.eclipse.che.api.core.model.user.User;
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
@ -77,7 +79,9 @@ 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.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl.WorkspaceConfigImplBuilder;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl.WorkspaceImplBuilder;
import org.eclipse.che.api.workspace.shared.dto.CommandDto;
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
@ -126,12 +130,10 @@ public class FactoryServiceTest {
@Mock private FactoryEditValidator editValidator;
@Mock private WorkspaceManager workspaceManager;
@Mock private FactoryParametersResolverHolder factoryParametersResolverHolder;
@Mock private UriInfo uriInfo;
private FactoryBuilder factoryBuilderSpy;
private User user;
private Set<FactoryParametersResolver> factoryParametersResolvers;
private FactoryService service;
@ -144,11 +146,8 @@ public class FactoryServiceTest {
@BeforeMethod
public void setUp() throws Exception {
factoryBuilderSpy = spy(new FactoryBuilder(new SourceStorageParametersValidator()));
factoryParametersResolvers = new HashSet<>();
lenient().doNothing().when(factoryBuilderSpy).checkValid(any(FactoryDto.class));
lenient().doNothing().when(factoryBuilderSpy).checkValid(any(FactoryDto.class), anyBoolean());
when(factoryParametersResolverHolder.getFactoryParametersResolvers())
.thenReturn(factoryParametersResolvers);
user = new UserImpl(USER_ID, USER_EMAIL, ADMIN_USER_NAME);
lenient().when(userManager.getById(anyString())).thenReturn(user);
lenient()
@ -169,6 +168,7 @@ public class FactoryServiceTest {
@Filter
public static class EnvironmentFilter implements RequestFilter {
@Override
public void doFilter(GenericContainerRequest request) {
EnvironmentContext context = EnvironmentContext.getCurrent();
context.setSubject(new SubjectImpl(ADMIN_USER_NAME, USER_ID, ADMIN_USER_PASSWORD, false));
@ -253,10 +253,10 @@ public class FactoryServiceTest {
@Test
public void shouldReturnFactoryListByNameAttribute() throws Exception {
final Factory factory = createFactory();
when(factoryManager.getByAttribute(1, 0, ImmutableList.of(Pair.of("name", factory.getName()))))
.thenReturn(ImmutableList.of(factory));
final FactoryImpl factory = createFactory();
doReturn(new Page<>(ImmutableList.of(factory), 0, 1, 1))
.when(factoryManager)
.getByAttribute(1, 0, ImmutableList.of(Pair.of("name", factory.getName())));
final Response response =
given()
.auth()
@ -288,11 +288,11 @@ public class FactoryServiceTest {
@Test
public void shouldReturnFactoryListByCreatorAttribute() throws Exception {
final Factory factory1 = createNamedFactory("factory1");
final Factory factory2 = createNamedFactory("factory2");
when(factoryManager.getByAttribute(
2, 0, ImmutableList.of(Pair.of("creator.userId", user.getName()))))
.thenReturn(ImmutableList.of(factory1, factory2));
final FactoryImpl factory1 = createNamedFactory("factory1");
final FactoryImpl factory2 = createNamedFactory("factory2");
doReturn(new Page<>(ImmutableList.of(factory1, factory2), 0, 2, 2))
.when(factoryManager)
.getByAttribute(2, 0, ImmutableList.of(Pair.of("creator.userId", user.getName())));
final Response response =
given()
@ -406,28 +406,26 @@ public class FactoryServiceTest {
public void shouldGenerateFactoryJsonIncludeGivenProjects() throws Exception {
// given
final String wsId = "workspace123234";
WorkspaceImpl.WorkspaceImplBuilder ws = WorkspaceImpl.builder();
WorkspaceConfigImpl.WorkspaceConfigImplBuilder wsConfig = WorkspaceConfigImpl.builder();
WorkspaceImplBuilder ws = WorkspaceImpl.builder();
WorkspaceConfigImplBuilder wsConfig = WorkspaceConfigImpl.builder();
ws.setId(wsId);
wsConfig.setProjects(
Arrays.asList(
DTO.createDto(ProjectConfigDto.class)
newDto(ProjectConfigDto.class)
.withPath("/proj1")
.withSource(
DTO.createDto(SourceStorageDto.class).withType("git").withLocation("location")),
DTO.createDto(ProjectConfigDto.class)
newDto(SourceStorageDto.class).withType("git").withLocation("location")),
newDto(ProjectConfigDto.class)
.withPath("/proj2")
.withSource(
DTO.createDto(SourceStorageDto.class)
.withType("git")
.withLocation("location"))));
newDto(SourceStorageDto.class).withType("git").withLocation("location"))));
wsConfig.setName("wsname");
wsConfig.setEnvironments(singletonMap("env1", DTO.createDto(EnvironmentDto.class)));
wsConfig.setEnvironments(singletonMap("env1", newDto(EnvironmentDto.class)));
wsConfig.setDefaultEnv("env1");
ws.setStatus(WorkspaceStatus.RUNNING);
wsConfig.setCommands(
singletonList(
DTO.createDto(CommandDto.class)
newDto(CommandDto.class)
.withName("MCI")
.withType("mvn")
.withCommandLine("clean install")));
@ -453,20 +451,20 @@ public class FactoryServiceTest {
public void shouldNotGenerateFactoryIfNoProjectsWithSourceStorage() throws Exception {
// given
final String wsId = "workspace123234";
WorkspaceImpl.WorkspaceImplBuilder ws = WorkspaceImpl.builder();
WorkspaceConfigImpl.WorkspaceConfigImplBuilder wsConfig = WorkspaceConfigImpl.builder();
WorkspaceImplBuilder ws = WorkspaceImpl.builder();
WorkspaceConfigImplBuilder wsConfig = WorkspaceConfigImpl.builder();
ws.setId(wsId);
wsConfig.setProjects(
Arrays.asList(
DTO.createDto(ProjectConfigDto.class).withPath("/proj1"),
DTO.createDto(ProjectConfigDto.class).withPath("/proj2")));
newDto(ProjectConfigDto.class).withPath("/proj1"),
newDto(ProjectConfigDto.class).withPath("/proj2")));
wsConfig.setName("wsname");
wsConfig.setEnvironments(singletonMap("env1", DTO.createDto(EnvironmentDto.class)));
wsConfig.setEnvironments(singletonMap("env1", newDto(EnvironmentDto.class)));
wsConfig.setDefaultEnv("env1");
ws.setStatus(WorkspaceStatus.RUNNING);
wsConfig.setCommands(
singletonList(
DTO.createDto(CommandDto.class)
newDto(CommandDto.class)
.withName("MCI")
.withType("mvn")
.withCommandLine("clean install")));
@ -525,7 +523,8 @@ public class FactoryServiceTest {
@Test
public void checkValidateResolver() throws Exception {
final FactoryParametersResolver dummyResolver = Mockito.mock(FactoryParametersResolver.class);
factoryParametersResolvers.add(dummyResolver);
when(factoryParametersResolverHolder.getFactoryParametersResolver(anyMap()))
.thenReturn(dummyResolver);
// invalid factory
final String invalidFactoryMessage = "invalid factory";
@ -535,12 +534,10 @@ public class FactoryServiceTest {
// create factory
final FactoryDto expectFactory =
DTO.createDto(FactoryDto.class).withV("4.0").withName("matchingResolverFactory");
newDto(FactoryDto.class).withV(CURRENT_VERSION).withName("matchingResolverFactory");
// accept resolver
when(dummyResolver.accept(anyMapOf(String.class, String.class))).thenReturn(true);
when(dummyResolver.createFactory(anyMapOf(String.class, String.class)))
.thenReturn(expectFactory);
when(dummyResolver.createFactory(anyMap())).thenReturn(expectFactory);
// when
final Map<String, String> map = new HashMap<>();
@ -557,22 +554,22 @@ public class FactoryServiceTest {
assertTrue(response.getBody().asString().contains(invalidFactoryMessage));
// check we call resolvers
verify(dummyResolver).accept(anyMapOf(String.class, String.class));
verify(dummyResolver).createFactory(anyMapOf(String.class, String.class));
factoryParametersResolverHolder.getFactoryParametersResolver(anyMap());
verify(dummyResolver).createFactory(anyMap());
// check we call validator
verify(acceptValidator).validateOnAccept(any());
}
private Factory createFactory() {
private FactoryImpl createFactory() {
return createNamedFactory(FACTORY_NAME);
}
private Factory createNamedFactory(String name) {
private FactoryImpl createNamedFactory(String name) {
return createFactoryWithStorage(name, PROJECT_SOURCE_TYPE, PROJECT_SOURCE_LOCATION);
}
private Factory createFactoryWithStorage(String name, String type, String location) {
private FactoryImpl createFactoryWithStorage(String name, String type, String location) {
return FactoryImpl.builder()
.setId(FACTORY_ID)
.setVersion("4.0")
@ -620,9 +617,7 @@ public class FactoryServiceTest {
private static <T> List<T> unwrapDtoList(Response response, Class<T> dtoClass)
throws IOException {
return FluentIterable.from(
DtoFactory.getInstance()
.createListDtoFromJson(response.body().asInputStream(), dtoClass))
.toList();
return new ArrayList<>(
DtoFactory.getInstance().createListDtoFromJson(response.body().asInputStream(), dtoClass));
}
}

View File

@ -173,7 +173,7 @@ public class FactoryBuilderTest {
@Test(
expectedExceptions = ConflictException.class,
expectedExceptionsMessageRegExp =
"You are missing a mandatory parameter \"workspace.projects\\[1\\].type\". .*")
"You are missing a mandatory parameter \"workspace.projects\\[1\\].path\". .*")
public void shouldThrowExceptionWithMessagePointingToMissingMandatoryParameter()
throws Exception {
factoryBuilder = new FactoryBuilder(sourceProjectParametersValidator);
@ -196,7 +196,7 @@ public class FactoryBuilderTest {
.withAttributes(singletonMap("key", singletonList("value")))
.withDescription("description")
.withName("test")
.withPath("/test");
.withPath("");
FactoryDto factory = prepareFactory();
factory.getWorkspace().setProjects(asList(project, project2));

View File

@ -31,6 +31,7 @@ import java.util.stream.Stream;
import javax.inject.Inject;
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.model.factory.Button;
import org.eclipse.che.api.factory.server.model.impl.ActionImpl;
import org.eclipse.che.api.factory.server.model.impl.AuthorImpl;
@ -213,9 +214,9 @@ public class FactoryDaoTest {
public void shouldGetFactoryByIdAttribute() throws Exception {
final FactoryImpl factory = factories[0];
final List<Pair<String, String>> attributes = ImmutableList.of(Pair.of("id", factory.getId()));
final List<FactoryImpl> result = factoryDao.getByAttribute(1, 0, attributes);
final Page<FactoryImpl> result = factoryDao.getByAttributes(1, 0, attributes);
assertEquals(new HashSet<>(result), ImmutableSet.of(factory));
assertEquals(new HashSet<>(result.getItems()), ImmutableSet.of(factory));
}
@Test(dependsOnMethods = "shouldUpdateFactory")
@ -231,17 +232,25 @@ public class FactoryDaoTest {
factory3.getPolicies().setReferer("ref2");
factoryDao.update(factory1);
factoryDao.update(factory3);
final List<FactoryImpl> result = factoryDao.getByAttribute(factories.length, 0, attributes);
final Page<FactoryImpl> result = factoryDao.getByAttributes(factories.length, 0, attributes);
assertEquals(new HashSet<>(result), ImmutableSet.of(factories[0], factories[2], factories[4]));
assertEquals(
new HashSet<>(result.getItems()),
ImmutableSet.of(factories[0], factories[2], factories[4]));
}
@Test
public void shouldFindAllFactoriesWhenAttributesNotSpecified() throws Exception {
final List<Pair<String, String>> attributes = emptyList();
final List<FactoryImpl> result = factoryDao.getByAttribute(factories.length, 0, attributes);
final Page<FactoryImpl> result = factoryDao.getByAttributes(factories.length, 0, attributes);
assertEquals(new HashSet<>(result), new HashSet<>(asList(factories)));
assertEquals(new HashSet<>(result.getItems()), new HashSet<>(asList(factories)));
}
@Test
public void shouldFindAllFactoriesOfSpecifiedUser() throws Exception {
final Page<FactoryImpl> result = factoryDao.getByUser(users[1].getId(), 30, 0);
assertEquals(new HashSet<>(result.getItems()), new HashSet<>(asList(factories[1])));
}
@Test(expectedExceptions = NotFoundException.class, dependsOnMethods = "shouldGetFactoryById")

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.plugin.urlfactory;
package org.eclipse.che.api.factory.server.urlfactory;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.testng.Assert.assertEquals;
@ -60,7 +60,7 @@ public class ProjectConfigDtoMergerTest {
Assert.assertTrue(factory.getWorkspace().getProjects().isEmpty());
// merge
projectConfigDtoMerger.merge(factory, computedProjectConfig);
projectConfigDtoMerger.merge(factory, () -> computedProjectConfig);
// project
assertEquals(factory.getWorkspace().getProjects().size(), 1);
@ -79,7 +79,7 @@ public class ProjectConfigDtoMergerTest {
Assert.assertNull(projectConfigDto.getSource());
// merge
projectConfigDtoMerger.merge(factory, computedProjectConfig);
projectConfigDtoMerger.merge(factory, () -> computedProjectConfig);
// project still 1
assertEquals(factory.getWorkspace().getProjects().size(), 1);

View File

@ -9,7 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.plugin.urlfactory;
package org.eclipse.che.api.factory.server.urlfactory;
import static org.eclipse.jetty.http.HttpStatus.NOT_FOUND_404;
import static org.eclipse.jetty.http.HttpStatus.OK_200;
@ -23,7 +23,6 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -81,9 +80,7 @@ public class URLCheckerTest {
public void checkUrlFileNotExists() {
// test to check if this url exist
URL urlJson =
URLCheckerTest.class.getResource(
"/" + URLCheckerTest.class.getPackage().getName().replace('.', '/') + "/.che.json");
URL urlJson = getClass().getClassLoader().getResource(".che.json");
Assert.assertNotNull(urlJson);
boolean exists = URLChecker.exists(urlJson.toString() + "-notfound");
@ -101,9 +98,7 @@ public class URLCheckerTest {
@Test
public void checkUrlIsInvalid() throws MalformedURLException {
// test to check if this url exist
URL urlJson =
URLCheckerTest.class.getResource(
"/" + URLCheckerTest.class.getPackage().getName().replace('.', '/') + "/.che.json");
URL urlJson = getClass().getClassLoader().getResource(".che.json");
Assert.assertNotNull(urlJson);
boolean exists = URLChecker.exists(new URL(urlJson.toString() + "-notfound"));
@ -166,8 +161,8 @@ public class URLCheckerTest {
/** Dummy servlet class. */
static class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getOutputStream().print("hello");
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.factory.server.urlfactory;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.che.api.devfile.server.DevfileManager;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.dto.server.DtoFactory;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Testing {@link URLFactoryBuilder}
*
* @author Florent Benoit
* @author Max Shaposhnyk
*/
@Listeners(MockitoTestNGListener.class)
public class URLFactoryBuilderTest {
private final String defaultEditor = "org.eclipse.che.editor.theia:1.0.0";
private final String defaultPlugin = "che-machine-exec-plugin:0.0.1";
/** Grab content of URLs */
@Mock private URLFetcher urlFetcher;
@Mock private DevfileManager devfileManager;
/** Tested instance. */
private URLFactoryBuilder urlFactoryBuilder;
@BeforeClass
public void setUp() {
this.urlFactoryBuilder =
new URLFactoryBuilder(defaultEditor, defaultPlugin, urlFetcher, devfileManager);
}
@Test
public void checkDefaultConfiguration() throws Exception {
Map<String, String> attributes = new HashMap<>();
attributes.put(WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, defaultEditor);
attributes.put(WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE, defaultPlugin);
// setup environment
WorkspaceConfigDto expectedWsConfig =
newDto(WorkspaceConfigDto.class).withAttributes(attributes).withName("foo");
WorkspaceConfigDto actualWsConfigDto = urlFactoryBuilder.buildDefaultWorkspaceConfig("foo");
assertEquals(actualWsConfigDto, expectedWsConfig);
}
/** Check that with a custom factory.json we've this factory being built */
@Test
public void checkWithCustomFactoryJsonFile() throws Exception {
WorkspaceConfigDto workspaceConfigDto = newDto(WorkspaceConfigDto.class);
FactoryDto templateFactory =
newDto(FactoryDto.class)
.withV(CURRENT_VERSION)
.withName("florent")
.withWorkspace(workspaceConfigDto);
String jsonFactory = DtoFactory.getInstance().toJson(templateFactory);
String myLocation = "http://foo-location";
when(urlFetcher.fetch(myLocation)).thenReturn(jsonFactory);
FactoryDto factory = urlFactoryBuilder.createFactoryFromJson(myLocation).get();
assertEquals(templateFactory, factory);
}
}

View File

@ -9,10 +9,10 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.plugin.urlfactory;
package org.eclipse.che.api.factory.server.urlfactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.che.plugin.urlfactory.URLFetcher.MAXIMUM_READ_BYTES;
import static org.eclipse.che.api.factory.server.urlfactory.URLFetcher.MAXIMUM_READ_BYTES;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@ -51,9 +51,7 @@ public class URLFetcherTest {
public void checkGetContent() {
// test to download this class object
URL urlJson =
URLFetcherTest.class.getResource(
"/" + URLFetcherTest.class.getPackage().getName().replace('.', '/') + "/.che.json");
URL urlJson = getClass().getClassLoader().getResource(".che.json");
Assert.assertNotNull(urlJson);
String content = URLFetcher.fetch(urlJson.toString());
@ -82,9 +80,7 @@ public class URLFetcherTest {
public void checkMissingContent() {
// test to download this class object
URL urlJson =
URLFetcherTest.class.getResource(
"/" + URLFetcherTest.class.getPackage().getName().replace('.', '/') + "/.che.json");
URL urlJson = getClass().getClassLoader().getResource(".che.json");
Assert.assertNotNull(urlJson);
// add extra path to make url not found
@ -95,9 +91,7 @@ public class URLFetcherTest {
/** Check when we reach custom limit */
@Test
public void checkPartialContent() {
URL urlJson =
URLFetcherTest.class.getResource(
"/" + URLFetcherTest.class.getPackage().getName().replace('.', '/') + "/.che.json");
URL urlJson = getClass().getClassLoader().getResource(".che.json");
Assert.assertNotNull(urlJson);
String content = new OneByteURLFetcher().fetch(urlJson.toString());
@ -118,9 +112,9 @@ public class URLFetcherTest {
}
/** Limit to only one Byte. */
class OneByteURLFetcher extends URLFetcher {
static class OneByteURLFetcher extends URLFetcher {
/** Override the limit */
@Override
protected long getLimit() {
return 1;
}

View File

@ -50,7 +50,7 @@ public interface ProjectConfigDto extends ProjectConfig {
ProjectConfigDto withDescription(String description);
@Override
@FactoryParameter(obligation = MANDATORY)
@FactoryParameter(obligation = OPTIONAL)
String getType();
void setType(String type);