pull/567/head
Igor 2023-09-08 11:34:09 +03:00
parent 9d43803100
commit d3fe5cf3f3
16 changed files with 584 additions and 9 deletions

View File

@ -139,6 +139,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-bitbucket-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-git-ssh</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-github</artifactId>

View File

@ -44,6 +44,8 @@ import org.eclipse.che.api.factory.server.bitbucket.BitbucketFactoryParametersRe
import org.eclipse.che.api.factory.server.bitbucket.BitbucketScmFileResolver;
import org.eclipse.che.api.factory.server.bitbucket.BitbucketServerAuthorizingFactoryParametersResolver;
import org.eclipse.che.api.factory.server.bitbucket.BitbucketServerScmFileResolver;
import org.eclipse.che.api.factory.server.git.ssh.GitSshFactoryParametersResolver;
import org.eclipse.che.api.factory.server.git.ssh.GitSshScmFileResolver;
import org.eclipse.che.api.factory.server.github.GithubFactoryParametersResolver;
import org.eclipse.che.api.factory.server.github.GithubScmFileResolver;
import org.eclipse.che.api.factory.server.gitlab.GitlabFactoryParametersResolver;
@ -175,6 +177,7 @@ public class WsMasterModule extends AbstractModule {
factoryParametersResolverMultibinder
.addBinding()
.to(AzureDevOpsFactoryParametersResolver.class);
factoryParametersResolverMultibinder.addBinding().to(GitSshFactoryParametersResolver.class);
Multibinder<ScmFileResolver> scmFileResolverResolverMultibinder =
Multibinder.newSetBinder(binder(), ScmFileResolver.class);
@ -183,6 +186,7 @@ public class WsMasterModule extends AbstractModule {
scmFileResolverResolverMultibinder.addBinding().to(GitlabScmFileResolver.class);
scmFileResolverResolverMultibinder.addBinding().to(BitbucketServerScmFileResolver.class);
scmFileResolverResolverMultibinder.addBinding().to(AzureDevOpsScmFileResolver.class);
scmFileResolverResolverMultibinder.addBinding().to(GitSshScmFileResolver.class);
install(new org.eclipse.che.api.factory.server.scm.KubernetesScmModule());
install(new org.eclipse.che.api.factory.server.bitbucket.BitbucketServerModule());

View File

@ -759,6 +759,11 @@
<artifactId>che-core-api-factory-bitbucket-server</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-git-ssh</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-github</artifactId>

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2023 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-master-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>7.74.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-factory-git-ssh</artifactId>
<packaging>jar</packaging>
<name>Che Core :: API :: Factory Resolver Git Ssh</name>
<properties>
<findbugs.failonerror>true</findbugs.failonerror>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory</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-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</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.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.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

@ -0,0 +1,31 @@
/*
* Copyright (c) 2012-2023 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.git.ssh;
import org.eclipse.che.api.factory.server.scm.AuthorizingFileContentProvider;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
/**
* Git Ssh specific authorizing file content provider.
*
* @author Anatolii Bazko
*/
class GitSshAuthorizingFileContentProvider extends AuthorizingFileContentProvider<GitSshUrl> {
GitSshAuthorizingFileContentProvider(
GitSshUrl gitSshUrl,
URLFetcher urlFetcher,
PersonalAccessTokenManager personalAccessTokenManager) {
super(gitSshUrl, urlFetcher, personalAccessTokenManager);
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2012-2023 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.git.ssh;
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 jakarta.validation.constraints.NotNull;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.factory.server.RawDevfileUrlFactoryParameterResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDevfileV2Dto;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
import org.eclipse.che.api.factory.shared.dto.FactoryVisitor;
import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
/**
* Provides Factory Parameters resolver for Git Ssh repositories.
*
* @author Anatolii Bazko
*/
@Singleton
public class GitSshFactoryParametersResolver extends RawDevfileUrlFactoryParameterResolver {
private final GitSshURLParser gitSshURLParser;
private final PersonalAccessTokenManager personalAccessTokenManager;
@Inject
public GitSshFactoryParametersResolver(
GitSshURLParser gitSshURLParser,
URLFetcher urlFetcher,
URLFactoryBuilder urlFactoryBuilder,
PersonalAccessTokenManager personalAccessTokenManager) {
super(urlFactoryBuilder, urlFetcher);
this.gitSshURLParser = gitSshURLParser;
this.personalAccessTokenManager = personalAccessTokenManager;
}
@Override
public boolean accept(@NotNull final Map<String, String> factoryParameters) {
return factoryParameters.containsKey(URL_PARAMETER_NAME)
&& gitSshURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public FactoryMetaDto createFactory(@NotNull final Map<String, String> factoryParameters)
throws ApiException {
// no need to check null value of url parameter as accept() method has performed the check
final GitSshUrl gitSshUrl = gitSshURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
gitSshUrl,
new GitSshAuthorizingFileContentProvider(
gitSshUrl, urlFetcher, personalAccessTokenManager),
extractOverrideParams(factoryParameters),
true)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new GitSshFactoryVisitor(gitSshUrl));
}
/**
* Visitor that puts the default devfile or updates devfile projects into the Git Ssh Factory, if
* needed.
*/
private class GitSshFactoryVisitor implements FactoryVisitor {
private final GitSshUrl gitSshUrl;
private GitSshFactoryVisitor(GitSshUrl gitSshUrl) {
this.gitSshUrl = gitSshUrl;
}
@Override
public FactoryDevfileV2Dto visit(FactoryDevfileV2Dto factoryDto) {
ScmInfoDto scmInfo =
newDto(ScmInfoDto.class)
.withScmProviderName(gitSshUrl.getProviderName())
.withRepositoryUrl(gitSshUrl.getRepositoryLocation());
return factoryDto.withScmInfo(scmInfo);
}
@Override
public FactoryDto visit(FactoryDto factory) {
if (factory.getDevfile() == null) {
factory.setDevfile(urlFactoryBuilder.buildDefaultDevfile(gitSshUrl.getRepository()));
}
updateProjects(
factory.getDevfile(),
() ->
newDto(ProjectDto.class)
.withSource(
newDto(SourceDto.class)
.withLocation(gitSshUrl.getRepositoryLocation())
.withType("git"))
.withName(gitSshUrl.getRepository()),
project -> {});
return factory;
}
}
@Override
public RemoteFactoryUrl parseFactoryUrl(String factoryUrl) {
return gitSshURLParser.parse(factoryUrl);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2012-2023 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.git.ssh;
import jakarta.validation.constraints.NotNull;
import javax.inject.Inject;
import org.eclipse.che.api.factory.server.ScmFileResolver;
/**
* Git Ssh specific SCM file resolver.
*
* @author Anatolii Bazko
*/
public class GitSshScmFileResolver implements ScmFileResolver {
private final GitSshURLParser gitSshURLParser;
@Inject
public GitSshScmFileResolver(GitSshURLParser gitSshURLParser) {
this.gitSshURLParser = gitSshURLParser;
}
@Override
public boolean accept(@NotNull String repository) {
return gitSshURLParser.isValid(repository);
}
/**
* There is no way to get a file content from a git repository via ssh protocol. So this method
* always returns an empty string. It allows to start a workspace from an empty devfile.
*/
@Override
public String fileContent(@NotNull String repository, @NotNull String filePath) {
return "";
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2012-2023 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.git.ssh;
import static java.lang.String.format;
import static java.util.regex.Pattern.compile;
import jakarta.validation.constraints.NotNull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
/**
* Parser of String Git Ssh URLs and provide {@link GitSshUrl} objects.
*
* @author Anatolii Bazko
*/
@Singleton
public class GitSshURLParser {
private final Pattern gitSshPattern;
private final DevfileFilenamesProvider devfileFilenamesProvider;
@Inject
public GitSshURLParser(DevfileFilenamesProvider devfileFilenamesProvider) {
this.devfileFilenamesProvider = devfileFilenamesProvider;
this.gitSshPattern = compile("^git@(?<hostName>[^:]++):(.*)/(?<repoName>[^/]++)$");
}
public boolean isValid(@NotNull String url) {
return gitSshPattern.matcher(url).matches();
}
public GitSshUrl parse(String url) {
Matcher matcher = gitSshPattern.matcher(url);
if (!matcher.matches()) {
throw new IllegalArgumentException(
format("The given url %s is not a valid. It should start with git@<hostName>", url));
}
String hostName = matcher.group("hostName");
String repoName = matcher.group("repoName");
if (repoName.endsWith(".git")) {
repoName = repoName.substring(0, repoName.length() - 4);
}
return new GitSshUrl()
.withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames())
.withHostName(hostName)
.withRepository(repoName)
.withRepositoryLocation(url)
.withUrl(url);
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2012-2023 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.git.ssh;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl;
/**
* Representation of Git Ssh URL, allowing to get details from it.
*
* @author Anatolii Bazko
*/
public class GitSshUrl extends DefaultFactoryUrl {
private String repository;
private String hostName;
private String repositoryLocation;
private final List<String> devfileFilenames = new ArrayList<>();
protected GitSshUrl() {}
@Override
public String getProviderName() {
return "git-ssh";
}
@Override
public String getBranch() {
return null;
}
public GitSshUrl withDevfileFilenames(List<String> devfileFilenames) {
this.devfileFilenames.addAll(devfileFilenames);
return this;
}
@Override
public void setDevfileFilename(String devfileName) {
this.devfileFilenames.clear();
this.devfileFilenames.add(devfileName);
}
@Override
public List<DevfileLocation> devfileFileLocations() {
return devfileFilenames.stream().map(this::createDevfileLocation).collect(Collectors.toList());
}
@Override
public String rawFileLocation(String filename) {
return filename;
}
private DevfileLocation createDevfileLocation(String devfileFilename) {
return new DevfileLocation() {
@Override
public Optional<String> filename() {
return Optional.of(devfileFilename);
}
@Override
public String location() {
return devfileFilename;
}
};
}
@Override
public String getHostName() {
return hostName;
}
public GitSshUrl withHostName(String hostName) {
this.hostName = hostName;
return this;
}
public String getRepositoryLocation() {
return repositoryLocation;
}
public GitSshUrl withRepositoryLocation(String repositoryLocation) {
this.repositoryLocation = repositoryLocation;
return this;
}
public String getRepository() {
return repository;
}
public GitSshUrl withRepository(String repository) {
this.repository = repository;
return this;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2012-2023 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.git.ssh;
import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertEquals;
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** @author Anatalii Bazko */
@Listeners(MockitoTestNGListener.class)
public class GitSshURLParserTest {
private GitSshURLParser gitSshURLParser;
@BeforeMethod
protected void start() {
gitSshURLParser = new GitSshURLParser(mock(DevfileFilenamesProvider.class));
}
@Test(dataProvider = "parsing")
public void testParse(String url, String hostName, String repository) {
GitSshUrl gitSshUrl = gitSshURLParser.parse(url);
assertEquals(gitSshUrl.getHostName(), hostName);
assertEquals(gitSshUrl.getRepository(), repository);
}
@DataProvider(name = "parsing")
public Object[][] expectedParsing() {
return new Object[][] {
{"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", "ssh.dev.azure.com", "MyRepo"},
{"git@github.com:MyOrg/MyRepo.git", "github.com", "MyRepo"},
};
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2023 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%nopex</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</configuration>

View File

@ -0,0 +1,2 @@
package PACKAGE_NAME;public enum FactoryResolverPriority {
}

View File

@ -61,11 +61,6 @@ public class DefaultFactoryUrl implements RemoteFactoryUrl {
return URI.create(devfileFileLocation).getHost();
}
@Override
public String getProviderUrl() {
return getHostName();
}
@Override
public String getBranch() {
return null;

View File

@ -39,9 +39,6 @@ public interface RemoteFactoryUrl {
/** Remote hostname */
String getHostName();
/** Remote provider URL */
String getProviderUrl();
/** Remote branch */
String getBranch();

View File

@ -46,7 +46,7 @@ public class AuthorizingFactoryParameterResolverTest {
@Test
public void shouldFetchContentWithAuthentication() throws Exception {
// given
when(remoteFactoryUrl.getProviderUrl()).thenReturn("https://provider.url");
when(remoteFactoryUrl.getHostName()).thenReturn("hostName");
when(urlFetcher.fetch(anyString(), anyString())).thenReturn("content");
when(personalAccessTokenManager.getAndStore(anyString())).thenReturn(personalAccessToken);

View File

@ -40,6 +40,7 @@
<module>che-core-api-account</module>
<module>che-core-api-user</module>
<module>che-core-api-factory-azure-devops</module>
<module>che-core-api-factory-git-ssh</module>
<module>che-core-api-factory-shared</module>
<module>che-core-api-factory</module>
<module>che-core-api-factory-github</module>