Support raw devfile urls without yaml extension (#683)

On handling raw devfile urls, request content by the url, and check if the content is a devfile. If yes treat the url as a raw devfile url.

---------

Signed-off-by: ivinokur <ivinokur@redhat.com>
Co-authored-by: Anatolii Bazko <abazko@redhat.com>
pr-main-to-7.87.0-SNAPSHOT
Igor Vinokur 2024-05-15 10:13:37 +03:00 committed by GitHub
parent e072c7642b
commit 88cbaebd1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 88 additions and 8 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 Red Hat, Inc.
* This program and the accompanying materials are made * This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 * available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/ * which is available at https://www.eclipse.org/legal/epl-2.0/
@ -16,7 +16,9 @@ import static java.lang.String.format;
import static org.eclipse.che.api.factory.server.FactoryResolverPriority.HIGHEST; import static org.eclipse.che.api.factory.server.FactoryResolverPriority.HIGHEST;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME; import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -30,8 +32,10 @@ import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl; import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder; import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto; import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher; import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.URLFileContentProvider; import org.eclipse.che.api.workspace.server.devfile.URLFileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileFormatException;
/** /**
* {@link FactoryParametersResolver} implementation to resolve factory based on url parameter as a * {@link FactoryParametersResolver} implementation to resolve factory based on url parameter as a
@ -45,13 +49,15 @@ public class RawDevfileUrlFactoryParameterResolver extends BaseFactoryParameterR
protected final URLFactoryBuilder urlFactoryBuilder; protected final URLFactoryBuilder urlFactoryBuilder;
protected final URLFetcher urlFetcher; protected final URLFetcher urlFetcher;
private final DevfileParser devfileParser;
@Inject @Inject
public RawDevfileUrlFactoryParameterResolver( public RawDevfileUrlFactoryParameterResolver(
URLFactoryBuilder urlFactoryBuilder, URLFetcher urlFetcher) { URLFactoryBuilder urlFactoryBuilder, URLFetcher urlFetcher, DevfileParser devfileParser) {
super(null, urlFactoryBuilder, PROVIDER_NAME); super(null, urlFactoryBuilder, PROVIDER_NAME);
this.urlFactoryBuilder = urlFactoryBuilder; this.urlFactoryBuilder = urlFactoryBuilder;
this.urlFetcher = urlFetcher; this.urlFetcher = urlFetcher;
this.devfileParser = devfileParser;
} }
/** /**
@ -64,7 +70,17 @@ public class RawDevfileUrlFactoryParameterResolver extends BaseFactoryParameterR
@Override @Override
public boolean accept(Map<String, String> factoryParameters) { public boolean accept(Map<String, String> factoryParameters) {
String url = factoryParameters.get(URL_PARAMETER_NAME); String url = factoryParameters.get(URL_PARAMETER_NAME);
return !isNullOrEmpty(url) && PATTERN.matcher(url).matches(); return !isNullOrEmpty(url) && (PATTERN.matcher(url).matches() || containsYaml(url));
}
private boolean containsYaml(String requestURL) {
try {
String fetch = urlFetcher.fetch(requestURL);
JsonNode parsedYaml = devfileParser.parseYamlRaw(fetch);
return !parsedYaml.isEmpty();
} catch (IOException | DevfileFormatException e) {
return false;
}
} }
@Override @Override

View File

@ -24,11 +24,14 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertFalse;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.assertTrue;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.FileNotFoundException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.core.BadRequestException;
@ -63,6 +66,7 @@ public class RawDevfileUrlFactoryParameterResolverTest {
+ " reference: ../localfile\n"; + " reference: ../localfile\n";
@Mock private URLFetcher urlFetcher; @Mock private URLFetcher urlFetcher;
@Mock private DevfileParser devfileParser;
@InjectMocks private RawDevfileUrlFactoryParameterResolver rawDevfileUrlFactoryParameterResolver; @InjectMocks private RawDevfileUrlFactoryParameterResolver rawDevfileUrlFactoryParameterResolver;
@ -84,7 +88,7 @@ public class RawDevfileUrlFactoryParameterResolverTest {
"editor", "plugin", false, devfileParser, new DevfileVersionDetector()); "editor", "plugin", false, devfileParser, new DevfileVersionDetector());
RawDevfileUrlFactoryParameterResolver res = RawDevfileUrlFactoryParameterResolver res =
new RawDevfileUrlFactoryParameterResolver(factoryBuilder, urlFetcher); new RawDevfileUrlFactoryParameterResolver(factoryBuilder, urlFetcher, devfileParser);
// set up our factory with the location of our devfile that is referencing our localfile // set up our factory with the location of our devfile that is referencing our localfile
Map<String, String> factoryParameters = new HashMap<>(); Map<String, String> factoryParameters = new HashMap<>();
@ -106,7 +110,7 @@ public class RawDevfileUrlFactoryParameterResolverTest {
URLFetcher urlFetcher = mock(URLFetcher.class); URLFetcher urlFetcher = mock(URLFetcher.class);
RawDevfileUrlFactoryParameterResolver res = RawDevfileUrlFactoryParameterResolver res =
new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher); new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher, devfileParser);
Map<String, String> factoryParameters = new HashMap<>(); Map<String, String> factoryParameters = new HashMap<>();
factoryParameters.put(URL_PARAMETER_NAME, "http://myloc/devfile"); factoryParameters.put(URL_PARAMETER_NAME, "http://myloc/devfile");
@ -137,7 +141,7 @@ public class RawDevfileUrlFactoryParameterResolverTest {
URLFetcher urlFetcher = mock(URLFetcher.class); URLFetcher urlFetcher = mock(URLFetcher.class);
RawDevfileUrlFactoryParameterResolver res = RawDevfileUrlFactoryParameterResolver res =
new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher); new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher, devfileParser);
Map<String, String> factoryParameters = new HashMap<>(); Map<String, String> factoryParameters = new HashMap<>();
factoryParameters.put(URL_PARAMETER_NAME, url); factoryParameters.put(URL_PARAMETER_NAME, url);
@ -165,12 +169,67 @@ public class RawDevfileUrlFactoryParameterResolverTest {
assertTrue(result); assertTrue(result);
} }
@Test(dataProvider = "devfileUrlsWithoutExtension")
public void shouldAcceptRawDevfileUrlWithoutExtension(String url) throws Exception {
// given
JsonNode jsonNode = mock(JsonNode.class);
when(urlFetcher.fetch(eq(url))).thenReturn(DEVFILE);
when(devfileParser.parseYamlRaw(eq(DEVFILE))).thenReturn(jsonNode);
when(jsonNode.isEmpty()).thenReturn(false);
// when
boolean result =
rawDevfileUrlFactoryParameterResolver.accept(singletonMap(URL_PARAMETER_NAME, url));
// then
assertTrue(result);
}
@Test @Test
public void shouldNotAcceptRawDevfileUrl() { public void shouldAcceptRawDevfileUrlWithYaml() throws Exception {
// given
JsonNode jsonNode = mock(JsonNode.class);
String url = "https://host/path/devfile";
when(urlFetcher.fetch(eq(url))).thenReturn(DEVFILE);
when(devfileParser.parseYamlRaw(eq(DEVFILE))).thenReturn(jsonNode);
when(jsonNode.isEmpty()).thenReturn(false);
// when
boolean result =
rawDevfileUrlFactoryParameterResolver.accept(singletonMap(URL_PARAMETER_NAME, url));
// then
assertTrue(result);
}
@Test
public void shouldNotAcceptPublicGitRepositoryUrl() throws Exception {
// given
JsonNode jsonNode = mock(JsonNode.class);
String gitRepositoryUrl = "https://host/user/repo.git";
when(urlFetcher.fetch(eq(gitRepositoryUrl))).thenReturn("unsupported content");
when(devfileParser.parseYamlRaw(eq("unsupported content"))).thenReturn(jsonNode);
when(jsonNode.isEmpty()).thenReturn(true);
// when // when
boolean result = boolean result =
rawDevfileUrlFactoryParameterResolver.accept( rawDevfileUrlFactoryParameterResolver.accept(
singletonMap(URL_PARAMETER_NAME, "https://host/user/repo.git")); singletonMap(URL_PARAMETER_NAME, gitRepositoryUrl));
// then
assertFalse(result);
}
@Test
public void shouldNotAcceptPrivateGitRepositoryUrl() throws Exception {
// given
String gitRepositoryUrl = "https://host/user/private-repo.git";
when(urlFetcher.fetch(eq(gitRepositoryUrl))).thenThrow(new FileNotFoundException());
// when
boolean result =
rawDevfileUrlFactoryParameterResolver.accept(
singletonMap(URL_PARAMETER_NAME, gitRepositoryUrl));
// then // then
assertFalse(result); assertFalse(result);
@ -201,4 +260,9 @@ public class RawDevfileUrlFactoryParameterResolverTest {
"https://host/path/any-name.yml?token=TOKEN123" "https://host/path/any-name.yml?token=TOKEN123"
}; };
} }
@DataProvider(name = "devfileUrlsWithoutExtension")
private Object[] devfileUrlsWithoutExtension() {
return new String[] {"https://host/path/any-name", "https://host/path/any-name?token=TOKEN123"};
}
} }