Skip to content

Commit cefb871

Browse files
Provide JSON as an option to generate report (#972)
#971
1 parent bf9dc06 commit cefb871

13 files changed

+1067
-57
lines changed

src/main/java/com/sap/oss/phosphor/fosstars/tool/Application.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ private static void checkOptionsIn(CommandLine commandLine) {
344344
}
345345

346346
if (commandLine.hasOption("report-type")
347-
&& !asList("text", "markdown").contains(commandLine.getOptionValue("report-type"))) {
347+
&& !asList("text", "markdown", "json").contains(
348+
commandLine.getOptionValue("report-type"))) {
348349

349350
throw new IllegalArgumentException(
350351
format("Unknown report type: %s", commandLine.getOptionValue("report-type")));

src/main/java/com/sap/oss/phosphor/fosstars/tool/OssProjectSecurityRatingHandler.java

+8
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import com.sap.oss.phosphor.fosstars.model.rating.oss.OssSecurityRating;
1717
import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject;
1818
import com.sap.oss.phosphor.fosstars.tool.format.Formatter;
19+
import com.sap.oss.phosphor.fosstars.tool.format.JsonPrettyPrinter;
1920
import com.sap.oss.phosphor.fosstars.tool.format.OssSecurityRatingMarkdownFormatter;
2021
import com.sap.oss.phosphor.fosstars.tool.format.PrettyPrinter;
2122
import com.sap.oss.phosphor.fosstars.tool.report.MergedJsonReporter;
23+
import com.sap.oss.phosphor.fosstars.tool.report.OssSecurityRatingJsonReporter;
2224
import com.sap.oss.phosphor.fosstars.tool.report.OssSecurityRatingMarkdownReporter;
2325
import com.sap.oss.phosphor.fosstars.tool.report.Reporter;
2426
import java.io.File;
@@ -177,6 +179,8 @@ Formatter createFormatter(String type) {
177179
return PrettyPrinter.withVerboseOutput(OSS_SECURITY_GITHUB_ADVISOR);
178180
case "markdown":
179181
return new OssSecurityRatingMarkdownFormatter(OSS_SECURITY_GITHUB_ADVISOR);
182+
case "json":
183+
return new JsonPrettyPrinter(OSS_SECURITY_GITHUB_ADVISOR);
180184
default:
181185
throw new IllegalArgumentException(format("Unsupported report type: %s", type));
182186
}
@@ -192,6 +196,10 @@ Optional<Reporter<GitHubProject>> reporterFrom(ReportConfig reportConfig) throws
192196
return Optional.of(
193197
new OssSecurityRatingMarkdownReporter(reportConfig.where, reportConfig.source,
194198
rating(), OSS_SECURITY_GITHUB_ADVISOR));
199+
case JSON_REPORT:
200+
return Optional.of(
201+
new OssSecurityRatingJsonReporter(reportConfig.where, reportConfig.source,
202+
rating(), OSS_SECURITY_GITHUB_ADVISOR));
195203
default:
196204
logger.warn("Oops! That's an unknown type of report: {}", reportConfig.type);
197205
return Optional.empty();

src/main/java/com/sap/oss/phosphor/fosstars/tool/ReportConfig.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class ReportConfig {
1111
* Types of reports.
1212
*/
1313
public enum ReportType {
14-
MARKDOWN, JSON, ISSUES
14+
MARKDOWN, JSON, ISSUES, JSON_REPORT
1515
}
1616

1717
/**

src/main/java/com/sap/oss/phosphor/fosstars/tool/format/AbstractMarkdownFormatter.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static com.sap.oss.phosphor.fosstars.tool.format.Markdown.DOUBLE_NEW_LINE;
44
import static com.sap.oss.phosphor.fosstars.tool.format.Markdown.NEW_LINE;
55
import static java.lang.String.format;
6+
import static java.util.Collections.emptyList;
67
import static java.util.stream.Collectors.toList;
78

89
import com.sap.oss.phosphor.fosstars.advice.Advice;
@@ -29,12 +30,20 @@
2930
import java.util.TreeSet;
3031
import java.util.stream.Collectors;
3132
import org.apache.commons.lang3.StringUtils;
33+
import org.apache.logging.log4j.LogManager;
34+
import org.apache.logging.log4j.Logger;
3235

3336
/**
3437
* A base class for Markdown formatters.
3538
*/
3639
public abstract class AbstractMarkdownFormatter extends CommonFormatter {
3740

41+
/**
42+
* A logger.
43+
*/
44+
private static final Logger LOGGER
45+
= LogManager.getLogger(AbstractMarkdownFormatter.class);
46+
3847
/**
3948
* Create a new formatter.
4049
*
@@ -67,7 +76,7 @@ protected MarkdownElement markdownAdviceFor(Subject subject) {
6776
throw new UncheckedIOException("Oops! Could not print advice!", e);
6877
}
6978

70-
if (adviceList.isEmpty()) {
79+
if (adviceList == null || adviceList.isEmpty()) {
7180
return MarkdownString.EMPTY;
7281
}
7382

@@ -85,6 +94,21 @@ protected MarkdownElement markdownAdviceFor(Subject subject) {
8594
return Markdown.section().with(markdownAdviceHeader()).thatContains(advice);
8695
}
8796

97+
/**
98+
* Extract advices for a subject.
99+
*
100+
* @param subject The subject.
101+
* @return Advices collected form a subject.
102+
*/
103+
protected List<Advice> adviceFor(Subject subject) {
104+
try {
105+
return advisor.adviceFor(subject);
106+
} catch (IOException e) {
107+
LOGGER.warn("Oops! Could not collect advices!", e);
108+
return emptyList();
109+
}
110+
}
111+
88112
/**
89113
* Convert links from advice to Markdown elements.
90114
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
package com.sap.oss.phosphor.fosstars.tool.format;
2+
3+
import static java.util.Collections.emptyList;
4+
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.sap.oss.phosphor.fosstars.advice.Advice;
8+
import com.sap.oss.phosphor.fosstars.advice.AdviceContent;
9+
import com.sap.oss.phosphor.fosstars.advice.Advisor;
10+
import com.sap.oss.phosphor.fosstars.model.Subject;
11+
import com.sap.oss.phosphor.fosstars.model.Value;
12+
import com.sap.oss.phosphor.fosstars.model.value.RatingValue;
13+
import com.sap.oss.phosphor.fosstars.model.value.ScoreValue;
14+
import com.sap.oss.phosphor.fosstars.tool.format.model.Advices;
15+
import com.sap.oss.phosphor.fosstars.tool.format.model.Feature;
16+
import com.sap.oss.phosphor.fosstars.tool.format.model.Rating;
17+
import com.sap.oss.phosphor.fosstars.tool.format.model.Score;
18+
import java.io.IOException;
19+
import java.io.UncheckedIOException;
20+
import java.text.DecimalFormat;
21+
import java.util.List;
22+
import java.util.stream.Collectors;
23+
import org.apache.commons.lang3.StringUtils;
24+
import org.apache.logging.log4j.LogManager;
25+
import org.apache.logging.log4j.Logger;
26+
27+
/**
28+
* The class prints a pretty rating value in JSON.
29+
*/
30+
public class JsonPrettyPrinter extends CommonFormatter {
31+
32+
/**
33+
* Object Mapper for Json.
34+
*/
35+
private static final ObjectMapper mapper = new ObjectMapper();
36+
37+
/**
38+
* A logger.
39+
*/
40+
private static final Logger LOGGER
41+
= LogManager.getLogger(JsonPrettyPrinter.class);
42+
43+
/**
44+
* A formatter for doubles.
45+
*/
46+
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#");
47+
48+
static {
49+
DECIMAL_FORMAT.setMinimumFractionDigits(1);
50+
DECIMAL_FORMAT.setMaximumFractionDigits(2);
51+
}
52+
53+
/**
54+
* Initializes a pretty printer.
55+
*
56+
* @param advisor to be added to the printed output.
57+
*/
58+
public JsonPrettyPrinter(Advisor advisor) {
59+
super(advisor);
60+
}
61+
62+
@Override
63+
public String print(Subject subject) {
64+
if (!subject.ratingValue().isPresent()) {
65+
return StringUtils.EMPTY;
66+
}
67+
RatingValue ratingValue = subject.ratingValue().get();
68+
Rating rating = from(ratingValue, subject);
69+
rating.advices(adviceFor(subject));
70+
StringBuilder output = new StringBuilder();
71+
try {
72+
output.append(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rating));
73+
} catch (JsonProcessingException e) {
74+
throw new UncheckedIOException(
75+
"Oops! Could not parse the rating value object to Json string!", e);
76+
}
77+
78+
return output.toString();
79+
}
80+
81+
/**
82+
* Extract advices for a subject.
83+
*
84+
* @param subject The subject.
85+
* @return Advices collected form a subject.
86+
*/
87+
private List<Advices> adviceFor(Subject subject) {
88+
try {
89+
return advisor.adviceFor(subject).stream().map(JsonPrettyPrinter::from)
90+
.collect(Collectors.toList());
91+
} catch (IOException e) {
92+
LOGGER.warn("Oops! Could not collect advices!", e);
93+
return emptyList();
94+
}
95+
}
96+
97+
/**
98+
* Map Advice to serializable class.
99+
*
100+
* @param advice The Advice.
101+
* @return The serializable class.
102+
*/
103+
private static Advices from(Advice advice) {
104+
AdviceContent content = advice.content();
105+
return new Advices(content.text(), content.feature().name(), content.links());
106+
}
107+
108+
/**
109+
* Format a rating value.
110+
*
111+
* @param ratingValue The rating value.
112+
* @param subject The subject.
113+
* @return A formatted rating value.
114+
*/
115+
private static Rating from(RatingValue ratingValue, Subject subject) {
116+
ScoreValue scoreValue = ratingValue.scoreValue();
117+
Rating rating = new Rating()
118+
.purl(subject.purl())
119+
.label(ratingValue.label().name());
120+
Score score = from(scoreValue);
121+
rating.score(score);
122+
return rating;
123+
}
124+
125+
/**
126+
* Extract Score from Score Value.
127+
*
128+
* @param scoreValue The score value.
129+
* @return the serializable Score.
130+
*/
131+
private static Score from(ScoreValue scoreValue) {
132+
Score score = new Score()
133+
.name(scoreValue.score().name())
134+
.value(tellMeActualValueOf(scoreValue))
135+
.confidence(printValue(scoreValue.confidence()))
136+
.weight(printValue(scoreValue.weight()));
137+
from(scoreValue, score);
138+
return score;
139+
}
140+
141+
/**
142+
* Map feature value to serializable class.
143+
*
144+
* @param featureValue The feature value.
145+
* @return The serializable class from feature value.
146+
*/
147+
private static Feature from(Value<?> featureValue) {
148+
return new Feature()
149+
.name(featureValue.feature().name())
150+
.value(tellMeActualValueOf(featureValue));
151+
}
152+
153+
/**
154+
* Extract Sub scores from the score value.
155+
*
156+
* @param scoreValue The score value to be printed.
157+
* @param score Tells if the score is a top-level score in the rating.
158+
*/
159+
private static void from(ScoreValue scoreValue, Score score) {
160+
for (Value<?> usedValue : scoreValue.usedValues()) {
161+
if (usedValue instanceof ScoreValue) {
162+
score.subScore(from((ScoreValue) usedValue));
163+
} else {
164+
score.feature(from(usedValue));
165+
}
166+
}
167+
}
168+
169+
/**
170+
* Prints an actual value of a score value. The method takes care about unknown and not-applicable
171+
* score values.
172+
*
173+
* @param scoreValue The score value.
174+
* @return A string that represents the score value.
175+
*/
176+
public static String tellMeActualValueOf(ScoreValue scoreValue) {
177+
if (scoreValue.isNotApplicable()) {
178+
return "N/A";
179+
}
180+
181+
if (scoreValue.isUnknown()) {
182+
return "unknown";
183+
}
184+
185+
return printValue(scoreValue.get());
186+
}
187+
188+
/**
189+
* Prints an actual value. The method takes care about unknown and not-applicable values.
190+
*
191+
* @param value The value.
192+
* @return A string that represents the score value.
193+
*/
194+
public static String tellMeActualValueOf(Value<?> value) {
195+
if (value.isNotApplicable()) {
196+
return "N/A";
197+
}
198+
199+
if (value.isUnknown()) {
200+
return "unknown";
201+
}
202+
203+
return String.format("%s", value.get());
204+
}
205+
206+
/**
207+
* Prints out a number with its max value.
208+
*
209+
* @param value The number.
210+
* @return A formatted string with the number and max value.
211+
*/
212+
public static String printValue(double value) {
213+
return String.format("%-4s",
214+
DECIMAL_FORMAT.format(value));
215+
}
216+
}

src/main/java/com/sap/oss/phosphor/fosstars/tool/format/OssRulesOfPlayRatingMarkdownFormatter.java

-24
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import static com.sap.oss.phosphor.fosstars.tool.format.Markdown.SPACE;
88
import static java.lang.String.format;
99
import static java.util.Arrays.asList;
10-
import static java.util.Collections.emptyList;
1110
import static java.util.Collections.emptyMap;
1211
import static java.util.Collections.unmodifiableMap;
1312
import static java.util.Objects.requireNonNull;
@@ -46,8 +45,6 @@
4645
import java.util.Set;
4746
import java.util.function.BooleanSupplier;
4847
import javax.annotation.Nullable;
49-
import org.apache.logging.log4j.LogManager;
50-
import org.apache.logging.log4j.Logger;
5148

5249
/**
5350
* The class prints a rating value
@@ -67,12 +64,6 @@ public class OssRulesOfPlayRatingMarkdownFormatter extends AbstractMarkdownForma
6764
private static final String DEFAULT_RATING_VALUE_TEMPLATE
6865
= loadFrom(RATING_VALUE_TEMPLATE_RESOURCE, OssRulesOfPlayRatingMarkdownFormatter.class);
6966

70-
/**
71-
* A logger.
72-
*/
73-
private static final Logger LOGGER
74-
= LogManager.getLogger(OssRulesOfPlayRatingMarkdownFormatter.class);
75-
7667
/**
7768
* Maps a rule to its identifier.
7869
*/
@@ -192,21 +183,6 @@ String print(RatingValue ratingValue, List<Advice> advice) {
192183
.replace("%ADVICE%", makeAdviceFrom(violations, warnings, passedRules, unclearRules));
193184
}
194185

195-
/**
196-
* Looks for advice for a subject.
197-
*
198-
* @param subject The subject.
199-
* @return A list of advice.
200-
*/
201-
private List<Advice> adviceFor(Subject subject) {
202-
try {
203-
return advisor.adviceFor(subject);
204-
} catch (IOException e) {
205-
LOGGER.warn("Oops! Could not print advice!", e);
206-
return emptyList();
207-
}
208-
}
209-
210186
/**
211187
* Convert a list of formatted rules to Markdown-formatted advice.
212188
*

0 commit comments

Comments
 (0)