diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java b/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java index a6197eee08..d3bebfd4a3 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java @@ -143,12 +143,10 @@ public String getDescription() { return "Reload your configuration or update configuration source."; } - /** - * Name of the category for this management link. - * TODO: Use getCategory when core requirement is greater or equal to 2.226 - */ - public @NonNull String getCategoryName() { - return "CONFIGURATION"; + @NonNull + @Override + public Category getCategory() { + return Category.CONFIGURATION; } @NonNull @@ -228,7 +226,8 @@ public void doReplace(StaplerRequest2 request, StaplerResponse2 response) throws candidateSources.add(candidateSource); } else { LOGGER.log(Level.WARNING, "Source {0} could not be applied", candidateSource); - // todo: show message in UI + throw new ConfiguratorException( + "Source " + candidateSource + " could not be applied or does not exist."); } } if (!candidateSources.isEmpty()) { @@ -244,7 +243,8 @@ public void doReplace(StaplerRequest2 request, StaplerResponse2 response) throws LOGGER.log(Level.FINE, "Replace configuration with: " + normalizedSource); } else { LOGGER.log(Level.WARNING, "Provided sources could not be applied"); - // todo: show message in UI + throw new ConfiguratorException( + "Provided sources could not be applied. Please check the syntax and validity of the provided configuration."); } } else { LOGGER.log(Level.FINE, "No such source exists, applying default"); @@ -453,7 +453,12 @@ public List getBundledCasCURIs() { URL bundled = servletContext.getResource(cascItem); if (bundled != null && matcher.matches(new File(bundled.getPath()).toPath())) { res.add(bundled.toString()); - } // TODO: else do some handling? + } else if (bundled != null) { + LOGGER.log( + Level.FINE, + "Skipped bundled resource {0} as it does not match the YAML pattern.", + bundled.getPath()); + } } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to execute " + res, e); } @@ -569,23 +574,7 @@ public void doViewExport(StaplerRequest2 req, StaplerResponse2 res) throws Excep ByteArrayOutputStream out = new ByteArrayOutputStream(); export(out); - req.setAttribute("viewExport", new ManagementLink() { - @Override - public String getIconFileName() { - return ""; - } - - // TODO - FIX - EXTREMELY HACKY - couldn't expose a public method for some reason - @Override - public String getUrlName() { - return out.toString(StandardCharsets.UTF_8); - } - - @Override - public String getDisplayName() { - return "Export configuration"; - } - }); + req.setAttribute("exportedYaml", out.toString(StandardCharsets.UTF_8)); req.getView(this, "viewExport.jelly").forward(req, res); } diff --git a/plugin/src/main/resources/io/jenkins/plugins/casc/ConfigurationAsCode/viewExport.jelly b/plugin/src/main/resources/io/jenkins/plugins/casc/ConfigurationAsCode/viewExport.jelly index d826cbdcb7..3067e809f3 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/casc/ConfigurationAsCode/viewExport.jelly +++ b/plugin/src/main/resources/io/jenkins/plugins/casc/ConfigurationAsCode/viewExport.jelly @@ -3,7 +3,7 @@ xmlns:f="/lib/form"> - + @@ -11,7 +11,7 @@ - +
@@ -20,7 +20,7 @@
-      ${viewExport.urlName}
+      ${exportedYaml}
     
diff --git a/plugin/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeApiTest.java b/plugin/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeApiTest.java index b69e8774e0..34f0ec7a3e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeApiTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeApiTest.java @@ -5,10 +5,14 @@ import static org.hamcrest.Matchers.is; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; import jenkins.model.Jenkins; import org.htmlunit.HttpMethod; import org.htmlunit.WebRequest; import org.htmlunit.WebResponse; +import org.htmlunit.util.NameValuePair; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; @@ -159,4 +163,26 @@ public void testDoConfigure_ValidYaml_NoChanges() throws Exception { assertThat(j.jenkins.getSystemMessage(), is("Idempotency Test")); } } + + @Test + public void testDoReplace_ValidSource() throws Exception { + configureAdminSecurity(); + + Path configFile = Files.createTempFile("valid", ".yaml"); + Files.writeString(configFile, "jenkins:\n systemMessage: 'Hello Replace'"); + + try (JenkinsRule.WebClient wc = j.createWebClient().withBasicApiToken(ADMIN)) { + wc.setThrowExceptionOnFailingStatusCode(false); + + WebRequest request = + new WebRequest(new URL(j.getURL(), "manage/configuration-as-code/replace"), HttpMethod.POST); + + request.setRequestParameters(List.of(new NameValuePair("_.newSource", configFile.toString()))); + + WebResponse response = wc.getPage(request).getWebResponse(); + + assertThat(response.getStatusCode(), is(200)); + assertThat(j.jenkins.getSystemMessage(), is("Hello Replace")); + } + } } diff --git a/plugin/src/test/java/io/jenkins/plugins/casc/ErrorPageTest.java b/plugin/src/test/java/io/jenkins/plugins/casc/ErrorPageTest.java index 5819bfa60e..ccf4a03099 100644 --- a/plugin/src/test/java/io/jenkins/plugins/casc/ErrorPageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/casc/ErrorPageTest.java @@ -1,5 +1,6 @@ package io.jenkins.plugins.casc; +import static jenkins.model.Jenkins.ADMINISTER; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -16,6 +17,7 @@ import org.junit.jupiter.api.io.TempDir; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; +import org.jvnet.hudson.test.MockAuthorizationStrategy; import org.jvnet.hudson.test.junit.jupiter.WithJenkins; @WithJenkins @@ -87,4 +89,34 @@ void noImplementationFoundForSymbol() throws Exception { assertThat(pageContent, containsString("Attribute was:")); assertThat(pageContent, containsString("unknown")); } + + @Test + void replaceWithInvalidSource() throws Exception { + String pageContent = replaceConfiguration(); + + assertThat( + pageContent, containsString("Source non-existent-file.yaml could not be applied or does not exist.")); + } + + private String replaceConfiguration() throws Exception { + r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); + r.jenkins.setAuthorizationStrategy( + new MockAuthorizationStrategy().grant(ADMINISTER).everywhere().to("admin")); + + try (WebClient webClient = r.createWebClient().withThrowExceptionOnFailingStatusCode(false)) { + webClient.login("admin", "admin"); + + HtmlPage htmlPage = webClient.goTo("manage/configuration-as-code/"); + + HtmlButton button = (HtmlButton) htmlPage.getElementById("btn-open-apply-configuration"); + HtmlElementUtil.click(button); + + HtmlForm replaceForm = htmlPage.getFormByName("replace"); + replaceForm.getInputByName("_.newSource").setValue("non-existent-file.yaml"); + + HtmlPage submit = r.submit(replaceForm); + + return submit.asNormalizedText(); + } + } } diff --git a/test-harness/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeTest.java b/test-harness/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeTest.java index f2fb1f2f2b..90364a27e2 100644 --- a/test-harness/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeTest.java +++ b/test-harness/src/test/java/io/jenkins/plugins/casc/ConfigurationAsCodeTest.java @@ -340,12 +340,6 @@ void testHtmlDocStringRetrieval(JenkinsConfiguredWithCodeRule j) throws Exceptio assertEquals(expectedDocString, actualDocString); } - @Test - void configurationCategory(JenkinsConfiguredWithCodeRule j) { - ConfigurationAsCode configurationAsCode = ConfigurationAsCode.get(); - assertThat(configurationAsCode.getCategoryName(), is("CONFIGURATION")); - } - private static File newFolder(File root, String... subDirs) throws IOException { String subFolder = String.join("/", subDirs); File result = new File(root, subFolder);