parent
0ea20e687b
commit
641dc5fe21
|
|
@ -139,10 +139,6 @@
|
|||
<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,8 +44,6 @@ 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;
|
||||
|
|
@ -177,7 +175,6 @@ public class WsMasterModule extends AbstractModule {
|
|||
factoryParametersResolverMultibinder
|
||||
.addBinding()
|
||||
.to(AzureDevOpsFactoryParametersResolver.class);
|
||||
factoryParametersResolverMultibinder.addBinding().to(GitSshFactoryParametersResolver.class);
|
||||
|
||||
Multibinder<ScmFileResolver> scmFileResolverResolverMultibinder =
|
||||
Multibinder.newSetBinder(binder(), ScmFileResolver.class);
|
||||
|
|
@ -186,7 +183,6 @@ 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
|
|
@ -759,11 +759,6 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ public class AzureDevOpsURLParser {
|
|||
*/
|
||||
private final Pattern azureDevOpsPattern;
|
||||
|
||||
private final Pattern azureSSHDevOpsPattern;
|
||||
|
||||
@Inject
|
||||
public AzureDevOpsURLParser(
|
||||
DevfileFilenamesProvider devfileFilenamesProvider,
|
||||
|
|
@ -58,14 +60,22 @@ public class AzureDevOpsURLParser {
|
|||
+ "([?&]version=GB(?<branch>[^&]++))?"
|
||||
+ "(.*)",
|
||||
azureDevOpsScmApiEndpointHost));
|
||||
this.azureSSHDevOpsPattern =
|
||||
compile(
|
||||
format(
|
||||
"^git@ssh\\.%s:v3/(?<organization>.*)/(?<project>.*)/(?<repoName>.*)$",
|
||||
azureDevOpsScmApiEndpointHost));
|
||||
}
|
||||
|
||||
public boolean isValid(@NotNull String url) {
|
||||
return azureDevOpsPattern.matcher(url).matches();
|
||||
return azureDevOpsPattern.matcher(url).matches()
|
||||
|| azureSSHDevOpsPattern.matcher(url).matches();
|
||||
}
|
||||
|
||||
public AzureDevOpsUrl parse(String url) {
|
||||
Matcher matcher = azureDevOpsPattern.matcher(url);
|
||||
boolean isHTTPSUrl = azureDevOpsPattern.matcher(url).matches();
|
||||
Matcher matcher =
|
||||
isHTTPSUrl ? azureDevOpsPattern.matcher(url) : azureSSHDevOpsPattern.matcher(url);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(format("The given url %s is not a valid.", url));
|
||||
}
|
||||
|
|
@ -83,23 +93,28 @@ public class AzureDevOpsURLParser {
|
|||
project = repoName;
|
||||
}
|
||||
|
||||
String organization = matcher.group("organization");
|
||||
String branch = matcher.group("branch");
|
||||
String tag = matcher.group("tag");
|
||||
String branch = null;
|
||||
String tag = null;
|
||||
|
||||
// The url might have the following formats:
|
||||
// - https://<organization>@<host>/<organization>/<project>/_git/<repoName>
|
||||
// - https://<credentials>@<host>/<organization>/<project>/_git/<repoName>
|
||||
// For the first case we need to remove the `organization` from the url to distinguish it from
|
||||
// `credentials`
|
||||
// TODO: return empty credentials like the BitBucketUrl
|
||||
String organizationCanIgnore = matcher.group("organizationCanIgnore");
|
||||
if (!isNullOrEmpty(organization) && organization.equals(organizationCanIgnore)) {
|
||||
url = url.replace(organizationCanIgnore + "@", "");
|
||||
String organization = matcher.group("organization");
|
||||
if (isHTTPSUrl) {
|
||||
branch = matcher.group("branch");
|
||||
tag = matcher.group("tag");
|
||||
// The url might have the following formats:
|
||||
// - https://<organization>@<host>/<organization>/<project>/_git/<repoName>
|
||||
// - https://<credentials>@<host>/<organization>/<project>/_git/<repoName>
|
||||
// For the first case we need to remove the `organization` from the url to distinguish it from
|
||||
// `credentials`
|
||||
// TODO: return empty credentials like the BitBucketUrl
|
||||
String organizationCanIgnore = matcher.group("organizationCanIgnore");
|
||||
if (!isNullOrEmpty(organization) && organization.equals(organizationCanIgnore)) {
|
||||
url = url.replace(organizationCanIgnore + "@", "");
|
||||
}
|
||||
}
|
||||
|
||||
return new AzureDevOpsUrl()
|
||||
.withHostName(azureDevOpsScmApiEndpointHost)
|
||||
.setIsHTTPSUrl(isHTTPSUrl)
|
||||
.withProject(project)
|
||||
.withRepository(repoName)
|
||||
.withOrganization(organization)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl;
|
|||
*/
|
||||
public class AzureDevOpsUrl extends DefaultFactoryUrl {
|
||||
|
||||
private boolean isHTTPSUrl;
|
||||
private String hostName;
|
||||
|
||||
private String repository;
|
||||
|
|
@ -145,7 +146,17 @@ public class AzureDevOpsUrl extends DefaultFactoryUrl {
|
|||
}
|
||||
|
||||
public String getRepositoryLocation() {
|
||||
return getRepoPathJoiner().add("_git").add(repository).toString();
|
||||
if (isHTTPSUrl) {
|
||||
return getRepoPathJoiner().add("_git").add(repository).toString();
|
||||
}
|
||||
return "git@ssh."
|
||||
+ hostName.substring(8)
|
||||
+ ":v3/"
|
||||
+ organization
|
||||
+ "/"
|
||||
+ project
|
||||
+ "/"
|
||||
+ repository;
|
||||
}
|
||||
|
||||
private StringJoiner getRepoPathJoiner() {
|
||||
|
|
@ -157,6 +168,11 @@ public class AzureDevOpsUrl extends DefaultFactoryUrl {
|
|||
return hostName;
|
||||
}
|
||||
|
||||
public AzureDevOpsUrl setIsHTTPSUrl(boolean isHTTPSUrl) {
|
||||
this.isHTTPSUrl = isHTTPSUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AzureDevOpsUrl withHostName(String hostName) {
|
||||
this.hostName = "https://" + hostName;
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -109,6 +109,48 @@ public class AzureDevOpsURLParserTest {
|
|||
"main",
|
||||
null
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot.git",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo.dot",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen",
|
||||
"MyOrg",
|
||||
"MyProject",
|
||||
"MyRepo-with-hypen",
|
||||
null,
|
||||
null
|
||||
},
|
||||
{"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-", "MyOrg", "MyProject", "-", null, null},
|
||||
{"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j.git", "MyOrg", "MyProject", "-j", null, null},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain",
|
||||
"MyOrg",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* 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 java.lang.String.format;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class AzureDevOpsURLTest {
|
||||
private AzureDevOpsURLParser azureDevOpsURLParser;
|
||||
|
||||
@BeforeMethod
|
||||
protected void init() {
|
||||
azureDevOpsURLParser =
|
||||
new AzureDevOpsURLParser(mock(DevfileFilenamesProvider.class), "https://dev.azure.com/");
|
||||
}
|
||||
|
||||
@Test(dataProvider = "urlsProvider")
|
||||
public void checkDevfileLocation(String repoUrl, String fileUrl) {
|
||||
|
||||
AzureDevOpsUrl azureDevOpsUrl =
|
||||
azureDevOpsURLParser
|
||||
.parse(repoUrl)
|
||||
.withDevfileFilenames(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
assertEquals(azureDevOpsUrl.devfileFileLocations().size(), 2);
|
||||
Iterator<RemoteFactoryUrl.DevfileLocation> iterator =
|
||||
azureDevOpsUrl.devfileFileLocations().iterator();
|
||||
String location = iterator.next().location();
|
||||
assertEquals(location, format(fileUrl, "devfile.yaml"));
|
||||
assertEquals(location, format(fileUrl, "foo.bar"));
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] urlsProvider() {
|
||||
return new Object[][] {
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo.dot/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo-with-hypen/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/-",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/-j.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-j/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo.dot/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo-with-hypen/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-j/items?path=/devfile.yaml&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain&_a=contents",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=branch&version=main&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=branch&version=main&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag&_a=contents",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=tag&version=MyTag&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=tag&version=MyTag&api-version=7.0"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/_git/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyRepo/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "repoProvider")
|
||||
public void checkRepositoryLocation(String rawUrl, String repoUrl) {
|
||||
AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(rawUrl);
|
||||
assertEquals(azureDevOpsUrl.getRepositoryLocation(), repoUrl);
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] repoProvider() {
|
||||
return new Object[][] {
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/-",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/-"
|
||||
},
|
||||
{
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/-j.git",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/-j"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo",
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git",
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot.git",
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo",
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo"
|
||||
},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen",
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen"
|
||||
},
|
||||
{"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-", "git@ssh.dev.azure.com:v3/MyOrg/MyProject/-"},
|
||||
{
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j.git",
|
||||
"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain&_a=contents",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag&_a=contents",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag",
|
||||
"https://dev.azure.com/MyOrg/MyProject/_git/MyRepo"
|
||||
},
|
||||
{
|
||||
"https://MyOrg@dev.azure.com/MyOrg/_git/MyRepo",
|
||||
"https://dev.azure.com/MyOrg/MyRepo/_git/MyRepo"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import static java.util.regex.Pattern.compile;
|
|||
|
||||
import com.google.common.base.Splitter;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -50,11 +51,13 @@ public class BitbucketServerURLParser {
|
|||
private final PersonalAccessTokenManager personalAccessTokenManager;
|
||||
private static final List<String> bitbucketUrlPatternTemplates =
|
||||
List.of(
|
||||
"^(?<host>%s)/scm/~(?<user>[^/]+)/(?<repo>.*).git$",
|
||||
"^(?<host>%s)/users/(?<user>[^/]+)/repos/(?<repo>[^/]+)/browse(\\?at=(?<branch>.*))?",
|
||||
"^(?<host>%s)/users/(?<user>[^/]+)/repos/(?<repo>[^/]+)/?",
|
||||
"^(?<host>%s)/scm/(?<project>[^/~]+)/(?<repo>[^/]+).git",
|
||||
"^(?<host>%s)/projects/(?<project>[^/]+)/repos/(?<repo>[^/]+)/browse(\\?at=(?<branch>.*))?");
|
||||
"^(?<scheme>%s)://(?<host>%s)/scm/~(?<user>[^/]+)/(?<repo>.*).git$",
|
||||
"^(?<scheme>%s)://(?<host>%s)/users/(?<user>[^/]+)/repos/(?<repo>[^/]+)/browse(\\?at=(?<branch>.*))?",
|
||||
"^(?<scheme>%s)://(?<host>%s)/users/(?<user>[^/]+)/repos/(?<repo>[^/]+)/?",
|
||||
"^(?<scheme>%s)://(?<host>%s)/scm/(?<project>[^/~]+)/(?<repo>[^/]+).git",
|
||||
"^(?<scheme>%s)://(?<host>%s)/projects/(?<project>[^/]+)/repos/(?<repo>[^/]+)/browse(\\?at=(?<branch>.*))?",
|
||||
"^(?<scheme>%s)://git@(?<host>%s):(?<port>\\d*)/~(?<user>[^/]+)/(?<repo>.*).git$",
|
||||
"^(?<scheme>%s)://git@(?<host>%s):(?<port>\\d*)/(?<project>[^/]+)/(?<repo>.*).git$");
|
||||
private final List<Pattern> bitbucketUrlPatterns = new ArrayList<>();
|
||||
private static final String OAUTH_PROVIDER_NAME = "bitbucket-server";
|
||||
|
||||
|
|
@ -70,16 +73,24 @@ public class BitbucketServerURLParser {
|
|||
if (bitbucketEndpoints != null) {
|
||||
for (String bitbucketEndpoint : Splitter.on(",").split(bitbucketEndpoints)) {
|
||||
String trimmedEndpoint = StringUtils.trimEnd(bitbucketEndpoint, '/');
|
||||
URI uri = URI.create(trimmedEndpoint);
|
||||
bitbucketUrlPatternTemplates.forEach(
|
||||
t -> bitbucketUrlPatterns.add(Pattern.compile(format(t, trimmedEndpoint))));
|
||||
t -> {
|
||||
String scheme = t.contains("git@") ? "ssh" : uri.getScheme();
|
||||
String host = uri.getHost();
|
||||
bitbucketUrlPatterns.add(Pattern.compile(format(t, scheme, host)));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUserTokenPresent(String repositoryUrl) {
|
||||
String serverUrl = getServerUrl(repositoryUrl);
|
||||
URI uri = URI.create(repositoryUrl);
|
||||
String schema = uri.getScheme();
|
||||
String host = uri.getHost();
|
||||
if (bitbucketUrlPatternTemplates.stream()
|
||||
.anyMatch(t -> Pattern.compile(format(t, serverUrl)).matcher(repositoryUrl).matches())) {
|
||||
.anyMatch(t -> Pattern.compile(format(t, schema, host)).matcher(repositoryUrl).matches())) {
|
||||
try {
|
||||
Optional<PersonalAccessToken> token =
|
||||
personalAccessTokenManager.get(EnvironmentContext.getCurrent().getSubject(), serverUrl);
|
||||
|
|
@ -94,7 +105,9 @@ public class BitbucketServerURLParser {
|
|||
}
|
||||
|
||||
public boolean isValid(@NotNull String url) {
|
||||
if (!bitbucketUrlPatterns.isEmpty()) {
|
||||
if (!url.contains("://")) {
|
||||
return false;
|
||||
} else if (!bitbucketUrlPatterns.isEmpty()) {
|
||||
return bitbucketUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
|
||||
} else {
|
||||
return
|
||||
|
|
@ -126,18 +139,27 @@ public class BitbucketServerURLParser {
|
|||
}
|
||||
|
||||
private String getServerUrl(String repositoryUrl) {
|
||||
if (repositoryUrl.startsWith("ssh://git@")) {
|
||||
String substring = repositoryUrl.substring(10);
|
||||
return "https://" + substring.substring(0, substring.indexOf(":"));
|
||||
}
|
||||
return repositoryUrl.substring(
|
||||
0,
|
||||
repositoryUrl.indexOf("/scm") > 0
|
||||
? repositoryUrl.indexOf("/scm")
|
||||
: repositoryUrl.indexOf("/users") > 0
|
||||
? repositoryUrl.indexOf("/users")
|
||||
: repositoryUrl.length());
|
||||
: repositoryUrl.indexOf("/projects") > 0
|
||||
? repositoryUrl.indexOf("/projects")
|
||||
: repositoryUrl.length());
|
||||
}
|
||||
|
||||
private Optional<Matcher> getPatternMatcherByUrl(String url) {
|
||||
URI uri = URI.create(url);
|
||||
String scheme = uri.getScheme();
|
||||
String host = uri.getHost();
|
||||
return bitbucketUrlPatternTemplates.stream()
|
||||
.map(t -> compile(format(t, getServerUrl(url))).matcher(url))
|
||||
.map(t -> compile(format(t, scheme, host)).matcher(url))
|
||||
.filter(Matcher::matches)
|
||||
.findAny();
|
||||
}
|
||||
|
|
@ -174,7 +196,14 @@ public class BitbucketServerURLParser {
|
|||
}
|
||||
|
||||
private BitbucketServerUrl parse(Matcher matcher) {
|
||||
String scheme = matcher.group("scheme");
|
||||
String host = matcher.group("host");
|
||||
String port = null;
|
||||
try {
|
||||
port = matcher.group("port");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// keep port with null, as the pattern doesn't have the port group
|
||||
}
|
||||
String user = null;
|
||||
String project = null;
|
||||
try {
|
||||
|
|
@ -191,7 +220,9 @@ public class BitbucketServerURLParser {
|
|||
}
|
||||
|
||||
return new BitbucketServerUrl()
|
||||
.withScheme(scheme)
|
||||
.withHostName(host)
|
||||
.withPort(port)
|
||||
.withProject(project)
|
||||
.withUser(user)
|
||||
.withRepository(repoName)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ public class BitbucketServerUrl extends DefaultFactoryUrl {
|
|||
/** Hostname of bitbucket URL */
|
||||
private String hostName;
|
||||
|
||||
private String scheme;
|
||||
private String port;
|
||||
|
||||
/** Project part of bitbucket URL */
|
||||
private String project;
|
||||
|
||||
|
|
@ -54,6 +57,11 @@ public class BitbucketServerUrl extends DefaultFactoryUrl {
|
|||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderUrl() {
|
||||
return (scheme.equals("ssh") ? "https" : scheme) + "://" + hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hostname of this bitbucket server url
|
||||
*
|
||||
|
|
@ -68,6 +76,16 @@ public class BitbucketServerUrl extends DefaultFactoryUrl {
|
|||
return this;
|
||||
}
|
||||
|
||||
public BitbucketServerUrl withScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BitbucketServerUrl withPort(String port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets project of this bitbucket server url
|
||||
*
|
||||
|
|
@ -171,7 +189,7 @@ public class BitbucketServerUrl extends DefaultFactoryUrl {
|
|||
public String rawFileLocation(String fileName) {
|
||||
StringJoiner joiner =
|
||||
new StringJoiner("/")
|
||||
.add(hostName)
|
||||
.add((scheme.equals("ssh") ? "https" : scheme) + "://" + hostName)
|
||||
.add("rest/api/1.0")
|
||||
.add(!isNullOrEmpty(user) && isNullOrEmpty(project) ? "users" : "projects")
|
||||
.add(firstNonNull(user, project))
|
||||
|
|
@ -192,7 +210,14 @@ public class BitbucketServerUrl extends DefaultFactoryUrl {
|
|||
* @return location of the repository.
|
||||
*/
|
||||
protected String repositoryLocation() {
|
||||
return hostName
|
||||
if (scheme.equals("ssh")) {
|
||||
return String.format(
|
||||
"%s://git@%s:%s/%s/%s.git",
|
||||
scheme, hostName, port, (isNullOrEmpty(user) ? project : "~" + user), repository);
|
||||
}
|
||||
return scheme
|
||||
+ "://"
|
||||
+ hostName
|
||||
+ "/scm/"
|
||||
+ (isNullOrEmpty(user) ? project : "~" + user)
|
||||
+ "/"
|
||||
|
|
|
|||
|
|
@ -29,18 +29,21 @@ import org.testng.annotations.Test;
|
|||
@Listeners(MockitoTestNGListener.class)
|
||||
public class BitbucketServerAuthorizingFileContentProviderTest {
|
||||
|
||||
public static final String TEST_HOSTNAME = "https://foo.bar";
|
||||
public static final String TEST_HOSTNAME = "foo.bar";
|
||||
public static final String TEST_SCHEME = "https";
|
||||
@Mock private URLFetcher urlFetcher;
|
||||
@Mock private PersonalAccessTokenManager personalAccessTokenManager;
|
||||
|
||||
@Test
|
||||
public void shouldFetchContentWithTokenIfPresent() throws Exception {
|
||||
BitbucketServerUrl url = new BitbucketServerUrl().withHostName(TEST_HOSTNAME);
|
||||
BitbucketServerUrl url =
|
||||
new BitbucketServerUrl().withHostName(TEST_HOSTNAME).withScheme(TEST_SCHEME);
|
||||
BitbucketServerAuthorizingFileContentProvider fileContentProvider =
|
||||
new BitbucketServerAuthorizingFileContentProvider(
|
||||
url, urlFetcher, personalAccessTokenManager);
|
||||
|
||||
PersonalAccessToken token = new PersonalAccessToken(TEST_HOSTNAME, "user1", "token");
|
||||
PersonalAccessToken token =
|
||||
new PersonalAccessToken(TEST_SCHEME + "://" + TEST_HOSTNAME, "user1", "token");
|
||||
when(personalAccessTokenManager.get(anyString())).thenReturn(token);
|
||||
|
||||
String fileURL = "https://foo.bar/scm/repo/.devfile";
|
||||
|
|
@ -54,13 +57,15 @@ public class BitbucketServerAuthorizingFileContentProviderTest {
|
|||
|
||||
@Test
|
||||
public void shouldFetchTokenIfNotYetPresent() throws Exception {
|
||||
BitbucketServerUrl url = new BitbucketServerUrl().withHostName(TEST_HOSTNAME);
|
||||
BitbucketServerUrl url =
|
||||
new BitbucketServerUrl().withHostName(TEST_HOSTNAME).withScheme(TEST_SCHEME);
|
||||
BitbucketServerAuthorizingFileContentProvider fileContentProvider =
|
||||
new BitbucketServerAuthorizingFileContentProvider(
|
||||
url, urlFetcher, personalAccessTokenManager);
|
||||
|
||||
PersonalAccessToken token = new PersonalAccessToken(TEST_HOSTNAME, "user1", "token");
|
||||
when(personalAccessTokenManager.get(eq(TEST_HOSTNAME))).thenReturn(token);
|
||||
PersonalAccessToken token =
|
||||
new PersonalAccessToken(TEST_SCHEME + "://" + TEST_HOSTNAME, "user1", "token");
|
||||
when(personalAccessTokenManager.get(eq(TEST_SCHEME + "://" + TEST_HOSTNAME))).thenReturn(token);
|
||||
|
||||
String fileURL = "https://foo.bar/scm/repo/.devfile";
|
||||
|
||||
|
|
@ -68,7 +73,7 @@ public class BitbucketServerAuthorizingFileContentProviderTest {
|
|||
fileContentProvider.fetchContent(fileURL);
|
||||
|
||||
// then
|
||||
verify(personalAccessTokenManager).get(eq(TEST_HOSTNAME));
|
||||
verify(personalAccessTokenManager).get(eq(TEST_SCHEME + "://" + TEST_HOSTNAME));
|
||||
verify(urlFetcher).fetch(eq(fileURL), eq("Bearer token"));
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +83,7 @@ public class BitbucketServerAuthorizingFileContentProviderTest {
|
|||
BitbucketServerUrl url =
|
||||
new BitbucketServerUrl()
|
||||
.withHostName(TEST_HOSTNAME)
|
||||
.withScheme(TEST_SCHEME)
|
||||
.withProject("proj")
|
||||
.withRepository("repo")
|
||||
.withDevfileFilenames(Collections.singletonList(".devfile"));
|
||||
|
|
@ -87,7 +93,8 @@ public class BitbucketServerAuthorizingFileContentProviderTest {
|
|||
BitbucketServerAuthorizingFileContentProvider fileContentProvider =
|
||||
new BitbucketServerAuthorizingFileContentProvider(
|
||||
url, urlFetcher, personalAccessTokenManager);
|
||||
PersonalAccessToken token = new PersonalAccessToken(TEST_HOSTNAME, "user1", "token");
|
||||
PersonalAccessToken token =
|
||||
new PersonalAccessToken(TEST_SCHEME + "://" + TEST_HOSTNAME, "user1", "token");
|
||||
when(personalAccessTokenManager.get(anyString())).thenReturn(token);
|
||||
|
||||
// when
|
||||
|
|
|
|||
|
|
@ -83,6 +83,24 @@ public class BitbucketServerURLParserTest {
|
|||
assertEquals(bitbucketServerUrl.getBranch(), branch);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "parsing")
|
||||
public void shouldParseWithoutPredefinedEndpoint(
|
||||
String url, String user, String project, String repository, String branch) {
|
||||
// given
|
||||
bitbucketURLParser =
|
||||
new BitbucketServerURLParser(
|
||||
null, devfileFilenamesProvider, oAuthAPI, mock(PersonalAccessTokenManager.class));
|
||||
|
||||
// when
|
||||
BitbucketServerUrl bitbucketServerUrl = bitbucketURLParser.parse(url);
|
||||
|
||||
// then
|
||||
assertEquals(bitbucketServerUrl.getUser(), user);
|
||||
assertEquals(bitbucketServerUrl.getProject(), project);
|
||||
assertEquals(bitbucketServerUrl.getRepository(), repository);
|
||||
assertEquals(bitbucketServerUrl.getBranch(), branch);
|
||||
}
|
||||
|
||||
@Test(
|
||||
expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp =
|
||||
|
|
@ -134,6 +152,8 @@ public class BitbucketServerURLParserTest {
|
|||
{"https://bitbucket.2mcl.com/users/user/repos/repo"},
|
||||
{"https://bitbucket.2mcl.com/users/user/repos/repo/"},
|
||||
{"https://bbkt.com/scm/project/test1.git"},
|
||||
{"ssh://git@bitbucket.2mcl.com:12345/~user/repo.git"},
|
||||
{"ssh://git@bitbucket.2mcl.com:12345/project/test1.git"}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +161,8 @@ public class BitbucketServerURLParserTest {
|
|||
public Object[][] expectedParsing() {
|
||||
return new Object[][] {
|
||||
{"https://bitbucket.2mcl.com/scm/project/test1.git", null, "project", "test1", null},
|
||||
{"ssh://git@bitbucket.2mcl.com:12345/project/test1.git", null, "project", "test1", null},
|
||||
{"ssh://git@bitbucket.2mcl.com:12345/~user/test1.git", "user", null, "test1", null},
|
||||
{
|
||||
"https://bitbucket.2mcl.com/projects/project/repos/test1/browse?at=refs%2Fheads%2Fbranch",
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.bitbucket;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
|
||||
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;
|
||||
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class BitbucketServerURLTest {
|
||||
private BitbucketServerURLParser bitbucketServerURLParser;
|
||||
@Mock private DevfileFilenamesProvider devfileFilenamesProvider;
|
||||
|
||||
@BeforeMethod
|
||||
protected void init() {
|
||||
when(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
.thenReturn(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
bitbucketServerURLParser =
|
||||
new BitbucketServerURLParser(
|
||||
"https://bitbucket.net",
|
||||
devfileFilenamesProvider,
|
||||
mock(OAuthAPI.class),
|
||||
mock(PersonalAccessTokenManager.class));
|
||||
}
|
||||
|
||||
@Test(dataProvider = "urlsProvider")
|
||||
public void checkDevfileLocation(String repoUrl, String fileUrl) {
|
||||
lenient()
|
||||
.when(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
.thenReturn(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
|
||||
BitbucketServerUrl gitlabUrl = bitbucketServerURLParser.parse(repoUrl);
|
||||
assertEquals(gitlabUrl.devfileFileLocations().size(), 2);
|
||||
Iterator<RemoteFactoryUrl.DevfileLocation> iterator =
|
||||
gitlabUrl.devfileFileLocations().iterator();
|
||||
assertEquals(iterator.next().location(), format(fileUrl, "devfile.yaml"));
|
||||
assertEquals(iterator.next().location(), format(fileUrl, "foo.bar"));
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] urlsProvider() {
|
||||
return new Object[][] {
|
||||
{
|
||||
"https://bitbucket.net/scm/~user/repo.git",
|
||||
"https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s"
|
||||
},
|
||||
{
|
||||
"https://bitbucket.net/users/user/repos/repo/browse?at=branch",
|
||||
"https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s?at=branch"
|
||||
},
|
||||
{
|
||||
"https://bitbucket.net/users/user/repos/repo",
|
||||
"https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s"
|
||||
},
|
||||
{
|
||||
"https://bitbucket.net/scm/project/repo.git",
|
||||
"https://bitbucket.net/rest/api/1.0/projects/project/repos/repo/raw/%s"
|
||||
},
|
||||
{
|
||||
"https://bitbucket.net/projects/project/repos/repo/browse?at=branch",
|
||||
"https://bitbucket.net/rest/api/1.0/projects/project/repos/repo/raw/%s?at=branch"
|
||||
},
|
||||
{
|
||||
"ssh://git@bitbucket.net:12345/project/repo.git",
|
||||
"https://bitbucket.net/rest/api/1.0/projects/project/repos/repo/raw/%s"
|
||||
},
|
||||
{
|
||||
"ssh://git@bitbucket.net:12345/~user/repo.git",
|
||||
"https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "repoProvider")
|
||||
public void checkRepositoryLocation(String rawUrl, String repoUrl) {
|
||||
BitbucketServerUrl bitbucketServerUrl = bitbucketServerURLParser.parse(rawUrl);
|
||||
assertEquals(bitbucketServerUrl.repositoryLocation(), repoUrl);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "urlsProvider")
|
||||
public void shouldReturnProviderUrl(String repoUrl, String ignored) {
|
||||
// when
|
||||
BitbucketServerUrl bitbucketServerUrl = bitbucketServerURLParser.parse(repoUrl);
|
||||
|
||||
// then
|
||||
assertEquals(bitbucketServerUrl.getProviderUrl(), "https://bitbucket.net");
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] repoProvider() {
|
||||
return new Object[][] {
|
||||
{"https://bitbucket.net/scm/~user/repo.git", "https://bitbucket.net/scm/~user/repo.git"},
|
||||
{
|
||||
"https://bitbucket.net/users/user/repos/repo/browse?at=branch",
|
||||
"https://bitbucket.net/scm/~user/repo.git"
|
||||
},
|
||||
{"https://bitbucket.net/users/user/repos/repo", "https://bitbucket.net/scm/~user/repo.git"},
|
||||
{"https://bitbucket.net/scm/project/repo.git", "https://bitbucket.net/scm/project/repo.git"},
|
||||
{
|
||||
"https://bitbucket.net/projects/project/repos/repo/browse?at=branch",
|
||||
"https://bitbucket.net/scm/project/repo.git"
|
||||
},
|
||||
{
|
||||
"ssh://git@bitbucket.net:12345/project/repo.git",
|
||||
"ssh://git@bitbucket.net:12345/project/repo.git"
|
||||
},
|
||||
{
|
||||
"ssh://git@bitbucket.net:12345/~user/repo.git",
|
||||
"ssh://git@bitbucket.net:12345/~user/repo.git"
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -36,29 +36,39 @@ public class BitbucketURLParser {
|
|||
Pattern.compile(
|
||||
"^https?://(?<username>[^/@]+)?@?bitbucket\\.org/(?<workspaceId>[^/]+)/(?<repoName>[^/]+)/?(\\.git)?(/(src|branch)/(?<branchName>[^/]+)/?)?$");
|
||||
|
||||
protected static final Pattern BITBUCKET_SSH_PATTERN =
|
||||
Pattern.compile("^git@bitbucket.org:(?<workspaceId>.*)/(?<repoName>.*)$");
|
||||
|
||||
public boolean isValid(@NotNull String url) {
|
||||
return BITBUCKET_PATTERN.matcher(url).matches();
|
||||
return BITBUCKET_PATTERN.matcher(url).matches() || BITBUCKET_SSH_PATTERN.matcher(url).matches();
|
||||
}
|
||||
|
||||
public BitbucketUrl parse(String url) {
|
||||
// Apply bitbucket url to the regexp
|
||||
Matcher matcher = BITBUCKET_PATTERN.matcher(url);
|
||||
boolean isHTTPSUrl = BITBUCKET_PATTERN.matcher(url).matches();
|
||||
Matcher matcher =
|
||||
isHTTPSUrl ? BITBUCKET_PATTERN.matcher(url) : BITBUCKET_SSH_PATTERN.matcher(url);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("The given bitbucket url %s is not a valid URL bitbucket url. ", url));
|
||||
}
|
||||
|
||||
String username = matcher.group("username");
|
||||
String workspaceId = matcher.group("workspaceId");
|
||||
String repoName = matcher.group("repoName");
|
||||
if (repoName.matches("^[\\w-][\\w.-]*?\\.git$")) {
|
||||
repoName = repoName.substring(0, repoName.length() - 4);
|
||||
}
|
||||
String workspaceId = matcher.group("workspaceId");
|
||||
String branchName = matcher.group("branchName");
|
||||
String username = null;
|
||||
String branchName = null;
|
||||
if (isHTTPSUrl) {
|
||||
username = matcher.group("username");
|
||||
branchName = matcher.group("branchName");
|
||||
}
|
||||
|
||||
return new BitbucketUrl()
|
||||
.withUsername(username)
|
||||
.withRepository(repoName)
|
||||
.setIsHTTPSUrl(isHTTPSUrl)
|
||||
.withBranch(branchName)
|
||||
.withWorkspaceId(workspaceId)
|
||||
.withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ public class BitbucketUrl extends DefaultFactoryUrl {
|
|||
/** Repository part of the URL. */
|
||||
private String repository;
|
||||
|
||||
private boolean isHTTPSUrl;
|
||||
|
||||
/** Branch name */
|
||||
private String branch;
|
||||
|
||||
|
|
@ -81,6 +83,11 @@ public class BitbucketUrl extends DefaultFactoryUrl {
|
|||
return this;
|
||||
}
|
||||
|
||||
public BitbucketUrl setIsHTTPSUrl(boolean isHTTPSUrl) {
|
||||
this.isHTTPSUrl = isHTTPSUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getWorkspaceId() {
|
||||
return workspaceId;
|
||||
}
|
||||
|
|
@ -164,13 +171,16 @@ public class BitbucketUrl extends DefaultFactoryUrl {
|
|||
* @return location of the repository.
|
||||
*/
|
||||
protected String repositoryLocation() {
|
||||
return "https://"
|
||||
+ (this.username != null ? this.username + '@' : "")
|
||||
+ HOSTNAME
|
||||
+ "/"
|
||||
+ this.workspaceId
|
||||
+ "/"
|
||||
+ this.repository
|
||||
+ ".git";
|
||||
if (isHTTPSUrl) {
|
||||
return "https://"
|
||||
+ (this.username != null ? this.username + '@' : "")
|
||||
+ HOSTNAME
|
||||
+ "/"
|
||||
+ workspaceId
|
||||
+ "/"
|
||||
+ repository
|
||||
+ ".git";
|
||||
}
|
||||
return String.format("git@%s:%s/%s.git", HOSTNAME, workspaceId, repository);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,15 @@ public class BitbucketURLParserTest {
|
|||
{"https://bitbucket.org/eclipse/che.with.dots.git"},
|
||||
{"https://bitbucket.org/eclipse/che-with-hyphen"},
|
||||
{"https://bitbucket.org/eclipse/che-with-hyphen.git"},
|
||||
{"git@bitbucket.org:eclipse/che"},
|
||||
{"git@bitbucket.org:eclipse/che123"},
|
||||
{"git@bitbucket.org:eclipse/che/"},
|
||||
{"git@bitbucket.org:eclipse/che/src/4.2.x"},
|
||||
{"git@bitbucket.org:eclipse/che/src/master/"},
|
||||
{"git@bitbucket.org:eclipse/che.git"},
|
||||
{"git@bitbucket.org:eclipse/che.with.dots.git"},
|
||||
{"git@bitbucket.org:eclipse/che-with-hyphen"},
|
||||
{"git@bitbucket.org:eclipse/che-with-hyphen.git"},
|
||||
{"https://username@bitbucket.org/eclipse/che"},
|
||||
{"https://username@bitbucket.org/eclipse/che123"},
|
||||
{"https://username@bitbucket.org/eclipse/che/"},
|
||||
|
|
@ -102,6 +111,16 @@ public class BitbucketURLParserTest {
|
|||
{"https://bitbucket.org/eclipse/che-with-hyphen.git", "eclipse", "che-with-hyphen", null},
|
||||
{"https://bitbucket.org/eclipse/che/", "eclipse", "che", null},
|
||||
{"https://bitbucket.org/eclipse/repositorygit", "eclipse", "repositorygit", null},
|
||||
{"git@bitbucket.org:eclipse/che", "eclipse", "che", null},
|
||||
{"git@bitbucket.org:eclipse/che123", "eclipse", "che123", null},
|
||||
{"git@bitbucket.org:eclipse/che.git", "eclipse", "che", null},
|
||||
{"git@bitbucket.org:eclipse/che.with.dot.git", "eclipse", "che.with.dot", null},
|
||||
{"git@bitbucket.org:eclipse/-.git", "eclipse", "-", null},
|
||||
{"git@bitbucket.org:eclipse/-j.git", "eclipse", "-j", null},
|
||||
{"git@bitbucket.org:eclipse/-", "eclipse", "-", null},
|
||||
{"git@bitbucket.org:eclipse/che-with-hyphen", "eclipse", "che-with-hyphen", null},
|
||||
{"git@bitbucket.org:eclipse/che-with-hyphen.git", "eclipse", "che-with-hyphen", null},
|
||||
{"git@bitbucket.org:eclipse/repositorygit", "eclipse", "repositorygit", null},
|
||||
{"https://bitbucket.org/eclipse/che/src/4.2.x", "eclipse", "che", "4.2.x"},
|
||||
{"https://bitbucket.org/eclipse/che/src/master/", "eclipse", "che", "master"}
|
||||
};
|
||||
|
|
@ -117,7 +136,15 @@ public class BitbucketURLParserTest {
|
|||
{"https://bitbucket.org/eclipse/івапівап.git", "івапівап.git"},
|
||||
{"https://bitbucket.org/eclipse/ ", " "},
|
||||
{"https://bitbucket.org/eclipse/.", "."},
|
||||
{"https://bitbucket.org/eclipse/ .git", " .git"}
|
||||
{"https://bitbucket.org/eclipse/ .git", " .git"},
|
||||
{"git@bitbucket.org:eclipse/che .git", "che .git"},
|
||||
{"git@bitbucket.org:eclipse/.git", ".git"},
|
||||
{"git@bitbucket.org:eclipse/myB@dR&pository.git", "myB@dR&pository.git"},
|
||||
{"git@bitbucket.org:eclipse/.", "."},
|
||||
{"git@bitbucket.org:eclipse/івапівап.git", "івапівап.git"},
|
||||
{"git@bitbucket.org:eclipse/ ", " "},
|
||||
{"git@bitbucket.org:eclipse/.", "."},
|
||||
{"git@bitbucket.org:eclipse/ .git", " .git"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,22 @@ public class BitbucketUrlTest {
|
|||
assertEquals(iterator.next().location(), "https://bitbucket.org/eclipse/che/raw/HEAD/foo.bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnDevfileLocationBySSHUrl() {
|
||||
bitbucketUrl = bitbucketURLParser.parse("git@bitbucket.org:eclipse/che");
|
||||
|
||||
lenient()
|
||||
.when(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
.thenReturn(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
|
||||
assertEquals(bitbucketUrl.devfileFileLocations().size(), 2);
|
||||
Iterator<DevfileLocation> iterator = bitbucketUrl.devfileFileLocations().iterator();
|
||||
assertEquals(
|
||||
iterator.next().location(), "https://bitbucket.org/eclipse/che/raw/HEAD/devfile.yaml");
|
||||
|
||||
assertEquals(iterator.next().location(), "https://bitbucket.org/eclipse/che/raw/HEAD/foo.bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnEmptyCredentials() {
|
||||
// when
|
||||
|
|
@ -77,4 +93,11 @@ public class BitbucketUrlTest {
|
|||
public void checkRepositoryLocation() {
|
||||
assertEquals(bitbucketUrl.repositoryLocation(), "https://bitbucket.org/eclipse/che.git");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnRepositoryLocationBySSHUrl() {
|
||||
bitbucketUrl = bitbucketURLParser.parse("git@bitbucket.org:eclipse/che");
|
||||
|
||||
assertEquals(bitbucketUrl.repositoryLocation(), "git@bitbucket.org:eclipse/che.git");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
<?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.73.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>
|
||||
|
|
@ -1,31 +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.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +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.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +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.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 "";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +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.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +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.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +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.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"},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -20,6 +20,7 @@ import static org.eclipse.che.api.factory.server.github.GithubApiClient.GITHUB_S
|
|||
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
|
|
@ -63,6 +64,8 @@ public class GithubURLParser {
|
|||
*/
|
||||
private final Pattern githubPattern;
|
||||
|
||||
private final Pattern githubSSHPattern;
|
||||
|
||||
private final boolean disableSubdomainIsolation;
|
||||
|
||||
@Inject
|
||||
|
|
@ -101,10 +104,14 @@ public class GithubURLParser {
|
|||
format(
|
||||
"^%s/(?<repoUser>[^/]+)/(?<repoName>[^/]++)((/)|(?:/tree/(?<branchName>.++))|(/pull/(?<pullRequestId>\\d++)))?$",
|
||||
endpoint));
|
||||
this.githubSSHPattern =
|
||||
compile(format("^git@%s:(?<repoUser>.*)/(?<repoName>.*)$", URI.create(endpoint).getHost()));
|
||||
}
|
||||
|
||||
public boolean isValid(@NotNull String url) {
|
||||
return githubPattern.matcher(trimEnd(url, '/')).matches();
|
||||
String trimmedUrl = trimEnd(url, '/');
|
||||
return githubPattern.matcher(trimmedUrl).matches()
|
||||
|| githubSSHPattern.matcher(trimmedUrl).matches();
|
||||
}
|
||||
|
||||
public GithubUrl parseWithoutAuthentication(String url) throws ApiException {
|
||||
|
|
@ -116,13 +123,11 @@ public class GithubURLParser {
|
|||
}
|
||||
|
||||
private GithubUrl parse(String url, boolean authenticationRequired) throws ApiException {
|
||||
// Apply github url to the regexp
|
||||
Matcher matcher = githubPattern.matcher(url);
|
||||
boolean isHTTPSUrl = githubPattern.matcher(url).matches();
|
||||
Matcher matcher = isHTTPSUrl ? githubPattern.matcher(url) : githubSSHPattern.matcher(url);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
format(
|
||||
"The given github url %s is not a valid URL github url. It should start with https://github.com/<user>/<repo>",
|
||||
url));
|
||||
format("The given url %s is not a valid github URL. ", url));
|
||||
}
|
||||
|
||||
String serverUrl =
|
||||
|
|
@ -135,9 +140,12 @@ public class GithubURLParser {
|
|||
repoName = repoName.substring(0, repoName.length() - 4);
|
||||
}
|
||||
|
||||
String branchName = matcher.group("branchName");
|
||||
|
||||
String pullRequestId = matcher.group("pullRequestId");
|
||||
String branchName = null;
|
||||
String pullRequestId = null;
|
||||
if (isHTTPSUrl) {
|
||||
branchName = matcher.group("branchName");
|
||||
pullRequestId = matcher.group("pullRequestId");
|
||||
}
|
||||
|
||||
if (pullRequestId != null) {
|
||||
GithubPullRequest pullRequest =
|
||||
|
|
@ -169,6 +177,7 @@ public class GithubURLParser {
|
|||
return new GithubUrl()
|
||||
.withUsername(repoUser)
|
||||
.withRepository(repoName)
|
||||
.setIsHTTPSUrl(isHTTPSUrl)
|
||||
.withServerUrl(serverUrl)
|
||||
.withDisableSubdomainIsolation(disableSubdomainIsolation)
|
||||
.withBranch(branchName)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ public class GithubUrl extends DefaultFactoryUrl {
|
|||
/** Username part of github URL */
|
||||
private String username;
|
||||
|
||||
private boolean isHTTPSUrl = true;
|
||||
|
||||
/** Repository part of the URL. */
|
||||
private String repository;
|
||||
|
||||
|
|
@ -78,6 +80,11 @@ public class GithubUrl extends DefaultFactoryUrl {
|
|||
return this;
|
||||
}
|
||||
|
||||
public GithubUrl setIsHTTPSUrl(boolean isHTTPSUrl) {
|
||||
this.isHTTPSUrl = isHTTPSUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets repository of this github url
|
||||
*
|
||||
|
|
@ -209,8 +216,17 @@ public class GithubUrl extends DefaultFactoryUrl {
|
|||
* @return location of the repository.
|
||||
*/
|
||||
protected String repositoryLocation() {
|
||||
return (isNullOrEmpty(serverUrl) ? HOSTNAME : serverUrl)
|
||||
+ "/"
|
||||
if (isHTTPSUrl) {
|
||||
return (isNullOrEmpty(serverUrl) ? HOSTNAME : serverUrl)
|
||||
+ "/"
|
||||
+ this.username
|
||||
+ "/"
|
||||
+ this.repository
|
||||
+ ".git";
|
||||
}
|
||||
return "git@"
|
||||
+ getHostName().substring(getHostName().indexOf("://") + 3)
|
||||
+ ":"
|
||||
+ this.username
|
||||
+ "/"
|
||||
+ this.repository
|
||||
|
|
|
|||
|
|
@ -108,7 +108,13 @@ public class GithubURLParserTest {
|
|||
{"https://github.com/eclipse/che.git"},
|
||||
{"https://github.com/eclipse/che.with.dots.git"},
|
||||
{"https://github.com/eclipse/che-with-hyphen"},
|
||||
{"https://github.com/eclipse/che-with-hyphen.git"}
|
||||
{"https://github.com/eclipse/che-with-hyphen.git"},
|
||||
{"git@github.com:eclipse/che.git)"},
|
||||
{"git@github.com:eclipse/che)"},
|
||||
{"git@github.com:eclipse/che123)"},
|
||||
{"git@github.com:eclipse/che.with.dots.git)"},
|
||||
{"git@github.com:eclipse/che-with-hyphen)"},
|
||||
{"git@github.com:eclipse/che-with-hyphen.git)"}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +139,18 @@ public class GithubURLParserTest {
|
|||
"eclipse",
|
||||
"che",
|
||||
"branch/with/slash"
|
||||
}
|
||||
},
|
||||
{"git@github.com:eclipse/che", "eclipse", "che", null},
|
||||
{"git@github.com:eclipse/che123", "eclipse", "che123", null},
|
||||
{"git@github.com:eclipse/che.git", "eclipse", "che", null},
|
||||
{"git@github.com:eclipse/che.with.dot.git", "eclipse", "che.with.dot", null},
|
||||
{"git@github.com:eclipse/-.git", "eclipse", "-", null},
|
||||
{"git@github.com:eclipse/-j.git", "eclipse", "-j", null},
|
||||
{"git@github.com:eclipse/-", "eclipse", "-", null},
|
||||
{"git@github.com:eclipse/che-with-hyphen", "eclipse", "che-with-hyphen", null},
|
||||
{"git@github.com:eclipse/che-with-hyphen.git", "eclipse", "che-with-hyphen", null},
|
||||
{"git@github.com:eclipse/che/", "eclipse", "che", null},
|
||||
{"git@github.com:eclipse/repositorygit", "eclipse", "repositorygit", null},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +164,15 @@ public class GithubURLParserTest {
|
|||
{"https://github.com/eclipse/івапівап.git", "івапівап.git"},
|
||||
{"https://github.com/eclipse/ ", " "},
|
||||
{"https://github.com/eclipse/.", "."},
|
||||
{"https://github.com/eclipse/ .git", " .git"}
|
||||
{"https://github.com/eclipse/ .git", " .git"},
|
||||
{"git@github.com:eclipse/che .git", "che .git"},
|
||||
{"git@github.com:eclipse/.git", ".git"},
|
||||
{"git@github.com:eclipse/myB@dR&pository.git", "myB@dR&pository.git"},
|
||||
{"git@github.com:eclipse/.", "."},
|
||||
{"git@github.com:eclipse/івапівап.git", "івапівап.git"},
|
||||
{"git@github.com:eclipse/ ", " "},
|
||||
{"git@github.com:eclipse/.", "."},
|
||||
{"git@github.com:eclipse/ .git", " .git"}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,34 @@ public class GithubUrlTest {
|
|||
iterator.next().location(), "https://raw.githubusercontent.com/eclipse/che/HEAD/foo.bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnDevfileLocationFromSSHUrl() throws Exception {
|
||||
DevfileFilenamesProvider devfileFilenamesProvider = mock(DevfileFilenamesProvider.class);
|
||||
|
||||
/** Parser used to create the url. */
|
||||
GithubURLParser githubUrlParser =
|
||||
new GithubURLParser(
|
||||
mock(PersonalAccessTokenManager.class),
|
||||
devfileFilenamesProvider,
|
||||
githubApiClient,
|
||||
null,
|
||||
false);
|
||||
|
||||
when(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
.thenReturn(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
|
||||
GithubUrl githubUrl = githubUrlParser.parse("git@github.com:eclipse/che");
|
||||
|
||||
assertEquals(githubUrl.devfileFileLocations().size(), 2);
|
||||
Iterator<DevfileLocation> iterator = githubUrl.devfileFileLocations().iterator();
|
||||
assertEquals(
|
||||
iterator.next().location(),
|
||||
"https://raw.githubusercontent.com/eclipse/che/HEAD/devfile.yaml");
|
||||
|
||||
assertEquals(
|
||||
iterator.next().location(), "https://raw.githubusercontent.com/eclipse/che/HEAD/foo.bar");
|
||||
}
|
||||
|
||||
/** Check the original repository */
|
||||
@Test
|
||||
public void checkRepositoryLocation() throws Exception {
|
||||
|
|
@ -86,6 +114,27 @@ public class GithubUrlTest {
|
|||
assertEquals(githubUrl.repositoryLocation(), "https://github.com/eclipse/che.git");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnRepositoryLocationFromSSHUrl() throws Exception {
|
||||
DevfileFilenamesProvider devfileFilenamesProvider = mock(DevfileFilenamesProvider.class);
|
||||
|
||||
/** Parser used to create the url. */
|
||||
GithubURLParser githubUrlParser =
|
||||
new GithubURLParser(
|
||||
mock(PersonalAccessTokenManager.class),
|
||||
devfileFilenamesProvider,
|
||||
githubApiClient,
|
||||
null,
|
||||
false);
|
||||
|
||||
when(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
.thenReturn(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
|
||||
GithubUrl githubUrl = githubUrlParser.parse("git@github.com:eclipse/che.git");
|
||||
|
||||
assertEquals(githubUrl.repositoryLocation(), "git@github.com:eclipse/che.git");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawFileLocationWithDefaultBranchName() {
|
||||
String file = ".che/che-theia-plugins.yaml";
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
*/
|
||||
package org.eclipse.che.api.factory.server.gitlab;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static java.net.URLEncoder.encode;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -37,6 +37,9 @@ public class GitlabUrl extends DefaultFactoryUrl {
|
|||
/** Hostname of the gitlab URL */
|
||||
private String hostName;
|
||||
|
||||
/** Scheme of the gitlab URL */
|
||||
private String scheme;
|
||||
|
||||
/** Project part of the gitlab URL */
|
||||
private String project;
|
||||
|
||||
|
|
@ -63,6 +66,11 @@ public class GitlabUrl extends DefaultFactoryUrl {
|
|||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderUrl() {
|
||||
return (isNullOrEmpty(scheme) ? "https" : scheme) + "://" + hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hostname of this gitlab url
|
||||
*
|
||||
|
|
@ -77,6 +85,11 @@ public class GitlabUrl extends DefaultFactoryUrl {
|
|||
return this;
|
||||
}
|
||||
|
||||
public GitlabUrl withScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets project of this bitbucket url
|
||||
*
|
||||
|
|
@ -120,7 +133,7 @@ public class GitlabUrl extends DefaultFactoryUrl {
|
|||
}
|
||||
|
||||
protected GitlabUrl withBranch(String branch) {
|
||||
if (!Strings.isNullOrEmpty(branch)) {
|
||||
if (!isNullOrEmpty(branch)) {
|
||||
this.branch = branch;
|
||||
}
|
||||
return this;
|
||||
|
|
@ -158,7 +171,7 @@ public class GitlabUrl extends DefaultFactoryUrl {
|
|||
public String rawFileLocation(String fileName) {
|
||||
String resultUrl =
|
||||
new StringJoiner("/")
|
||||
.add(hostName)
|
||||
.add((isNullOrEmpty(scheme) ? "https" : scheme) + "://" + hostName)
|
||||
.add("api/v4/projects")
|
||||
// use URL-encoded path to the project as a selector instead of id
|
||||
.add(encode(subGroups, Charsets.UTF_8))
|
||||
|
|
@ -180,6 +193,9 @@ public class GitlabUrl extends DefaultFactoryUrl {
|
|||
* @return location of the repository.
|
||||
*/
|
||||
protected String repositoryLocation() {
|
||||
return hostName + "/" + subGroups + ".git";
|
||||
if (isNullOrEmpty(scheme)) {
|
||||
return "git@" + hostName + ":" + subGroups + ".git";
|
||||
}
|
||||
return scheme + "://" + hostName + "/" + subGroups + ".git";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
|
|||
|
||||
import com.google.common.base.Splitter;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -46,8 +47,10 @@ public class GitlabUrlParser {
|
|||
private final PersonalAccessTokenManager personalAccessTokenManager;
|
||||
private static final List<String> gitlabUrlPatternTemplates =
|
||||
List.of(
|
||||
"^(?<host>%s)/(?<subgroups>([^/]++/?)+)/-/tree/(?<branch>.++)(/)?",
|
||||
"^(?<host>%s)/(?<subgroups>.*)"); // a wider one, should be the last in the
|
||||
"^(?<scheme>%s)://(?<host>%s)/(?<subgroups>([^/]++/?)+)/-/tree/(?<branch>.++)(/)?",
|
||||
"^(?<scheme>%s)://(?<host>%s)/(?<subgroups>.*)"); // a wider one, should be the last in
|
||||
// the list
|
||||
private final String gitlabSSHPatternTemplate = "^git@(?<host>%s):(?<subgroups>.*)$";
|
||||
// list
|
||||
private final List<Pattern> gitlabUrlPatterns = new ArrayList<>();
|
||||
private static final String OAUTH_PROVIDER_NAME = "gitlab";
|
||||
|
|
@ -62,13 +65,18 @@ public class GitlabUrlParser {
|
|||
if (gitlabEndpoints != null) {
|
||||
for (String gitlabEndpoint : Splitter.on(",").split(gitlabEndpoints)) {
|
||||
String trimmedEndpoint = trimEnd(gitlabEndpoint, '/');
|
||||
URI uri = URI.create(trimmedEndpoint);
|
||||
String schema = uri.getScheme();
|
||||
String host = uri.getHost();
|
||||
for (String gitlabUrlPatternTemplate : gitlabUrlPatternTemplates) {
|
||||
gitlabUrlPatterns.add(compile(format(gitlabUrlPatternTemplate, trimmedEndpoint)));
|
||||
gitlabUrlPatterns.add(compile(format(gitlabUrlPatternTemplate, schema, host)));
|
||||
}
|
||||
gitlabUrlPatterns.add(compile(format(gitlabSSHPatternTemplate, host)));
|
||||
}
|
||||
} else {
|
||||
gitlabUrlPatternTemplates.forEach(
|
||||
t -> gitlabUrlPatterns.add(compile(format(t, "https://gitlab.com"))));
|
||||
t -> gitlabUrlPatterns.add(compile(format(t, "https", "gitlab.com"))));
|
||||
gitlabUrlPatterns.add(compile(format(gitlabSSHPatternTemplate, "gitlab.com")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +120,7 @@ public class GitlabUrlParser {
|
|||
gitlabApiClient.getOAuthTokenInfo("");
|
||||
} catch (ScmCommunicationException e) {
|
||||
return e.getStatusCode() == HTTP_UNAUTHORIZED;
|
||||
} catch (ScmItemNotFoundException e) {
|
||||
} catch (ScmItemNotFoundException | IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -120,18 +128,32 @@ public class GitlabUrlParser {
|
|||
}
|
||||
|
||||
private Optional<Matcher> getPatternMatcherByUrl(String url) {
|
||||
Optional<String> serverUrlOptional = getServerUrl(url);
|
||||
if (serverUrlOptional.isPresent()) {
|
||||
String serverUrl = serverUrlOptional.get();
|
||||
return gitlabUrlPatternTemplates.stream()
|
||||
.map(t -> compile(format(t, serverUrl)).matcher(url))
|
||||
.filter(Matcher::matches)
|
||||
.findAny();
|
||||
}
|
||||
return Optional.empty();
|
||||
URI uri =
|
||||
URI.create(
|
||||
url.matches(format(gitlabSSHPatternTemplate, ".*"))
|
||||
? "ssh://" + url.replace(":", "/")
|
||||
: url);
|
||||
String scheme = uri.getScheme();
|
||||
String host = uri.getHost();
|
||||
return gitlabUrlPatternTemplates.stream()
|
||||
.map(t -> compile(format(t, scheme, host)).matcher(url))
|
||||
.filter(Matcher::matches)
|
||||
.findAny()
|
||||
.or(
|
||||
() -> {
|
||||
Matcher matcher = compile(format(gitlabSSHPatternTemplate, host)).matcher(url);
|
||||
if (matcher.matches()) {
|
||||
return Optional.of(matcher);
|
||||
}
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<String> getServerUrl(String repositoryUrl) {
|
||||
if (repositoryUrl.startsWith("git@")) {
|
||||
String substring = repositoryUrl.substring(4);
|
||||
return Optional.of("https://" + substring.substring(0, substring.indexOf(":")));
|
||||
}
|
||||
Matcher serverUrlMatcher = compile("[^/|:]/").matcher(repositoryUrl);
|
||||
if (serverUrlMatcher.find()) {
|
||||
return Optional.of(
|
||||
|
|
@ -162,6 +184,12 @@ public class GitlabUrlParser {
|
|||
}
|
||||
|
||||
private GitlabUrl parse(Matcher matcher) {
|
||||
String scheme = null;
|
||||
try {
|
||||
scheme = matcher.group("scheme");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok no such group
|
||||
}
|
||||
String host = matcher.group("host");
|
||||
String subGroups = trimEnd(matcher.group("subgroups"), '/');
|
||||
if (subGroups.endsWith(".git")) {
|
||||
|
|
@ -177,6 +205,7 @@ public class GitlabUrlParser {
|
|||
|
||||
return new GitlabUrl()
|
||||
.withHostName(host)
|
||||
.withScheme(scheme)
|
||||
.withSubGroups(subGroups)
|
||||
.withBranch(branch)
|
||||
.withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames());
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ public class GitlabAuthorizingFileContentProviderTest {
|
|||
@Test
|
||||
public void shouldExpandRelativePaths() throws Exception {
|
||||
URLFetcher urlFetcher = Mockito.mock(URLFetcher.class);
|
||||
GitlabUrl gitlabUrl =
|
||||
new GitlabUrl().withHostName("https://gitlab.net").withSubGroups("eclipse/che");
|
||||
GitlabUrl gitlabUrl = new GitlabUrl().withHostName("gitlab.net").withSubGroups("eclipse/che");
|
||||
FileContentProvider fileContentProvider =
|
||||
new GitlabAuthorizingFileContentProvider(gitlabUrl, urlFetcher, personalAccessTokenManager);
|
||||
var personalAccessToken = new PersonalAccessToken("foo", "che", "my-token");
|
||||
|
|
|
|||
|
|
@ -79,6 +79,22 @@ public class GitlabUrlParserTest {
|
|||
assertEquals(gitlabUrl.getBranch(), branch);
|
||||
}
|
||||
|
||||
/** Compare parsing */
|
||||
@Test(dataProvider = "parsing")
|
||||
public void shouldParseWithoutPredefinedEndpoint(
|
||||
String url, String project, String subGroups, String branch) {
|
||||
// given
|
||||
gitlabUrlParser =
|
||||
new GitlabUrlParser(null, devfileFilenamesProvider, mock(PersonalAccessTokenManager.class));
|
||||
// when
|
||||
GitlabUrl gitlabUrl = gitlabUrlParser.parse(url);
|
||||
|
||||
// then
|
||||
assertEquals(gitlabUrl.getProject(), project);
|
||||
assertEquals(gitlabUrl.getSubGroups(), subGroups);
|
||||
assertEquals(gitlabUrl.getBranch(), branch);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldValidateUrlByApiRequest() {
|
||||
// given
|
||||
|
|
@ -114,7 +130,12 @@ public class GitlabUrlParserTest {
|
|||
{"https://gitlab1.com/user/project/"},
|
||||
{"https://gitlab1.com/user/project/repo/"},
|
||||
{"https://gitlab1.com/user/project/-/tree/master/"},
|
||||
{"https://gitlab1.com/user/project/repo/-/tree/master/subfolder"}
|
||||
{"https://gitlab1.com/user/project/repo/-/tree/master/subfolder"},
|
||||
{"git@gitlab1.com:user/project/test1.git"},
|
||||
{"git@gitlab1.com:user/project1.git"},
|
||||
{"git@gitlab.foo.xxx:scm/project/test1.git"},
|
||||
{"git@gitlab1.com:user/project/"},
|
||||
{"git@gitlab1.com:user/project/repo/"},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +152,16 @@ public class GitlabUrlParserTest {
|
|||
},
|
||||
{"https://gitlab1.com/user/project/", "project", "user/project", null},
|
||||
{"https://gitlab1.com/user/project/repo/", "repo", "user/project/repo", null},
|
||||
{"git@gitlab1.com:user/project1.git", "project1", "user/project1", null},
|
||||
{"git@gitlab1.com:user/project/test1.git", "test1", "user/project/test1", null},
|
||||
{
|
||||
"git@gitlab1.com:user/project/group1/group2/test1.git",
|
||||
"test1",
|
||||
"user/project/group1/group2/test1",
|
||||
null
|
||||
},
|
||||
{"git@gitlab1.com:user/project/", "project", "user/project", null},
|
||||
{"git@gitlab1.com:user/project/repo/", "repo", "user/project/repo", null},
|
||||
{"https://gitlab1.com/user/project/-/tree/master/", "project", "user/project", "master"},
|
||||
{"https://gitlab1.com/user/project/repo/-/tree/foo", "repo", "user/project/repo", "foo"},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -66,6 +66,15 @@ public class GitlabUrlTest {
|
|||
assertEquals(iterator.next().location(), format(fileUrl, "foo.bar"));
|
||||
}
|
||||
|
||||
@Test(dataProvider = "urlsProvider")
|
||||
public void shouldReturnProviderUrl(String repoUrl, String ignored) {
|
||||
// when
|
||||
GitlabUrl gitlabUrl = gitlabUrlParser.parse(repoUrl);
|
||||
|
||||
// then
|
||||
assertEquals(gitlabUrl.getProviderUrl(), "https://gitlab.net");
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] urlsProvider() {
|
||||
return new Object[][] {
|
||||
|
|
@ -77,6 +86,14 @@ public class GitlabUrlTest {
|
|||
"https://gitlab.net/eclipse/fooproj/che.git",
|
||||
"https://gitlab.net/api/v4/projects/eclipse%%2Ffooproj%%2Fche/repository/files/%s/raw"
|
||||
},
|
||||
{
|
||||
"git@gitlab.net:eclipse/che.git",
|
||||
"https://gitlab.net/api/v4/projects/eclipse%%2Fche/repository/files/%s/raw"
|
||||
},
|
||||
{
|
||||
"git@gitlab.net:eclipse/fooproj/che.git",
|
||||
"https://gitlab.net/api/v4/projects/eclipse%%2Ffooproj%%2Fche/repository/files/%s/raw"
|
||||
},
|
||||
{
|
||||
"https://gitlab.net/eclipse/fooproj/-/tree/master/",
|
||||
"https://gitlab.net/api/v4/projects/eclipse%%2Ffooproj/repository/files/%s/raw?ref=master"
|
||||
|
|
@ -100,6 +117,8 @@ public class GitlabUrlTest {
|
|||
return new Object[][] {
|
||||
{"https://gitlab.net/eclipse/che.git", "https://gitlab.net/eclipse/che.git"},
|
||||
{"https://gitlab.net/eclipse/foo/che.git", "https://gitlab.net/eclipse/foo/che.git"},
|
||||
{"git@gitlab.net:eclipse/che.git", "git@gitlab.net:eclipse/che.git"},
|
||||
{"git@gitlab.net:eclipse/foo/che.git", "git@gitlab.net:eclipse/foo/che.git"},
|
||||
{
|
||||
"https://gitlab.net/eclipse/fooproj/che/-/tree/master/",
|
||||
"https://gitlab.net/eclipse/fooproj/che.git"
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public class AuthorizingFileContentProvider<T extends RemoteFactoryUrl>
|
|||
String authorization;
|
||||
if (isNullOrEmpty(credentials)) {
|
||||
PersonalAccessToken token =
|
||||
personalAccessTokenManager.get(remoteFactoryUrl.getHostName());
|
||||
personalAccessTokenManager.get(remoteFactoryUrl.getProviderUrl());
|
||||
authorization =
|
||||
formatAuthorization(
|
||||
token.getToken(),
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ public class DefaultFactoryUrl implements RemoteFactoryUrl {
|
|||
return URI.create(devfileFileLocation).getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderUrl() {
|
||||
return getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBranch() {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ public interface RemoteFactoryUrl {
|
|||
/** Remote hostname */
|
||||
String getHostName();
|
||||
|
||||
/** Remote provider URL */
|
||||
String getProviderUrl();
|
||||
|
||||
/** Remote branch */
|
||||
String getBranch();
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class AuthorizingFactoryParameterResolverTest {
|
|||
@Test
|
||||
public void shouldFetchContentWithAuthentication() throws Exception {
|
||||
// given
|
||||
when(remoteFactoryUrl.getHostName()).thenReturn("hostName");
|
||||
when(remoteFactoryUrl.getProviderUrl()).thenReturn("https://provider.url");
|
||||
when(urlFetcher.fetch(anyString(), anyString())).thenReturn("content");
|
||||
when(personalAccessTokenManager.get(anyString())).thenReturn(personalAccessToken);
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,11 @@ public class URLFactoryBuilderTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBranch() {
|
||||
return null;
|
||||
|
|
@ -286,6 +291,11 @@ public class URLFactoryBuilderTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBranch() {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
<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