feat: Support git+ssh urls (#458)
* feat: Support git+ssh urls Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/460/head
parent
709d872d24
commit
04bf956751
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -174,6 +176,7 @@ public class WsMasterModule extends AbstractModule {
|
|||
factoryParametersResolverMultibinder
|
||||
.addBinding()
|
||||
.to(AzureDevOpsFactoryParametersResolver.class);
|
||||
factoryParametersResolverMultibinder.addBinding().to(GitSshFactoryParametersResolver.class);
|
||||
|
||||
Multibinder<ScmFileResolver> scmFileResolverResolverMultibinder =
|
||||
Multibinder.newSetBinder(binder(), ScmFileResolver.class);
|
||||
|
|
@ -182,6 +185,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());
|
||||
|
|
|
|||
5
pom.xml
5
pom.xml
|
|
@ -739,6 +739,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>
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* 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.azure.devops;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Anatalii Bazko
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class AzureDevOpsURLParserTest {
|
||||
|
||||
private AzureDevOpsURLParser azureDevOpsURLParser;
|
||||
|
||||
@BeforeMethod
|
||||
protected void start() {
|
||||
azureDevOpsURLParser =
|
||||
new AzureDevOpsURLParser(mock(DevfileFilenamesProvider.class), "https://dev.azure.com/");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testParseInvalidUrl(){
|
||||
azureDevOpsURLParser.parse("http://www.eclipse.org");
|
||||
}
|
||||
|
||||
|
||||
@Test(dataProvider = "parsing")
|
||||
public void testParse(
|
||||
String url, String organization, String project, String repository, String branch, String tag) {
|
||||
AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(url);
|
||||
|
||||
assertEquals(azureDevOpsUrl.getOrganization(), organization);
|
||||
assertEquals(azureDevOpsUrl.getProject(), project);
|
||||
assertEquals(azureDevOpsUrl.getRepository(), repository);
|
||||
assertEquals(azureDevOpsUrl.getBranch(), branch);
|
||||
assertEquals(azureDevOpsUrl.getTag(), tag);
|
||||
}
|
||||
|
||||
@DataProvider(name = "parsing")
|
||||
public Object[][] expectedParsing() {
|
||||
return new Object[][]{
|
||||
{"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", "MyProject", "MyRepo", null, null},
|
||||
{"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.git", "MyOrg", "MyProject", "MyRepo", null, null},
|
||||
{"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot.git", "MyOrg", "MyProject", "MyRepo.dot", null, null},
|
||||
{"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", "MyProject", "MyRepo", null, null},
|
||||
{"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen", "MyOrg", "MyProject", "MyRepo-with-hypen", null, null},
|
||||
{"https://dev.azure.com/MyOrg/MyProject/_git/-", "MyOrg", "MyProject", "-", null, null},
|
||||
{"https://dev.azure.com/MyOrg/MyProject/_git/-j.git", "MyOrg", "MyProject", "-j", null, null},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain&_a=contents",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
"main",
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
"main",
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag&_a=contents",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
"MyTag"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
"MyTag"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "url")
|
||||
public void testCredentials(String url, String organization, Optional<String> credentials) {
|
||||
AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(url);
|
||||
|
||||
assertEquals(azureDevOpsUrl.getOrganization(), organization);
|
||||
assertEquals(azureDevOpsUrl.getCredentials(), credentials);
|
||||
}
|
||||
|
||||
@DataProvider(name = "url")
|
||||
public Object[][] url() {
|
||||
return new Object[][]{
|
||||
{"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", Optional.empty()},
|
||||
{"https://user:pwd@dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", Optional.of("user:pwd")},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -11,55 +11,44 @@
|
|||
*/
|
||||
package org.eclipse.che.api.factory.server.azure.devops;
|
||||
|
||||
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
|
||||
import org.eclipse.che.commons.subject.Subject;
|
||||
import org.eclipse.che.security.oauth.OAuthAPI;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Anatalii Bazko
|
||||
*/
|
||||
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
|
||||
import org.eclipse.che.commons.subject.Subject;
|
||||
import org.eclipse.che.security.oauth.OAuthAPI;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** @author Anatalii Bazko */
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class AzureDevOpsPersonalAccessTokenFetcherTest {
|
||||
|
||||
@Mock
|
||||
private AzureDevOpsApiClient azureDevOpsApiClient;
|
||||
@Mock
|
||||
private OAuthAPI oAuthAPI;
|
||||
@Mock
|
||||
private OAuthToken oAuthToken;
|
||||
@Mock
|
||||
private AzureDevOpsUser azureDevOpsUser;
|
||||
@Mock private AzureDevOpsApiClient azureDevOpsApiClient;
|
||||
@Mock private OAuthAPI oAuthAPI;
|
||||
@Mock private OAuthToken oAuthToken;
|
||||
@Mock private AzureDevOpsUser azureDevOpsUser;
|
||||
private AzureDevOpsPersonalAccessTokenFetcher personalAccessTokenFetcher;
|
||||
|
||||
@BeforeMethod
|
||||
protected void start() {
|
||||
personalAccessTokenFetcher = new AzureDevOpsPersonalAccessTokenFetcher(
|
||||
"localhost",
|
||||
"https://dev.azure.com",
|
||||
new String[]{},
|
||||
azureDevOpsApiClient,
|
||||
oAuthAPI);
|
||||
personalAccessTokenFetcher =
|
||||
new AzureDevOpsPersonalAccessTokenFetcher(
|
||||
"localhost", "https://dev.azure.com", new String[] {}, azureDevOpsApiClient, oAuthAPI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchPersonalAccessTokenShouldReturnNullIfScmServerUrlIsNotAzureDevOps() throws Exception {
|
||||
PersonalAccessToken personalAccessToken = personalAccessTokenFetcher.fetchPersonalAccessToken(mock(Subject.class), "https://eclipse.org");
|
||||
public void fetchPersonalAccessTokenShouldReturnNullIfScmServerUrlIsNotAzureDevOps()
|
||||
throws Exception {
|
||||
PersonalAccessToken personalAccessToken =
|
||||
personalAccessTokenFetcher.fetchPersonalAccessToken(
|
||||
mock(Subject.class), "https://eclipse.org");
|
||||
|
||||
assertNull(personalAccessToken);
|
||||
}
|
||||
|
|
@ -68,9 +57,11 @@ public class AzureDevOpsPersonalAccessTokenFetcherTest {
|
|||
public void fetchPersonalAccessTokenShouldReturnToken() throws Exception {
|
||||
when(oAuthAPI.getToken(AzureDevOps.PROVIDER_NAME)).thenReturn(oAuthToken);
|
||||
when(azureDevOpsApiClient.getUserWithOAuthToken(any())).thenReturn(azureDevOpsUser);
|
||||
when(azureDevOpsApiClient.getTokenScopes(any())).thenReturn(new String[]{"vso.code_full"});
|
||||
when(azureDevOpsApiClient.getTokenScopes(any())).thenReturn(new String[] {"vso.code_full"});
|
||||
|
||||
PersonalAccessToken personalAccessToken = personalAccessTokenFetcher.fetchPersonalAccessToken(mock(Subject.class), "https://dev.azure.com/");
|
||||
PersonalAccessToken personalAccessToken =
|
||||
personalAccessTokenFetcher.fetchPersonalAccessToken(
|
||||
mock(Subject.class), "https://dev.azure.com/");
|
||||
|
||||
assertNotNull(personalAccessToken);
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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.azure.devops;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Optional;
|
||||
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 AzureDevOpsURLParserTest {
|
||||
|
||||
private AzureDevOpsURLParser azureDevOpsURLParser;
|
||||
|
||||
@BeforeMethod
|
||||
protected void start() {
|
||||
azureDevOpsURLParser =
|
||||
new AzureDevOpsURLParser(mock(DevfileFilenamesProvider.class), "https://dev.azure.com/");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testParseInvalidUrl() {
|
||||
azureDevOpsURLParser.parse("http://www.eclipse.org");
|
||||
}
|
||||
|
||||
@Test(dataProvider = "parsing")
|
||||
public void testParse(
|
||||
String url,
|
||||
String organization,
|
||||
String project,
|
||||
String repository,
|
||||
String branch,
|
||||
String tag) {
|
||||
AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(url);
|
||||
|
||||
assertEquals(azureDevOpsUrl.getOrganization(), organization);
|
||||
assertEquals(azureDevOpsUrl.getProject(), project);
|
||||
assertEquals(azureDevOpsUrl.getRepository(), repository);
|
||||
assertEquals(azureDevOpsUrl.getBranch(), branch);
|
||||
assertEquals(azureDevOpsUrl.getTag(), tag);
|
||||
}
|
||||
|
||||
@DataProvider(name = "parsing")
|
||||
public Object[][] expectedParsing() {
|
||||
return new Object[][] {
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.git",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot.git",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo.dot",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo-with-hypen",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{"https://dev.azure.com/MyOrg/MyProject/_git/-", "MyOrg", "MyProject", "-", null, null},
|
||||
{"https://dev.azure.com/MyOrg/MyProject/_git/-j.git", "MyOrg", "MyProject", "-j", null, null},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain&_a=contents",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
"main",
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
"main",
|
||||
null
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag&_a=contents",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
"MyTag"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
"MyTag"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "url")
|
||||
public void testCredentials(String url, String organization, Optional<String> credentials) {
|
||||
AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(url);
|
||||
|
||||
assertEquals(azureDevOpsUrl.getOrganization(), organization);
|
||||
assertEquals(azureDevOpsUrl.getCredentials(), credentials);
|
||||
}
|
||||
|
||||
@DataProvider(name = "url")
|
||||
public Object[][] url() {
|
||||
return new Object[][] {
|
||||
{"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", Optional.empty()},
|
||||
{
|
||||
"https://user:pwd@dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"MyOrg",
|
||||
Optional.of("user:pwd")
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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.63.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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.DefaultFactoryParameterResolver;
|
||||
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 DefaultFactoryParameterResolver {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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"},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -187,10 +187,15 @@ public class FactoryService extends Service {
|
|||
throws BadRequestException {
|
||||
for (FactoryParametersResolver factoryParametersResolver :
|
||||
specificFactoryParametersResolvers) {
|
||||
if (factoryParametersResolver.accept(parameters)) {
|
||||
return factoryParametersResolver;
|
||||
try {
|
||||
if (factoryParametersResolver.accept(parameters)) {
|
||||
return factoryParametersResolver;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore and try next resolver
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultFactoryResolver.accept(parameters)) {
|
||||
return defaultFactoryResolver;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue