Added support for the stubs:// protocol
Currently Stub Runner can fetch a JAR, unpack it and only then pick the contract and stubs. Sometimes, what you want to achieve, is to point to a given folder where the stubs are already unpacked. Another option is a multimodule project, where the stubs are generated and then the user would like to just reference those instead of installing a JAR in a local m2. With this change we suggest adding a protocol called stubs that would allow to define a path where the unpacked contracts and stub lay. fixes gh-1150
This commit is contained in:
@@ -16,19 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -36,9 +23,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -73,188 +58,37 @@ public class ClasspathStubProvider implements StubDownloaderBuilder {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ClasspathStubProvider.class);
|
||||
|
||||
private static final int TEMP_DIR_ATTEMPTS = 10000;
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
new DefaultResourceLoader());
|
||||
|
||||
@Override
|
||||
public StubDownloader build(final StubRunnerOptions stubRunnerOptions) {
|
||||
if (stubRunnerOptions.stubsMode != StubRunnerProperties.StubsMode.CLASSPATH) {
|
||||
return null;
|
||||
}
|
||||
log.info("Will download stubs from classpath");
|
||||
return new StubDownloader() {
|
||||
@Override
|
||||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||||
StubConfiguration config) {
|
||||
List<RepoRoot> repoRoots = repoRoot(stubRunnerOptions, config);
|
||||
List<String> paths = toPaths(repoRoots);
|
||||
List<Resource> resources = resolveResources(paths);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("For paths " + paths + " found following resources "
|
||||
+ resources);
|
||||
}
|
||||
if (resources.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"No stubs were found on classpath for [" + config.getGroupId()
|
||||
+ ":" + config.getArtifactId() + "]");
|
||||
}
|
||||
final File tmp = createTempDir();
|
||||
if (stubRunnerOptions.isDeleteStubsAfterTest()) {
|
||||
tmp.deleteOnExit();
|
||||
}
|
||||
Pattern groupAndArtifactPattern = Pattern.compile("^(.*)("
|
||||
+ config.getGroupId() + "." + config.getArtifactId() + ")(.*)$");
|
||||
String version = config.getVersion();
|
||||
for (Resource resource : resources) {
|
||||
try {
|
||||
String relativePath = relativePathPicker(resource,
|
||||
groupAndArtifactPattern);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Relative path for resource is [" + relativePath
|
||||
+ "]");
|
||||
}
|
||||
// the relative path is OS agnostic and contains / only
|
||||
int lastIndexOf = relativePath.lastIndexOf("/");
|
||||
String relativePathWithoutFile = lastIndexOf > -1
|
||||
? relativePath.substring(0, lastIndexOf) : relativePath;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Relative path without file name is ["
|
||||
+ relativePathWithoutFile + "]");
|
||||
}
|
||||
Path directory = Files.createDirectories(
|
||||
new File(tmp, relativePathWithoutFile).toPath());
|
||||
File newFile = new File(directory.toFile(),
|
||||
resource.getFilename());
|
||||
if (!newFile.exists() && !isDirectory(resource)) {
|
||||
try (InputStream stream = resource.getInputStream()) {
|
||||
Files.copy(stream, newFile.toPath());
|
||||
}
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stored file [" + newFile + "]");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Exception occurred while trying to create dirs", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
log.info("Unpacked files for [" + config.getGroupId() + ":"
|
||||
+ config.getArtifactId() + ":" + version + "] to folder [" + tmp
|
||||
+ "]");
|
||||
return new AbstractMap.SimpleEntry<>(
|
||||
new StubConfiguration(config.getGroupId(), config.getArtifactId(),
|
||||
version, config.getClassifier()),
|
||||
tmp);
|
||||
}
|
||||
|
||||
boolean isDirectory(Resource resource) {
|
||||
try {
|
||||
return resource.getFile().isDirectory();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(
|
||||
"Exception occurred while trying to convert path to file for resource ["
|
||||
+ resource + "]",
|
||||
e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String relativePathPicker(Resource resource, Pattern groupAndArtifactPattern)
|
||||
throws IOException {
|
||||
String uri = resource.getURI().toString();
|
||||
Matcher groupAndArtifactMatcher = groupAndArtifactPattern.matcher(uri);
|
||||
if (groupAndArtifactMatcher.matches()) {
|
||||
MatchResult groupAndArtifactResult = groupAndArtifactMatcher
|
||||
.toMatchResult();
|
||||
return groupAndArtifactResult.group(2)
|
||||
+ groupAndArtifactResult.group(3);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Illegal uri [" + uri + "]");
|
||||
}
|
||||
}
|
||||
};
|
||||
return new ResourceResolvingStubDownloader(stubRunnerOptions, this::repoRoot,
|
||||
this::gavPattern);
|
||||
}
|
||||
|
||||
private List<String> toPaths(List<RepoRoot> repoRoots) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (RepoRoot repoRoot : repoRoots) {
|
||||
list.add(repoRoot.fullPath);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
List<Resource> resolveResources(List<String> paths) {
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
for (String path : paths) {
|
||||
try {
|
||||
List<Resource> list = Arrays.asList(this.resolver.getResources(path));
|
||||
resources.addAll(list);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Exception occurred while trying to fetch resources from ["
|
||||
+ path + "]");
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
private List<RepoRoot> repoRoot(StubRunnerOptions stubRunnerOptions,
|
||||
private RepoRoots repoRoot(StubRunnerOptions stubRunnerOptions,
|
||||
StubConfiguration configuration) {
|
||||
Resource repositoryRoot = stubRunnerOptions.getStubRepositoryRoot();
|
||||
if (repositoryRoot instanceof ClassPathResource) {
|
||||
ClassPathResource classPathResource = (ClassPathResource) repositoryRoot;
|
||||
String path = classPathResource.getPath();
|
||||
if (StringUtils.hasText(path)) {
|
||||
return Collections.singletonList(
|
||||
return RepoRoots.asList(
|
||||
new RepoRoot(stubRunnerOptions.getStubRepositoryRootAsString()));
|
||||
}
|
||||
}
|
||||
String path = "/**/" + configuration.getGroupId() + "/"
|
||||
+ configuration.getArtifactId();
|
||||
return Arrays.asList(new RepoRoot("classpath*:/META-INF" + path, "/**/*.*"),
|
||||
return RepoRoots.asList(new RepoRoot("classpath*:/META-INF" + path, "/**/*.*"),
|
||||
new RepoRoot("classpath*:/contracts" + path, "/**/*.*"),
|
||||
new RepoRoot("classpath*:/mappings" + path, "/**/*.*"));
|
||||
}
|
||||
|
||||
// Taken from Guava
|
||||
private File createTempDir() {
|
||||
File baseDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
String baseName = System.currentTimeMillis() + "-";
|
||||
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
|
||||
File tempDir = new File(baseDir, baseName + counter);
|
||||
if (tempDir.mkdir()) {
|
||||
return tempDir;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Failed to create directory within "
|
||||
+ TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName
|
||||
+ (TEMP_DIR_ATTEMPTS - 1) + ")");
|
||||
}
|
||||
|
||||
private static class RepoRoot {
|
||||
|
||||
final String repoRoot;
|
||||
|
||||
final String fullPath;
|
||||
|
||||
RepoRoot(String repoRoot) {
|
||||
this.repoRoot = repoRoot;
|
||||
this.fullPath = repoRoot + "";
|
||||
}
|
||||
|
||||
RepoRoot(String repoRoot, String suffix) {
|
||||
this.repoRoot = repoRoot;
|
||||
this.fullPath = repoRoot + suffix;
|
||||
}
|
||||
|
||||
private Pattern gavPattern(StubConfiguration config) {
|
||||
String ga = config.getGroupId() + "." + config.getArtifactId();
|
||||
return Pattern.compile("^(.*)(" + ga + ")(.*)$");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.contract.stubrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Allows to read stubs and contracts from a given location. Contrary to
|
||||
* {@link org.springframework.cloud.contract.stubrunner.AetherStubDownloaderBuilder},
|
||||
* doesn't require the location to be a maven repository.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class FileStubDownloader implements StubDownloaderBuilder {
|
||||
|
||||
private static final List<String> ACCEPTABLE_PROTOCOLS = Collections
|
||||
.singletonList("stubs");
|
||||
|
||||
/**
|
||||
* Does any of the accepted protocols matches the URL of the repository.
|
||||
* @param url - of the repository
|
||||
* @return {@code true} if protocol is accepted
|
||||
*/
|
||||
public static boolean isProtocolAccepted(String url) {
|
||||
return ACCEPTABLE_PROTOCOLS.stream().anyMatch(url::startsWith);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
|
||||
// should work only in remote and local option
|
||||
if (stubRunnerOptions.getStubsMode() == StubRunnerProperties.StubsMode.CLASSPATH
|
||||
|| stubRunnerOptions.getStubRepositoryRoot() == null) {
|
||||
return null;
|
||||
}
|
||||
Resource resource = stubRunnerOptions.getStubRepositoryRoot();
|
||||
// we verify whether the protocol starts with `stubs://`
|
||||
if (!(resource instanceof StubsResource)) {
|
||||
return null;
|
||||
}
|
||||
return new StubsStubDownloader(stubRunnerOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource resolve(String location, ResourceLoader resourceLoader) {
|
||||
if (StringUtils.isEmpty(location) || !isProtocolAccepted(location)) {
|
||||
return null;
|
||||
}
|
||||
return new StubsResource(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitive version of a Stubs {@link Resource}. Automatically makes Spring convert the
|
||||
* URL to a Resource.
|
||||
*/
|
||||
class StubsResource extends AbstractResource {
|
||||
|
||||
private final String rawLocation;
|
||||
|
||||
StubsResource(String location) {
|
||||
this.rawLocation = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.rawLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() throws IOException {
|
||||
return URI.create(this.rawLocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Concrete logic of picking stubs.
|
||||
*/
|
||||
class StubsStubDownloader implements StubDownloader {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StubsStubDownloader.class);
|
||||
|
||||
private static final String STUBS_FIND_PRODUCER_PROPERTY = "stubs.find-producer";
|
||||
|
||||
private static final String LATEST_VERSION = "+";
|
||||
|
||||
private final StubRunnerOptions stubRunnerOptions;
|
||||
|
||||
StubsStubDownloader(StubRunnerOptions stubRunnerOptions) {
|
||||
this.stubRunnerOptions = stubRunnerOptions;
|
||||
}
|
||||
|
||||
// StubConfiguration is the concrete stub to be fetched
|
||||
@Override
|
||||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||||
StubConfiguration stubConfiguration) {
|
||||
boolean shouldFindProducer = shouldFindProducer();
|
||||
if (!shouldFindProducer) {
|
||||
String schemeSpecific = schemeSpecificPart();
|
||||
log.info("Stubs are present under [" + schemeSpecific + "]");
|
||||
return new AbstractMap.SimpleEntry<>(stubConfiguration,
|
||||
new File(schemeSpecific));
|
||||
}
|
||||
return new ResourceResolvingStubDownloader(stubRunnerOptions, this::repoRoot,
|
||||
this::gavPattern).downloadAndUnpackStubJar(stubConfiguration);
|
||||
}
|
||||
|
||||
// for group id a.b.c and artifact id d
|
||||
// a.b.c/d
|
||||
// a/b/c/d
|
||||
private RepoRoots repoRoot(StubRunnerOptions stubRunnerOptions,
|
||||
StubConfiguration configuration) {
|
||||
String pathWithGroupAndArtifactId = "/" + configuration.getGroupId() + "/"
|
||||
+ configuration.getArtifactId();
|
||||
String pathWithGroupAndArtifactIdSlashSeparated = "/"
|
||||
+ configuration.getGroupId().replace(".", File.separator) + "/"
|
||||
+ configuration.getArtifactId();
|
||||
String anyFileSuffix = "/**/*.*";
|
||||
RepoRoots roots = RepoRoots.asList(
|
||||
new RepoRoot(schemeSpecificPart() + pathWithGroupAndArtifactId,
|
||||
anyFileSuffix),
|
||||
new RepoRoot(
|
||||
schemeSpecificPart() + pathWithGroupAndArtifactIdSlashSeparated,
|
||||
anyFileSuffix),
|
||||
new RepoRoot(schemeSpecificPart() + anyFileSuffix));
|
||||
if (!latestVersionIsSet(configuration)) {
|
||||
String pathWithGAV = pathWithGroupAndArtifactId + "/"
|
||||
+ configuration.getVersion();
|
||||
String pathWithSlashSeparatedGAV = pathWithGroupAndArtifactIdSlashSeparated
|
||||
+ "/" + configuration.getVersion();
|
||||
roots.addAll(RepoRoots.asList(
|
||||
new RepoRoot(schemeSpecificPart() + pathWithGAV, anyFileSuffix),
|
||||
new RepoRoot(schemeSpecificPart() + pathWithSlashSeparatedGAV,
|
||||
anyFileSuffix)));
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
private Pattern gavPattern(StubConfiguration config) {
|
||||
String version = config.getVersion();
|
||||
String ga = config.getGroupId() + "." + config.getArtifactId();
|
||||
String gav = latestVersionIsSet(config) ? ga : (ga + "." + version);
|
||||
return Pattern.compile("^(.*)(" + gav + ")(.*)$");
|
||||
}
|
||||
|
||||
private boolean latestVersionIsSet(StubConfiguration configuration) {
|
||||
return LATEST_VERSION.equals(configuration.getVersion());
|
||||
}
|
||||
|
||||
private boolean shouldFindProducer() {
|
||||
Map<String, String> args = this.stubRunnerOptions.getProperties();
|
||||
String findProducer = StubRunnerPropertyUtils.getProperty(args,
|
||||
STUBS_FIND_PRODUCER_PROPERTY);
|
||||
return Boolean.parseBoolean(findProducer);
|
||||
}
|
||||
|
||||
// stubs://foo -> foo
|
||||
private String schemeSpecificPart() {
|
||||
try {
|
||||
String part = this.stubRunnerOptions.getStubRepositoryRoot().getURI()
|
||||
.getSchemeSpecificPart();
|
||||
if (StringUtils.isEmpty(part)) {
|
||||
return part;
|
||||
}
|
||||
return part.startsWith("//") ? part.substring(2) : part;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.contract.stubrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import shaded.com.google.common.base.Function;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
class ResourceResolvingStubDownloader implements StubDownloader {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ResourceResolvingStubDownloader.class);
|
||||
|
||||
private static final int TEMP_DIR_ATTEMPTS = 10000;
|
||||
|
||||
private static final String LATEST_VERSION = "+";
|
||||
|
||||
private final StubRunnerOptions stubRunnerOptions;
|
||||
|
||||
private final BiFunction<StubRunnerOptions, StubConfiguration, RepoRoots> repoRootFunction;
|
||||
|
||||
private final Function<StubConfiguration, Pattern> gavPattern;
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
new DefaultResourceLoader());
|
||||
|
||||
ResourceResolvingStubDownloader(StubRunnerOptions stubRunnerOptions,
|
||||
BiFunction<StubRunnerOptions, StubConfiguration, RepoRoots> repoRootFunction,
|
||||
Function<StubConfiguration, Pattern> gavPattern) {
|
||||
this.stubRunnerOptions = stubRunnerOptions;
|
||||
this.repoRootFunction = repoRootFunction;
|
||||
this.gavPattern = gavPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||||
StubConfiguration config) {
|
||||
List<RepoRoot> repoRoots = repoRootFunction.apply(stubRunnerOptions, config);
|
||||
List<String> paths = toPaths(repoRoots);
|
||||
List<Resource> resources = resolveResources(paths);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("For paths " + paths + " found following resources " + resources);
|
||||
}
|
||||
if (resources.isEmpty()) {
|
||||
throw new IllegalStateException("No stubs were found on classpath for ["
|
||||
+ config.getGroupId() + ":" + config.getArtifactId() + "]");
|
||||
}
|
||||
final File tmp = createTempDir();
|
||||
if (stubRunnerOptions.isDeleteStubsAfterTest()) {
|
||||
tmp.deleteOnExit();
|
||||
}
|
||||
boolean atLeastOneFound = false;
|
||||
for (Resource resource : resources) {
|
||||
try {
|
||||
String relativePath = relativePathPicker(resource,
|
||||
this.gavPattern.apply(config));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Relative path for resource is [" + relativePath + "]");
|
||||
}
|
||||
if (StringUtils.isEmpty(relativePath)) {
|
||||
log.warn("Unable to match the URI [" + resource.getURI() + "]");
|
||||
continue;
|
||||
}
|
||||
atLeastOneFound = true;
|
||||
copyTheFoundFiles(tmp, resource, relativePath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Exception occurred while trying to create dirs", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
if (!atLeastOneFound) {
|
||||
log.warn("Didn't find any matching stubs");
|
||||
return null;
|
||||
}
|
||||
log.info("Unpacked files for [" + config.getGroupId() + ":"
|
||||
+ config.getArtifactId() + ":" + config.getVersion() + "] to folder ["
|
||||
+ tmp + "]");
|
||||
return new AbstractMap.SimpleEntry<>(new StubConfiguration(config.getGroupId(),
|
||||
config.getArtifactId(), config.getVersion(), config.getClassifier()),
|
||||
tmp);
|
||||
}
|
||||
|
||||
private void copyTheFoundFiles(File tmp, Resource resource, String relativePath)
|
||||
throws IOException {
|
||||
// the relative path is OS agnostic and contains / only
|
||||
int lastIndexOf = relativePath.lastIndexOf("/");
|
||||
String relativePathWithoutFile = lastIndexOf > -1
|
||||
? relativePath.substring(0, lastIndexOf) : relativePath;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Relative path without file name is [" + relativePathWithoutFile
|
||||
+ "]");
|
||||
}
|
||||
Path directory = Files
|
||||
.createDirectories(new File(tmp, relativePathWithoutFile).toPath());
|
||||
File newFile = new File(directory.toFile(), resource.getFilename());
|
||||
if (!newFile.exists() && !isDirectory(resource)) {
|
||||
try (InputStream stream = resource.getInputStream()) {
|
||||
Files.copy(stream, newFile.toPath());
|
||||
}
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stored file [" + newFile + "]");
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDirectory(Resource resource) {
|
||||
try {
|
||||
return resource.getFile().isDirectory();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(
|
||||
"Exception occurred while trying to convert path to file for resource ["
|
||||
+ resource + "]",
|
||||
e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String relativePathPicker(Resource resource, Pattern groupAndArtifactPattern)
|
||||
throws IOException {
|
||||
String uri = resource.getURI().toString();
|
||||
Matcher groupAndArtifactMatcher = groupAndArtifactPattern.matcher(uri);
|
||||
if (groupAndArtifactMatcher.matches()) {
|
||||
MatchResult groupAndArtifactResult = groupAndArtifactMatcher.toMatchResult();
|
||||
return groupAndArtifactResult.group(2) + groupAndArtifactResult.group(3);
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> toPaths(List<RepoRoot> repoRoots) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (RepoRoot repoRoot : repoRoots) {
|
||||
list.add(repoRoot.fullPath);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
List<Resource> resolveResources(List<String> paths) {
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
for (String path : paths) {
|
||||
try {
|
||||
List<Resource> list = Arrays.asList(this.resolver.getResources(path));
|
||||
resources.addAll(list);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Exception occurred while trying to fetch resources from ["
|
||||
+ path + "]");
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
// Taken from Guava
|
||||
private File createTempDir() {
|
||||
File baseDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
String baseName = System.currentTimeMillis() + "-";
|
||||
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
|
||||
File tempDir = new File(baseDir, baseName + counter);
|
||||
if (tempDir.mkdir()) {
|
||||
return tempDir;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Failed to create directory within "
|
||||
+ TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName
|
||||
+ (TEMP_DIR_ATTEMPTS - 1) + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RepoRoot {
|
||||
|
||||
final String repoRoot;
|
||||
|
||||
final String fullPath;
|
||||
|
||||
RepoRoot(String repoRoot) {
|
||||
this.repoRoot = repoRoot;
|
||||
this.fullPath = repoRoot;
|
||||
}
|
||||
|
||||
RepoRoot(String repoRoot, String suffix) {
|
||||
this.repoRoot = repoRoot;
|
||||
this.fullPath = repoRoot + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RepoRoots extends LinkedList<RepoRoot> {
|
||||
|
||||
RepoRoots() {
|
||||
}
|
||||
|
||||
RepoRoots(Collection<? extends RepoRoot> c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
static RepoRoots asList(RepoRoot... repoRoots) {
|
||||
return new RepoRoots(Arrays.asList(repoRoots));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class StubDownloaderBuilderProvider {
|
||||
|
||||
List<StubDownloaderBuilder> defaultStubDownloaderBuilders() {
|
||||
return Arrays.asList(new ScmStubDownloaderBuilder(), new ClasspathStubProvider(),
|
||||
new AetherStubDownloaderBuilder());
|
||||
new FileStubDownloader(), new AetherStubDownloaderBuilder());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.contract.stubrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.Test;
|
||||
|
||||
public class StubsStubDownloaderTests {
|
||||
|
||||
URL url = StubsStubDownloaderTests.class.getResource("/repository/mappings");
|
||||
|
||||
@Test
|
||||
public void should_pick_stubs_from_a_given_location() {
|
||||
String path = url.getPath();
|
||||
StubRunnerOptions options = new StubRunnerOptionsBuilder()
|
||||
.withStubRepositoryRoot("stubs://file://" + path).build();
|
||||
StubsStubDownloader downloader = new StubsStubDownloader(options);
|
||||
|
||||
Map.Entry<StubConfiguration, File> entry = downloader
|
||||
.downloadAndUnpackStubJar(new StubConfiguration("lv.spring.cloud:bye"));
|
||||
|
||||
BDDAssertions.then(entry).isNotNull();
|
||||
BDDAssertions.then(entry.getValue().getPath()).endsWith("repository/mappings");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pick_stubs_from_a_given_location_for_a_find_producer_with_ga() {
|
||||
String path = url.getPath();
|
||||
StubRunnerOptions options = new StubRunnerOptionsBuilder()
|
||||
.withStubRepositoryRoot("stubs://file://" + path)
|
||||
.withProperties(propsWithFindProducer()).build();
|
||||
StubsStubDownloader downloader = new StubsStubDownloader(options);
|
||||
|
||||
Map.Entry<StubConfiguration, File> entry = downloader
|
||||
.downloadAndUnpackStubJar(new StubConfiguration("lv.spring.cloud:bye"));
|
||||
|
||||
BDDAssertions.then(entry).isNotNull();
|
||||
File stub = new File(entry.getValue().getPath(),
|
||||
"lv/spring/cloud/bye/lv_bye.json");
|
||||
BDDAssertions.then(stub).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pick_stubs_from_a_given_location_for_a_find_producer_with_gav() {
|
||||
String path = url.getPath();
|
||||
StubRunnerOptions options = new StubRunnerOptionsBuilder()
|
||||
.withStubRepositoryRoot("stubs://file://" + path)
|
||||
.withProperties(propsWithFindProducer()).build();
|
||||
StubsStubDownloader downloader = new StubsStubDownloader(options);
|
||||
|
||||
Map.Entry<StubConfiguration, File> entry = downloader
|
||||
.downloadAndUnpackStubJar(new StubConfiguration("lv.spring:cloud:bye"));
|
||||
|
||||
BDDAssertions.then(entry).isNotNull();
|
||||
File stub = new File(entry.getValue().getPath(),
|
||||
"lv/spring/cloud/bye/lv_bye.json");
|
||||
BDDAssertions.then(stub).exists();
|
||||
}
|
||||
|
||||
private Map<String, String> propsWithFindProducer() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("stubs.find-producer", "true");
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user