Skip to content

Commit 0f3d41f

Browse files
authored
updates to support custom gitlab ports (#819)
Some last test additions and fixes based on testing deployed container
1 parent d87df42 commit 0f3d41f

File tree

4 files changed

+389
-9
lines changed

4 files changed

+389
-9
lines changed

wsmaster/che-core-api-factory-gitlab-common/src/main/java/org/eclipse/che/api/factory/server/gitlab/AbstractGitlabUrlParser.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ public class AbstractGitlabUrlParser {
4545
private final String providerName;
4646
private static final List<String> gitlabUrlPatternTemplates =
4747
List.of(
48-
"^(?<scheme>%s)://(?<host>%s)/(?<subgroups>([^/]++/?)+)/-/tree/(?<branch>.++)(/)?",
49-
"^(?<scheme>%s)://(?<host>%s)/(?<subgroups>.*)"); // a wider one, should be the last in
48+
"^(?<scheme>%s)://(?<host>%s)(:(?<port>%s))?/(?<subgroups>([^/]++/?)+)/-/tree/(?<branch>.++)(/)?",
49+
"^(?<scheme>%s)://(?<host>%s)(:(?<port>%s))?/(?<subgroups>.*)"); // a wider one, should be
50+
// the last in
5051
// the list
5152
private final String gitlabSSHPatternTemplate = "^git@(?<host>%s):(?<subgroups>.*)$";
5253
// list
@@ -62,15 +63,16 @@ public AbstractGitlabUrlParser(
6263
this.providerName = providerName;
6364
if (isNullOrEmpty(serverUrl)) {
6465
gitlabUrlPatternTemplates.forEach(
65-
t -> gitlabUrlPatterns.add(compile(format(t, "https", "gitlab.com"))));
66+
t -> gitlabUrlPatterns.add(compile(format(t, "https", "gitlab.com", 443))));
6667
gitlabUrlPatterns.add(compile(format(gitlabSSHPatternTemplate, "gitlab.com")));
6768
} else {
6869
String trimmedEndpoint = trimEnd(serverUrl, '/');
6970
URI uri = URI.create(trimmedEndpoint);
7071
String schema = uri.getScheme();
7172
String host = uri.getHost();
7273
for (String gitlabUrlPatternTemplate : gitlabUrlPatternTemplates) {
73-
gitlabUrlPatterns.add(compile(format(gitlabUrlPatternTemplate, schema, host)));
74+
gitlabUrlPatterns.add(
75+
compile(format(gitlabUrlPatternTemplate, schema, host, uri.getPort())));
7476
}
7577
gitlabUrlPatterns.add(compile(format(gitlabSSHPatternTemplate, host)));
7678
}
@@ -144,7 +146,7 @@ private Optional<Matcher> getPatternMatcherByUrl(String url) {
144146
String scheme = uri.getScheme();
145147
String host = uri.getHost();
146148
return gitlabUrlPatternTemplates.stream()
147-
.map(t -> compile(format(t, scheme, host)).matcher(url))
149+
.map(t -> compile(format(t, scheme, host, uri.getPort())).matcher(url))
148150
.filter(Matcher::matches)
149151
.findAny()
150152
.or(
@@ -193,12 +195,18 @@ public GitlabUrl parse(String url) {
193195

194196
private GitlabUrl parse(Matcher matcher) {
195197
String scheme = null;
198+
String port = null;
196199
try {
197200
scheme = matcher.group("scheme");
198201
} catch (IllegalArgumentException e) {
199202
// ok no such group
200203
}
201204
String host = matcher.group("host");
205+
try {
206+
port = matcher.group("port");
207+
} catch (IllegalArgumentException e) {
208+
// ok no such group
209+
}
202210
String subGroups = trimEnd(matcher.group("subgroups"), '/');
203211
if (subGroups.endsWith(".git")) {
204212
subGroups = subGroups.substring(0, subGroups.length() - 4);
@@ -213,6 +221,7 @@ private GitlabUrl parse(Matcher matcher) {
213221

214222
return new GitlabUrl()
215223
.withHostName(host)
224+
.withPort(port)
216225
.withScheme(scheme)
217226
.withSubGroups(subGroups)
218227
.withBranch(branch)

wsmaster/che-core-api-factory-gitlab-common/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrl.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012-2024 Red Hat, Inc.
2+
* Copyright (c) 2012-2025 Red Hat, Inc.
33
* This program and the accompanying materials are made
44
* available under the terms of the Eclipse Public License 2.0
55
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -37,6 +37,9 @@ public class GitlabUrl extends DefaultFactoryUrl {
3737
/** Hostname of the gitlab URL */
3838
private String hostName;
3939

40+
/** Port of the gitlab URL */
41+
private String port;
42+
4043
/** Scheme of the gitlab URL */
4144
private String scheme;
4245

@@ -68,7 +71,10 @@ public String getProviderName() {
6871

6972
@Override
7073
public String getProviderUrl() {
71-
return (isNullOrEmpty(scheme) ? "https" : scheme) + "://" + hostName;
74+
return (isNullOrEmpty(scheme) ? "https" : scheme)
75+
+ "://"
76+
+ hostName
77+
+ (isNullOrEmpty(port) ? "" : ":" + port);
7278
}
7379

7480
/**
@@ -85,6 +91,11 @@ public GitlabUrl withHostName(String hostName) {
8591
return this;
8692
}
8793

94+
public GitlabUrl withPort(String port) {
95+
this.port = port;
96+
return this;
97+
}
98+
8899
public GitlabUrl withScheme(String scheme) {
89100
this.scheme = scheme;
90101
return this;
@@ -171,7 +182,11 @@ public String location() {
171182
public String rawFileLocation(String fileName) {
172183
String resultUrl =
173184
new StringJoiner("/")
174-
.add((isNullOrEmpty(scheme) ? "https" : scheme) + "://" + hostName)
185+
.add(
186+
(isNullOrEmpty(scheme) ? "https" : scheme)
187+
+ "://"
188+
+ hostName
189+
+ (isNullOrEmpty(port) ? "" : ":" + port))
175190
.add("api/v4/projects")
176191
// use URL-encoded path to the project as a selector instead of id
177192
.add(encode(subGroups, Charsets.UTF_8))
@@ -193,6 +208,12 @@ protected String repositoryLocation() {
193208
if (isNullOrEmpty(scheme)) {
194209
return "git@" + hostName + ":" + subGroups + ".git";
195210
}
196-
return scheme + "://" + hostName + "/" + subGroups + ".git";
211+
return scheme
212+
+ "://"
213+
+ hostName
214+
+ (isNullOrEmpty(port) ? "" : ":" + port)
215+
+ "/"
216+
+ subGroups
217+
+ ".git";
197218
}
198219
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Copyright (c) 2012-2025 Red Hat, Inc.
3+
* This program and the accompanying materials are made
4+
* available under the terms of the Eclipse Public License 2.0
5+
* which is available at https://www.eclipse.org/legal/epl-2.0/
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* Red Hat, Inc. - initial API and implementation
11+
*/
12+
package org.eclipse.che.api.factory.server.gitlab;
13+
14+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
15+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
16+
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
17+
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
18+
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
19+
import static org.mockito.Mockito.mock;
20+
import static org.testng.Assert.assertEquals;
21+
import static org.testng.Assert.assertFalse;
22+
import static org.testng.Assert.assertTrue;
23+
24+
import com.github.tomakehurst.wiremock.WireMockServer;
25+
import com.github.tomakehurst.wiremock.client.WireMock;
26+
import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
27+
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
28+
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
29+
import org.mockito.Mock;
30+
import org.mockito.testng.MockitoTestNGListener;
31+
import org.testng.annotations.BeforeClass;
32+
import org.testng.annotations.BeforeMethod;
33+
import org.testng.annotations.DataProvider;
34+
import org.testng.annotations.Listeners;
35+
import org.testng.annotations.Test;
36+
37+
@Listeners(MockitoTestNGListener.class)
38+
public class GitlabCustomPortUrlParserTest {
39+
40+
@Mock private DevfileFilenamesProvider devfileFilenamesProvider;
41+
42+
/** Instance of component that will be tested. */
43+
private GitlabUrlParser gitlabUrlParser;
44+
45+
private WireMockServer wireMockServer;
46+
private WireMock wireMock;
47+
48+
@BeforeClass
49+
public void prepare() {
50+
wireMockServer =
51+
new WireMockServer(wireMockConfig().notifier(new Slf4jNotifier(false)).dynamicPort());
52+
wireMockServer.start();
53+
WireMock.configureFor("localhost", wireMockServer.port());
54+
wireMock = new WireMock("localhost", wireMockServer.port());
55+
}
56+
57+
@BeforeMethod
58+
public void setUp() {
59+
gitlabUrlParser =
60+
new GitlabUrlParser(
61+
"https://gitlab.custom.com:31280",
62+
devfileFilenamesProvider,
63+
mock(PersonalAccessTokenManager.class));
64+
}
65+
66+
/** Check URLs are valid with regexp */
67+
@Test(dataProvider = "UrlsProvider")
68+
public void checkRegexp(String url) {
69+
assertTrue(gitlabUrlParser.isValid(url), "url " + url + " is invalid");
70+
}
71+
72+
/** Compare parsing */
73+
@Test(dataProvider = "parsing")
74+
public void checkParsing(String url, String project, String subGroups, String branch) {
75+
GitlabUrl gitlabUrl = gitlabUrlParser.parse(url);
76+
77+
assertEquals(gitlabUrl.getProject(), project);
78+
assertEquals(gitlabUrl.getSubGroups(), subGroups);
79+
assertEquals(gitlabUrl.getBranch(), branch);
80+
}
81+
82+
/** Compare parsing */
83+
@Test(dataProvider = "parsing")
84+
public void shouldParseWithoutPredefinedEndpoint(
85+
String url, String project, String subGroups, String branch) {
86+
// given
87+
gitlabUrlParser =
88+
new GitlabUrlParser(null, devfileFilenamesProvider, mock(PersonalAccessTokenManager.class));
89+
// when
90+
GitlabUrl gitlabUrl = gitlabUrlParser.parse(url);
91+
92+
// then
93+
assertEquals(gitlabUrl.getProject(), project);
94+
assertEquals(gitlabUrl.getSubGroups(), subGroups);
95+
assertEquals(gitlabUrl.getBranch(), branch);
96+
}
97+
98+
@Test
99+
public void shouldValidateUrlByApiRequest() {
100+
// given
101+
String url = wireMockServer.url("/user/repo");
102+
stubFor(
103+
get(urlEqualTo("/oauth/token/info"))
104+
.willReturn(
105+
aResponse()
106+
.withStatus(401)
107+
.withBody(
108+
"{\"error\":\"invalid_token\",\"error_description\":\"The access token is invalid\",\"state\":\"unauthorized\"}")));
109+
110+
// when
111+
boolean result = gitlabUrlParser.isValid(url);
112+
113+
// then
114+
assertTrue(result);
115+
}
116+
117+
@Test
118+
public void shouldNotValidateUrlByApiRequestWithPlainStringResponse() {
119+
// given
120+
String url = wireMockServer.url("/user/repo");
121+
stubFor(
122+
get(urlEqualTo("/oauth/token/info"))
123+
.willReturn(aResponse().withStatus(401).withBody("plain string error")));
124+
125+
// when
126+
boolean result = gitlabUrlParser.isValid(url);
127+
128+
// then
129+
assertFalse(result);
130+
}
131+
132+
@Test
133+
public void shouldNotValidateUrlByApiRequest() {
134+
// given
135+
String url = wireMockServer.url("/user/repo");
136+
stubFor(get(urlEqualTo("/oauth/token/info")).willReturn(aResponse().withStatus(500)));
137+
138+
// when
139+
boolean result = gitlabUrlParser.isValid(url);
140+
141+
// then
142+
assertFalse(result);
143+
}
144+
145+
@DataProvider(name = "UrlsProvider")
146+
public Object[][] urls() {
147+
return new Object[][] {
148+
{"https://gitlab.custom.com:31280/user/project/test1.git"},
149+
{"https://gitlab.custom.com:31280/user/project1.git"},
150+
{"https://gitlab.custom.com:31280/scm/project/test1.git"},
151+
{"https://gitlab.custom.com:31280/user/project/"},
152+
{"https://gitlab.custom.com:31280/user/project/repo/"},
153+
{"https://gitlab.custom.com:31280/user/project/-/tree/master/"},
154+
{"https://gitlab.custom.com:31280/user/project/repo/-/tree/master/subfolder"},
155+
{"[email protected]:user/project/test1.git"},
156+
{"[email protected]:user/project1.git"},
157+
{"[email protected]:scm/project/test1.git"},
158+
{"[email protected]:user/project/"},
159+
{"[email protected]:user/project/repo/"},
160+
};
161+
}
162+
163+
@DataProvider(name = "parsing")
164+
public Object[][] expectedParsing() {
165+
return new Object[][] {
166+
{"https://gitlab.custom.com:31280/user/project1.git", "project1", "user/project1", null},
167+
{
168+
"https://gitlab.custom.com:31280/user/project/test1.git",
169+
"test1",
170+
"user/project/test1",
171+
null
172+
},
173+
{
174+
"https://gitlab.custom.com:31280/user/project/group1/group2/test1.git",
175+
"test1",
176+
"user/project/group1/group2/test1",
177+
null
178+
},
179+
{"https://gitlab.custom.com:31280/user/project/", "project", "user/project", null},
180+
{"https://gitlab.custom.com:31280/user/project/repo/", "repo", "user/project/repo", null},
181+
{"[email protected]:user/project1.git", "project1", "user/project1", null},
182+
{"[email protected]:user/project/test1.git", "test1", "user/project/test1", null},
183+
{
184+
"[email protected]:user/project/group1/group2/test1.git",
185+
"test1",
186+
"user/project/group1/group2/test1",
187+
null
188+
},
189+
{"[email protected]:user/project/", "project", "user/project", null},
190+
{"[email protected]:user/project/repo/", "repo", "user/project/repo", null},
191+
{
192+
"https://gitlab.custom.com:31280/user/project/-/tree/master/",
193+
"project",
194+
"user/project",
195+
"master"
196+
},
197+
{
198+
"https://gitlab.custom.com:31280/user/project/repo/-/tree/foo",
199+
"repo",
200+
"user/project/repo",
201+
"foo"
202+
},
203+
{
204+
"https://gitlab.custom.com:31280/user/project/repo/-/tree/branch/with/slash",
205+
"repo",
206+
"user/project/repo",
207+
"branch/with/slash"
208+
},
209+
{
210+
"https://gitlab.custom.com:31280/user/project/group1/group2/repo/-/tree/foo/",
211+
"repo",
212+
"user/project/group1/group2/repo",
213+
"foo"
214+
}
215+
};
216+
}
217+
}

0 commit comments

Comments
 (0)