This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch feature/SLING-8337 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git
commit 7deca18cc89a9f6b1c7eff019ce87c04c0d441cc Author: Robert Munteanu <[email protected]> AuthorDate: Tue Apr 23 16:14:37 2019 +0300 SLING-8337 - Create sub-command to manage the Jira update when promoting a release Move the VersionClient to be an HTTP-based test in preparation for testing commands that change state. --- pom.xml | 9 +-- .../sling/cli/impl/ComponentContextHelper.java | 47 ++++++++++++ .../sling/cli/impl/http/HttpClientFactory.java | 17 ++++- .../apache/sling/cli/impl/jira/VersionClient.java | 28 ++++--- .../org/apache/sling/cli/impl/jira/MockJira.java | 86 ++++++++++++++++++++++ .../sling/cli/impl/jira/VersionClientTest.java | 50 +++---------- 6 files changed, 182 insertions(+), 55 deletions(-) diff --git a/pom.xml b/pom.xml index 6fa560a..bf1224a 100644 --- a/pom.xml +++ b/pom.xml @@ -164,6 +164,10 @@ <artifactId>osgi.core</artifactId> </dependency> <dependency> + <groupId>org.osgi</groupId> + <artifactId>osgi.cmpn</artifactId> + </dependency> + <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.feature.launcher</artifactId> <version>0.8.0</version> @@ -231,10 +235,5 @@ <version>1.3.0</version> <scope>test</scope> </dependency> - <dependency> - <groupId>org.osgi</groupId> - <artifactId>osgi.cmpn</artifactId> - <scope>test</scope> - </dependency> </dependencies> </project> diff --git a/src/main/java/org/apache/sling/cli/impl/ComponentContextHelper.java b/src/main/java/org/apache/sling/cli/impl/ComponentContextHelper.java new file mode 100644 index 0000000..b996e88 --- /dev/null +++ b/src/main/java/org/apache/sling/cli/impl/ComponentContextHelper.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cli.impl; + +import org.osgi.service.component.ComponentContext; + +public class ComponentContextHelper { + + public static ComponentContextHelper wrap(ComponentContext wrapped) { + + return new ComponentContextHelper(wrapped); + } + + private final ComponentContext wrapped; + + public ComponentContextHelper(ComponentContext wrapped) { + this.wrapped = wrapped; + } + + public String getProperty(String name, String fallback) { + Object prop = wrapped.getProperties().get(name); + if ( prop != null) { + return prop.toString(); + } + + return fallback; + } + + public int getProperty(String name, int fallback) { + + return Integer.parseInt(getProperty(name, String.valueOf(fallback))); + } +} diff --git a/src/main/java/org/apache/sling/cli/impl/http/HttpClientFactory.java b/src/main/java/org/apache/sling/cli/impl/http/HttpClientFactory.java index 7975145..f881594 100644 --- a/src/main/java/org/apache/sling/cli/impl/http/HttpClientFactory.java +++ b/src/main/java/org/apache/sling/cli/impl/http/HttpClientFactory.java @@ -21,16 +21,31 @@ import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.sling.cli.impl.ComponentContextHelper; import org.apache.sling.cli.impl.Credentials; import org.apache.sling.cli.impl.CredentialsService; +import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @Component(service = HttpClientFactory.class) public class HttpClientFactory { + private static final String DEFAULT_JIRA_HOST = "jira.apache.org"; + private static final int DEFAULT_JIRA_PORT = 443; + @Reference private CredentialsService credentialsService; + + private String jiraHost; + private int jiraPort; + + protected void activate(ComponentContext ctx) { + + ComponentContextHelper helper = ComponentContextHelper.wrap(ctx); + jiraHost = helper.getProperty("jira.host", DEFAULT_JIRA_HOST); + jiraPort = helper.getProperty("jira.port", DEFAULT_JIRA_PORT); + } public CloseableHttpClient newClient() { @@ -42,7 +57,7 @@ public class HttpClientFactory { new UsernamePasswordCredentials(asf.getUsername(), asf.getPassword())); credentialsProvider.setCredentials(new AuthScope("reporter.apache.org", 443), new UsernamePasswordCredentials(asf.getUsername(), asf.getPassword())); - credentialsProvider.setCredentials(new AuthScope("jira.apache.org", 443), + credentialsProvider.setCredentials(new AuthScope(jiraHost, jiraPort), new UsernamePasswordCredentials(jira.getUsername(), jira.getPassword())); return HttpClients.custom() diff --git a/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java b/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java index 77e6881..0c11c3c 100644 --- a/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java +++ b/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java @@ -32,8 +32,10 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.sling.cli.impl.ComponentContextHelper; import org.apache.sling.cli.impl.http.HttpClientFactory; import org.apache.sling.cli.impl.release.Release; +import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -47,11 +49,17 @@ import com.google.gson.stream.JsonWriter; @Component(service = VersionClient.class) public class VersionClient { - private static final String JIRA_URL_PREFIX = "https://issues.apache.org/jira/rest/api/2/"; + private static final String DEFAULT_JIRA_URL_PREFIX = "https://issues.apache.org/jira/rest/api/2/"; private static final String CONTENT_TYPE_JSON = "application/json"; @Reference private HttpClientFactory httpClientFactory; + private String jiraUrlPrefix; + + protected void activate(ComponentContext ctx) { + ComponentContextHelper helper = ComponentContextHelper.wrap(ctx); + jiraUrlPrefix = helper.getProperty("jira.url.prefix", DEFAULT_JIRA_URL_PREFIX); + } /** * Finds a Jira version which matches the specified release @@ -98,12 +106,8 @@ public class VersionClient { try (CloseableHttpClient client = httpClientFactory.newClient()) { Optional<Version> opt = findVersion ( - v -> { - Release releaseFromVersion = Release.fromString(v.getName()).get(0); - return - releaseFromVersion.getComponent().equals(release.getComponent()) - && new org.osgi.framework.Version(releaseFromVersion.getVersion()).compareTo(new org.osgi.framework.Version(release.getVersion())) > 0; - },client); + v -> isFollowingVersion(Release.fromString(v.getName()).get(0), release) + ,client); if ( !opt.isPresent() ) return null; version = opt.get(); @@ -115,6 +119,12 @@ public class VersionClient { return version; } + private boolean isFollowingVersion(Release base, Release candidate) { + return base.getComponent().equals(candidate.getComponent()) + && new org.osgi.framework.Version(base.getVersion()) + .compareTo(new org.osgi.framework.Version(candidate.getVersion())) > 0; + } + public void create(String versionName) throws IOException { StringWriter w = new StringWriter(); try ( JsonWriter jw = new Gson().newJsonWriter(w) ) { @@ -124,7 +134,7 @@ public class VersionClient { jw.endObject(); } - HttpPost post = new HttpPost(JIRA_URL_PREFIX + "version"); + HttpPost post = new HttpPost(jiraUrlPrefix + "version"); post.addHeader("Content-Type", CONTENT_TYPE_JSON); post.addHeader("Accept", CONTENT_TYPE_JSON); post.setEntity(new StringEntity(w.toString())); @@ -190,7 +200,7 @@ public class VersionClient { } private HttpGet newGet(String suffix) { - HttpGet get = new HttpGet(JIRA_URL_PREFIX + suffix); + HttpGet get = new HttpGet(jiraUrlPrefix + suffix); get.addHeader("Accept", CONTENT_TYPE_JSON); return get; } diff --git a/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java b/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java new file mode 100644 index 0000000..903f8b0 --- /dev/null +++ b/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cli.impl.jira; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.IOUtils; +import org.junit.rules.ExternalResource; + +import com.sun.net.httpserver.HttpServer; + +public class MockJira extends ExternalResource { + + private static final Pattern VERSION_RELATED_ISSUES = Pattern.compile("^/jira/rest/api/2/version/(\\d+)/relatedIssueCounts$"); + + public static void main(String[] args) throws Throwable { + + MockJira mj = new MockJira(); + mj.before(); + System.out.println(mj.getBoundPort()); + } + + private HttpServer server; + + @Override + protected void before() throws Throwable { + server = HttpServer.create(new InetSocketAddress("localhost", 0), 0); + server.createContext("/", httpExchange -> { + + if ( httpExchange.getRequestURI().getPath().equals("/jira/rest/api/2/project/SLING/versions") ) { + httpExchange.sendResponseHeaders(200, 0); + try ( InputStream in = getClass().getResourceAsStream("/jira/versions.json"); + OutputStream out = httpExchange.getResponseBody() ) { + IOUtils.copy(in, out); + } + } else { + Matcher matcher = VERSION_RELATED_ISSUES.matcher(httpExchange.getRequestURI().getPath()); + if ( matcher.matches() ) { + int version = Integer.parseInt(matcher.group(1)); + InputStream in = getClass().getResourceAsStream("/jira/relatedIssueCounts/" + version + ".json"); + if ( in == null ) { + httpExchange.sendResponseHeaders(404, -1); + } else { + httpExchange.sendResponseHeaders(200, 0); + try ( OutputStream out = httpExchange.getResponseBody() ) { + IOUtils.copy(in, out); + } + } + } else { + httpExchange.sendResponseHeaders(400, -1); + } + } + }); + + server.start(); + } + + @Override + protected void after() { + + server.stop(0); + } + + public int getBoundPort() { + + return server.getAddress().getPort(); + } +} diff --git a/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java b/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java index 0cac72d..0d67dee 100644 --- a/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java +++ b/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java @@ -21,15 +21,9 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -import java.util.function.Function; -import org.apache.http.impl.client.CloseableHttpClient; import org.apache.sling.cli.impl.CredentialsService; import org.apache.sling.cli.impl.http.HttpClientFactory; import org.apache.sling.cli.impl.release.Release; @@ -42,10 +36,10 @@ public class VersionClientTest { private static final Map<String, String> SYSTEM_PROPS = new HashMap<>(); static { - SYSTEM_PROPS.put("asf.username", "user"); - SYSTEM_PROPS.put("asf.password", "password"); - SYSTEM_PROPS.put("jira.username", "user"); - SYSTEM_PROPS.put("jira.password", "password"); + SYSTEM_PROPS.put("asf.username", "asf-user"); + SYSTEM_PROPS.put("asf.password", "asf-password"); + SYSTEM_PROPS.put("jira.username", "jira-user"); + SYSTEM_PROPS.put("jira.password", "jira-password"); } @Rule @@ -54,17 +48,17 @@ public class VersionClientTest { @Rule public final SystemPropertiesRule sysProps = new SystemPropertiesRule(SYSTEM_PROPS); + @Rule + public final MockJira mockJira = new MockJira(); + private VersionClient versionClient; @Before public void prepareDependencies() throws ReflectiveOperationException { - context.registerInjectActivateService(new CredentialsService()); - context.registerInjectActivateService(new HttpClientFactory()); - versionClient = new StubVersionClient(); - Field factoryField = VersionClient.class.getDeclaredField("httpClientFactory"); - factoryField.setAccessible(true); - factoryField.set(versionClient, context.getService(HttpClientFactory.class)); + context.registerInjectActivateService(new CredentialsService()); + context.registerInjectActivateService(new HttpClientFactory(), "jira.host", "localhost", "jira.port", mockJira.getBoundPort()); + versionClient = context.registerInjectActivateService(new VersionClient(), "jira.url.prefix", "http://localhost:" + mockJira.getBoundPort() + "/jira/rest/api/2/"); } @Test @@ -98,28 +92,4 @@ public class VersionClientTest { assertThat("successor", successor, nullValue()); } - - private static final class StubVersionClient extends VersionClient { - @Override - protected <T> T doWithJiraVersions(CloseableHttpClient client, Function<InputStreamReader, T> parserCallback) - throws IOException { - - try ( InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/jira/versions.json")) ) { - return parserCallback.apply(reader); - } - } - - @Override - protected <T> T doWithRelatedIssueCounts(CloseableHttpClient client, Version version, - Function<InputStreamReader, T> parserCallback) throws IOException { - - InputStream stream = getClass().getResourceAsStream("/jira/relatedIssueCounts/" + version.getId()+".json"); - if ( stream == null ) - throw new IllegalArgumentException("No related issues count for version " + version.getId() + " (" + version.getName() + ")"); - - try ( InputStreamReader reader = new InputStreamReader(stream) ) { - return parserCallback.apply(reader); - } - } - } }
