2
votes

I have 3 Spring-Boot applications running:

  1. Eureka:8761
  2. Spring-Cloud-Config:8080
  3. myMicroService:8181

For Spring-Cloud-Config, I am using a local git URI to populate data. The local repo is on branch master and has a file structure like this:

./myMicroService
  |-- application.properties
  |-- foo.txt
  |-- bar.txt

According to the documentation, I can access the text files like this:

http://localhost:8080/myMicroService/default/master/foo.txt http://localhost:8080/myMicroService/default/master/bar.txt

Which works, but how do I get the full list of available *.txt files served up by Spring-Cloud-Config server?

I tried this:

http://localhost:8080/myMicroService/default/master

Which only returns application.properties and its values.

2
There is not a way to do this now.spencergibb

2 Answers

2
votes

Given there is no OOTB solution for this, I added a new request mapping to my config-server:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
@RestController
public class ConfigServer {

  public static void main(String[] args) {
    SpringApplication.run(ConfigServer.class, args);
  }

  @Value("${spring.cloud.config.server.git.uri}")
  private String uri;

  @Autowired private ResourceLoader resourceLoader;

  @GetMapping("/{name}/{profile}/{label}/listFiles")
  public Collection<String> retrieve(
      @PathVariable String name,
      @PathVariable String profile,
      @PathVariable String label,
      HttpServletRequest request)
      throws IOException {
    Resource resource = resourceLoader.getResource(uri);
    String uriPath = resource.getFile().getPath();
    Path namePath = Paths.get(uriPath, name);
    String baseUrl =
        String.format(
            "http://%s:%d/%s/%s/%s",
            request.getServerName(), request.getServerPort(), name, profile, label);
    try (Stream<Path> files = Files.walk(namePath)) {
      return files
          .map(Path::toFile)
          .filter(File::isFile)
          .map(File::getName)
          .map(filename -> baseUrl + "/" + filename)
          .collect(Collectors.toList());
    }
  }
}

getting list of files for myMicroService:

curl http://localhost:8888/myMicroService/default/master/listFiles

result:

[
   "http://localhost:8888/myMicroService/default/master/application.properties",
   "http://localhost:8888/myMicroService/default/master/foo.txt",
   "http://localhost:8888/myMicroService/default/master/bar.txt"
]
0
votes

The previous solution from assumes that your spring.cloud.config.server.git.uri is of https type. The solution below will reference the local filesystem that contains the files that have been pulled from the repo and will work with any repo type. The endpoints also accept a search path and take the format of:

  • /{name}/{profile}/{label}/{path}/listFiles
  • /{name}/{profile}/{path}/listFiles?useDefaultLabel
@Configuration
@EnableAutoConfiguration
@EnableConfigServer
@RestController
public class CloudConfigApplication {

    private UrlPathHelper helper = new UrlPathHelper();

    private SearchPathLocator service;

    public static void main(String[] args) {
        SpringApplication.run(CloudConfigApplication.class, args);
    }

    public CloudConfigApplication(SearchPathLocator service) {
        this.service = service;
    }

    @RequestMapping("/{name}/{profile}/{label}/**/listFiles")
    public List<String> retrieve(@PathVariable String name, @PathVariable String profile,
                                 @PathVariable String label, ServletWebRequest request,
                                 @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
            throws IOException {
        String path = getDirPath(request, name, profile, label);
        return listAll(request, name, profile, label, path);
    }

    @RequestMapping(value = "/{name}/{profile}/**/listFiles", params = "useDefaultLabel")
    public List<String> retrieve(@PathVariable String name, @PathVariable String profile,
                                 ServletWebRequest request,
                                 @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
            throws IOException {
        String path = getDirPath(request, name, profile, null);
        return listAll(request, name, profile, null, path);
    }

    private String getDirPath(ServletWebRequest request, String name, String profile, String label) {
        String stem;
        if (label != null) {
            stem = String.format("/%s/%s/%s/", name, profile, label);
        } else {
            stem = String.format("/%s/%s/", name, profile);
        }
        String path = this.helper.getPathWithinApplication(request.getRequest());
        path = path.substring(path.indexOf(stem) + stem.length()).replace("listFiles", "");
        return path;
    }

    public synchronized List<String> listAll(ServletWebRequest request, String application, String profile, String label, String path) {
        if (StringUtils.hasText(path)) {
            String[] locations = this.service.getLocations(application, profile, label).getLocations();
            List<String> fileURIs = new ArrayList<>();

            try {
                int i = locations.length;

                while (i-- > 0) {
                    String location = String.format("%s%s", locations[i], path).replace("file:", "");
                    Path filePath = new File(location).toPath();
                    if(Files.exists(filePath)) {
                        fileURIs.addAll(Files.list(filePath).filter(file -> !Files.isDirectory(file)).map(file -> {
                            String URL =
                                    String.format(
                                            "%s://%s:%d%s/%s/%s/%s%s?useDefaultLabel",
                                            request.getRequest().getScheme(), request.getRequest().getServerName(),
                                            request.getRequest().getServerPort(), request.getRequest().getContextPath(),
                                            application, profile, path,
                                            file.getFileName().toString());
                            if(label != null) {
                                URL = String.format(
                                        "%s://%s:%d%s/%s/%s/%s/%s%s",
                                        request.getRequest().getScheme(), request.getRequest().getServerName(),
                                        request.getRequest().getServerPort(), request.getRequest().getContextPath(),
                                        application, profile, label, path,
                                        file.getFileName().toString());
                            }
                            return URL;
                        }).collect(Collectors.toList()));
                    }
                }
            } catch (IOException var11) {
                throw new NoSuchResourceException("Error : " + path + ". (" + var11.getMessage() + ")");
            }
            return fileURIs;
        }
        throw new NoSuchResourceException("Not found: " + path);
    }
}

Getting the list:

curl http://localhost:8080/context/appName/profile/label/some/path/listFiles

Example Results:

[
   "http://localhost:8080/context/appName/profile/label/some/path/robots.txt"
]