From 88cbaebd1a1856a60a14eee25c79dad8456d0f9b Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Wed, 15 May 2024 10:13:37 +0300 Subject: [PATCH] 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 Co-authored-by: Anatolii Bazko --- ...RawDevfileUrlFactoryParameterResolver.java | 22 +++++- ...evfileUrlFactoryParameterResolverTest.java | 74 +++++++++++++++++-- 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java index 1e6b133832..1896fc699b 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java @@ -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 * available under the terms of the Eclipse Public License 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.shared.Constants.URL_PARAMETER_NAME; +import com.fasterxml.jackson.databind.JsonNode; import jakarta.validation.constraints.NotNull; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; 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.URLFactoryBuilder; 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.URLFileContentProvider; +import org.eclipse.che.api.workspace.server.devfile.exception.DevfileFormatException; /** * {@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 URLFetcher urlFetcher; + private final DevfileParser devfileParser; @Inject public RawDevfileUrlFactoryParameterResolver( - URLFactoryBuilder urlFactoryBuilder, URLFetcher urlFetcher) { + URLFactoryBuilder urlFactoryBuilder, URLFetcher urlFetcher, DevfileParser devfileParser) { super(null, urlFactoryBuilder, PROVIDER_NAME); this.urlFactoryBuilder = urlFactoryBuilder; this.urlFetcher = urlFetcher; + this.devfileParser = devfileParser; } /** @@ -64,7 +70,17 @@ public class RawDevfileUrlFactoryParameterResolver extends BaseFactoryParameterR @Override public boolean accept(Map factoryParameters) { 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 diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java index 68d7e1999d..84c9555944 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java @@ -24,11 +24,14 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.testng.Assert.assertFalse; import static org.testng.Assert.fail; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; import org.eclipse.che.api.core.BadRequestException; @@ -63,6 +66,7 @@ public class RawDevfileUrlFactoryParameterResolverTest { + " reference: ../localfile\n"; @Mock private URLFetcher urlFetcher; + @Mock private DevfileParser devfileParser; @InjectMocks private RawDevfileUrlFactoryParameterResolver rawDevfileUrlFactoryParameterResolver; @@ -84,7 +88,7 @@ public class RawDevfileUrlFactoryParameterResolverTest { "editor", "plugin", false, devfileParser, new DevfileVersionDetector()); 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 Map factoryParameters = new HashMap<>(); @@ -106,7 +110,7 @@ public class RawDevfileUrlFactoryParameterResolverTest { URLFetcher urlFetcher = mock(URLFetcher.class); RawDevfileUrlFactoryParameterResolver res = - new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher); + new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher, devfileParser); Map factoryParameters = new HashMap<>(); factoryParameters.put(URL_PARAMETER_NAME, "http://myloc/devfile"); @@ -137,7 +141,7 @@ public class RawDevfileUrlFactoryParameterResolverTest { URLFetcher urlFetcher = mock(URLFetcher.class); RawDevfileUrlFactoryParameterResolver res = - new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher); + new RawDevfileUrlFactoryParameterResolver(urlFactoryBuilder, urlFetcher, devfileParser); Map factoryParameters = new HashMap<>(); factoryParameters.put(URL_PARAMETER_NAME, url); @@ -165,12 +169,67 @@ public class RawDevfileUrlFactoryParameterResolverTest { 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 - 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 boolean result = 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 assertFalse(result); @@ -201,4 +260,9 @@ public class RawDevfileUrlFactoryParameterResolverTest { "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"}; + } }