Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public class ExhortExample {

// get a AnalysisReport future holding a deserialized Component Analysis report
var manifestContent = Files.readAllBytes(Paths.get("/path/to/pom.xml"));
CompletableFuture<AnalysisReport> componentReport = exhortApi.componentAnalysis("pom.xml", manifestContent);
CompletableFuture<AnalysisReport> componentReport = exhortApi.componentAnalysis("pom.xml", manifestContent, Paths.get("/path/to/pom.xml"));
}
}
```
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/redhat/exhort/Api.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.redhat.exhort.api.AnalysisReport;
import com.redhat.exhort.image.ImageRef;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -104,8 +105,8 @@ public int hashCode() {
* @return the deserialized Json report as an AnalysisReport wrapped in a CompletableFuture
* @throws IOException when failed to load the manifest content
*/
CompletableFuture<AnalysisReport> componentAnalysis(String manifestType, byte[] manifestContent)
throws IOException;
CompletableFuture<AnalysisReport> componentAnalysis(
String manifestType, byte[] manifestContent, Path manifestPath) throws IOException;

CompletableFuture<AnalysisReport> componentAnalysis(String manifestFile) throws IOException;

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/redhat/exhort/impl/ExhortApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -420,9 +421,10 @@ public static boolean debugLoggingIsNeeded() {

@Override
public CompletableFuture<AnalysisReport> componentAnalysis(
final String manifestType, final byte[] manifestContent) throws IOException {
final String manifestType, final byte[] manifestContent, final Path manifestPath)
throws IOException {
String exClientTraceId = commonHookBeginning(false);
var provider = Ecosystem.getProvider(manifestType);
var provider = Ecosystem.getProvider(manifestType, manifestPath);
var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint));
var content = provider.provideComponent(manifestContent);
commonHookAfterProviderCreatedSbomAndBeforeExhort();
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/com/redhat/exhort/tools/Ecosystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.redhat.exhort.providers.JavaMavenProvider;
import com.redhat.exhort.providers.JavaScriptNpmProvider;
import com.redhat.exhort.providers.PythonPipProvider;
import java.nio.file.Files;
import java.nio.file.Path;

/** Utility class used for instantiating providers. * */
Expand Down Expand Up @@ -55,7 +56,7 @@ private Ecosystem() {
* @return a Manifest record
*/
public static Provider getProvider(final Path manifestPath) {
return Ecosystem.getProvider(manifestPath.getFileName().toString());
return Ecosystem.getProvider(manifestPath.getFileName().toString(), manifestPath);
}

/**
Expand All @@ -64,12 +65,18 @@ public static Provider getProvider(final Path manifestPath) {
* @param manifestType the type (filename + type) of the manifest
* @return a Manifest record
*/
public static Provider getProvider(final String manifestType) {
public static Provider getProvider(final String manifestType, final Path manifestPath) {
switch (manifestType) {
case "pom.xml":
return new JavaMavenProvider();
case "package.json":
return new JavaScriptNpmProvider();
Path lockFile = manifestPath.getParent().resolve("package-lock.json");
if (Files.exists(lockFile)) {
return new JavaScriptNpmProvider();
} else {
throw new IllegalStateException(
String.format("NPM Lock file could not be found for %s", manifestType));
}
case "go.mod":
return new GoModulesProvider();
case "requirements.txt":
Expand Down
78 changes: 76 additions & 2 deletions src/test/java/com/redhat/exhort/ExhortTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@
*/
package com.redhat.exhort;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.commons.io.FileUtils;

public class ExhortTest {

Expand All @@ -36,8 +42,8 @@ protected String getStringFromFile(String... list) {
return new String(bytes);
}

public static InputStream getResourceAsStreamDecision(
Class<? extends ExhortTest> theClass, String[] list) throws IOException {
public static InputStream getResourceAsStreamDecision(Class theClass, String[] list)
throws IOException {
InputStream resourceAsStreamFromModule =
theClass.getModule().getResourceAsStream(String.join("/", list));
if (Objects.isNull(resourceAsStreamFromModule)) {
Expand Down Expand Up @@ -69,6 +75,74 @@ protected String getFileFromResource(String fileName, String... pathList) {
return tmpFile.toString();
}

public static class TempDirFromResources {
private final Path tmpDir;

public TempDirFromResources() throws IOException {
tmpDir = Files.createTempDirectory("exhort_test_");
}

public class AddPath {
private final String fileName;

public AddPath(String fileName) {
this.fileName = fileName;
}

public TempDirFromResources fromResources(String... pathList) {
Path tmpFile;
try {
tmpFile = Files.createFile(tmpDir.resolve(this.fileName));
try (var is = getResourceAsStreamDecision(super.getClass(), pathList)) {
if (Objects.nonNull(is)) {
Files.write(tmpFile, is.readAllBytes());
} else {
InputStream resourceIs =
getClass().getClassLoader().getResourceAsStream(String.join("/", pathList));
Files.write(tmpFile, resourceIs.readAllBytes());
resourceIs.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return TempDirFromResources.this;
}
}

public AddPath addFile(String fileName) {
return new AddPath(fileName);
}

public TempDirFromResources addDirectory(String dirName, String... pathList) {
File target = this.tmpDir.resolve(dirName).toFile();
String join = String.join("/", pathList);
URL resource = this.getClass().getClassLoader().getResource(join);
File source = new File(Objects.requireNonNull(resource).getFile());
try {
FileUtils.copyDirectory(source, target);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}

public TempDirFromResources addFile(
Optional<String> fileName, Supplier<List<String>> pathList) {
if (fileName.isEmpty()) {
return this;
}

return new AddPath(fileName.get()).fromResources(pathList.get().toArray(new String[0]));
}

public Path getTempDir() {
return this.tmpDir;
}
}

protected String getFileFromString(String fileName, String content) {
Path tmpFile;
try {
Expand Down
85 changes: 60 additions & 25 deletions src/test/java/com/redhat/exhort/impl/ExhortApiIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -69,7 +71,8 @@
class ExhortApiIT extends ExhortTest {

private static Api api;
private static Map<String, String> ecoSystemsManifestNames;
private static Map<String, AbstractMap.SimpleEntry<String, Optional<String>>>
ecoSystemsManifestNames;

private MockedStatic<Operations> mockedOperations;

Expand All @@ -81,17 +84,17 @@ static void beforeAll() {
ecoSystemsManifestNames =
Map.of(
"golang",
"go.mod",
new SimpleEntry<>("go.mod", Optional.empty()),
"maven",
"pom.xml",
new SimpleEntry<>("pom.xml", Optional.empty()),
"npm",
"package.json",
new SimpleEntry<>("package.json", Optional.of("package-lock.json")),
"pypi",
"requirements.txt",
new SimpleEntry<>("requirements.txt", Optional.empty()),
"gradle-groovy",
"build.gradle",
new SimpleEntry<>("build.gradle", Optional.empty()),
"gradle-kotlin",
"build.gradle.kts");
new SimpleEntry<>("build.gradle.kts", Optional.empty()));
}

@Tag("IntegrationTest")
Expand All @@ -104,19 +107,28 @@ static void afterAll() {

private static List<Arguments> scenarios() {
return ecoSystemsManifestNames.entrySet().stream()
.map(e -> Arguments.of(e.getKey(), e.getValue()))
.map(e -> Arguments.of(e.getKey(), e.getValue().getKey(), e.getValue().getValue()))
.collect(Collectors.toList());
}

@Tag("IntegrationTest")
@ParameterizedTest(name = "StackAnalysis for: {0} with manifest: {1}")
@MethodSource("scenarios")
void Integration_Test_End_To_End_Stack_Analysis(String useCase, String manifestFileName)
void Integration_Test_End_To_End_Stack_Analysis(
String useCase, String manifestFileName, Optional<String> lockFilename)
throws IOException, ExecutionException, InterruptedException {
var provider = Ecosystem.getProvider(manifestFileName);
Path manifestDirPath =
new TempDirFromResources()
.addFile(manifestFileName)
.fromResources("tst_manifests", "it", useCase, manifestFileName)
.addFile(
lockFilename,
() -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get()))
.getTempDir();
Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName);
var provider = Ecosystem.getProvider(manifestFileName, manifestFileNamePath);
var packageManager = provider.ecosystem;
String pathToManifest =
getFileFromResource(manifestFileName, "tst_manifests", "it", useCase, manifestFileName);
String pathToManifest = manifestFileNamePath.toString();
preparePythonEnvironment(packageManager);
// Github action runner with all maven and java versions seems to enter infinite loop in
// integration tests of
Expand All @@ -137,12 +149,21 @@ private void releaseStaticMock(Ecosystem.Type packageManager) {
@Tag("IntegrationTest")
@ParameterizedTest(name = "StackAnalysis Mixed for: {0} with manifest: {1}")
@MethodSource("scenarios")
void Integration_Test_End_To_End_Stack_Analysis_Mixed(String useCase, String manifestFileName)
void Integration_Test_End_To_End_Stack_Analysis_Mixed(
String useCase, String manifestFileName, Optional<String> lockFilename)
throws IOException, ExecutionException, InterruptedException {
var provider = Ecosystem.getProvider(manifestFileName);
Path manifestDirPath =
new TempDirFromResources()
.addFile(manifestFileName)
.fromResources("tst_manifests", "it", useCase, manifestFileName)
.addFile(
lockFilename,
() -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get()))
.getTempDir();
Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName);
String pathToManifest = manifestFileNamePath.toString();
var provider = Ecosystem.getProvider(manifestFileName, Path.of(pathToManifest));
var packageManager = provider.ecosystem;
String pathToManifest =
getFileFromResource(manifestFileName, "tst_manifests", "it", useCase, manifestFileName);
preparePythonEnvironment(packageManager);
// Github action runner with all maven and java versions seems to enter infinite loop in
// integration tests of
Expand All @@ -159,12 +180,21 @@ void Integration_Test_End_To_End_Stack_Analysis_Mixed(String useCase, String man
@Tag("IntegrationTest")
@ParameterizedTest(name = "StackAnalysis HTML for: {0} with manifest: {1}")
@MethodSource("scenarios")
void Integration_Test_End_To_End_Stack_Analysis_Html(String useCase, String manifestFileName)
void Integration_Test_End_To_End_Stack_Analysis_Html(
String useCase, String manifestFileName, Optional<String> lockFilename)
throws IOException, ExecutionException, InterruptedException {
var provider = Ecosystem.getProvider(manifestFileName);
Path manifestDirPath =
new TempDirFromResources()
.addFile(manifestFileName)
.fromResources("tst_manifests", "it", useCase, manifestFileName)
.addFile(
lockFilename,
() -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get()))
.getTempDir();
Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName);
String pathToManifest = manifestFileNamePath.toString();
var provider = Ecosystem.getProvider(manifestFileName, Path.of(pathToManifest));
var packageManager = provider.ecosystem;
String pathToManifest =
getFileFromResource(manifestFileName, "tst_manifests", "it", useCase, manifestFileName);
preparePythonEnvironment(packageManager);
// Github action runner with all maven and java versions seems to enter infinite loop in
// integration tests of
Expand All @@ -183,13 +213,18 @@ void Integration_Test_End_To_End_Stack_Analysis_Html(String useCase, String mani
names = {"GOLANG", "MAVEN", "NPM", "PYTHON"})
void Integration_Test_End_To_End_Component_Analysis(Ecosystem.Type packageManager)
throws IOException, ExecutionException, InterruptedException {
String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType());
byte[] manifestContent =
getStringFromFile("tst_manifests", "it", packageManager.getType(), manifestFileName)
.getBytes();
String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()).getKey();

Path tempDir =
new TempDirFromResources()
.addDirectory(packageManager.getType(), "tst_manifests", "it", packageManager.getType())
.getTempDir();
Path manifestPath = tempDir.resolve(packageManager.getType()).resolve(manifestFileName);
byte[] manifestContent = Files.readAllBytes(manifestPath);

preparePythonEnvironment(packageManager);
AnalysisReport analysisReportResult =
api.componentAnalysis(manifestFileName, manifestContent).get();
api.componentAnalysis(manifestFileName, manifestContent, manifestPath).get();
handleJsonResponse(analysisReportResult, false);
}

Expand Down
17 changes: 9 additions & 8 deletions src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -227,12 +228,12 @@ void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend()
void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend()
throws IOException, ExecutionException, InterruptedException {
// load pom.xml
byte[] targetPom;
try (var is =
getResourceAsStreamDecision(
this.getClass(), new String[] {"tst_manifests", "maven", "empty", "pom.xml"})) {
targetPom = is.readAllBytes();
}
Path tempDir =
new TempDirFromResources()
.addFile("pom.xml")
.fromResources(new String[] {"tst_manifests", "maven", "empty", "pom.xml"})
.getTempDir();
byte[] targetPom = Files.readAllBytes(tempDir.resolve("pom.xml"));

// stub the mocked provider with a fake content object
given(mockProvider.provideComponent(targetPom))
Expand Down Expand Up @@ -274,14 +275,14 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend()
// mock static getProvider utility function
try (var ecosystemTool = mockStatic(Ecosystem.class)) {
// stub static getProvider utility function to return our mock provider
ecosystemTool.when(() -> Ecosystem.getProvider("pom.xml")).thenReturn(mockProvider);
ecosystemTool.when(() -> Ecosystem.getProvider("pom.xml", tempDir)).thenReturn(mockProvider);

// stub the http client to return our mocked response when request matches our arg matcher
given(mockHttpClient.sendAsync(argThat(matchesRequest), any()))
.willReturn(CompletableFuture.completedFuture(mockHttpResponse));

// when invoking the api for a json stack analysis report
var responseAnalysis = exhortApiSut.componentAnalysis("pom.xml", targetPom);
var responseAnalysis = exhortApiSut.componentAnalysis("pom.xml", targetPom, tempDir);
// verify we got the correct analysis report
then(responseAnalysis.get()).isEqualTo(expectedReport);
}
Expand Down
Loading