tech.jhipster.lite.project.infrastructure.secondary.FileSystemProjectDownloader Maven / Gradle / Ivy
package tech.jhipster.lite.project.infrastructure.secondary;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import tech.jhipster.lite.project.domain.ProjectPath;
import tech.jhipster.lite.project.domain.download.Project;
import tech.jhipster.lite.project.domain.download.ProjectName;
import tech.jhipster.lite.shared.error.domain.Assert;
import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCoverage;
class FileSystemProjectDownloader {
private static final Pattern PROJECT_NAME_PATTERN = Pattern.compile("\"description\": *\"([^\"]+)\"");
public Optional download(ProjectPath path) {
Assert.notNull("path", path);
Path source = Paths.get(path.get());
if (notAProject(source)) {
return Optional.empty();
}
return zip(source)
.map(content -> {
ProjectName projectName = readProjectName(source);
return Optional.of(new Project(projectName, content));
})
.orElse(Optional.empty());
}
private boolean notAProject(Path source) {
return Files.notExists(source) || !Files.isDirectory(source);
}
@ExcludeFromGeneratedCodeCoverage(reason = "Hard to test exception handling")
private ProjectName readProjectName(Path source) {
Path packageJsonPath = source.resolve("package.json");
if (Files.notExists(packageJsonPath)) {
return ProjectName.DEFAULT;
}
try {
Matcher matcher = PROJECT_NAME_PATTERN.matcher(Files.readString(packageJsonPath));
if (!matcher.find()) {
return ProjectName.DEFAULT;
}
return new ProjectName(matcher.group(1));
} catch (IOException e) {
throw new ProjectZippingException(e);
}
}
@ExcludeFromGeneratedCodeCoverage(reason = "Hard to test exception handling")
private static Optional zip(Path source) {
ByteArrayOutputStream result = new ByteArrayOutputStream();
try (Stream sourceFiles = Files.walk(source); ZipOutputStream zip = new ZipOutputStream(result)) {
List files = sourceFiles.filter(regularFiles()).filter(notNodeModules()).toList();
if (files.isEmpty()) {
return Optional.empty();
}
files.forEach(appendFileEntry(source, zip));
} catch (IOException e) {
throw new ProjectZippingException(e);
}
return Optional.of(result.toByteArray());
}
private static Predicate regularFiles() {
return Files::isRegularFile;
}
private static Predicate notNodeModules() {
return path -> !path.toString().contains("/node_modules/");
}
private static Consumer appendFileEntry(Path source, ZipOutputStream zip) {
return path -> {
ZipEntry zipEntry = new ZipEntry(source.relativize(path).toString());
appendToZipEntry(zip, path, zipEntry);
};
}
@ExcludeFromGeneratedCodeCoverage(reason = "Hard to test exception handling")
private static void appendToZipEntry(ZipOutputStream zip, Path path, ZipEntry zipEntry) {
try {
zip.putNextEntry(zipEntry);
Files.copy(path, zip);
zip.closeEntry();
} catch (IOException e) {
throw new ProjectZippingException(e);
}
}
}