Repository: adessose/budgeteer
Issue: contract: Contract summary creation is missing null safety when accessing attributes

If a Contract is missing the custom field value "rechnungsempfaenger" as required by ContractReportService.createSummary, it exits with an internal error, since ContractReportService.getAttribute is missing null safety.

@tinne attached this stack trace:
[budgeteer.-.ContractReportService.log](https://github.com/adessoAG/budgeteer/files/5545655/budgeteer.-.ContractReportService.log)


Changed files:
budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/budget/report/BudgetReportService.java
budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/contract/report/ContractReportService.java
budgeteer-report-exporter/src/main/java/org/wickedsource/budgeteer/SheetTemplate/SheetTemplateSerializable.java

diff --git a/budgeteer-report-exporter/src/main/java/org/wickedsource/budgeteer/SheetTemplate/SheetTemplateSerializable.java b/budgeteer-report-exporter/src/main/java/org/wickedsource/budgeteer/SheetTemplate/SheetTemplateSerializable.java
index 55e50c1b..86503fdb 100644
--- a/budgeteer-report-exporter/src/main/java/org/wickedsource/budgeteer/SheetTemplate/SheetTemplateSerializable.java
+++ b/budgeteer-report-exporter/src/main/java/org/wickedsource/budgeteer/SheetTemplate/SheetTemplateSerializable.java
@@ -1,6 +1,22 @@
 package org.wickedsource.budgeteer.SheetTemplate;
 
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Stream;
+
 public interface SheetTemplateSerializable {
 	public String getName();
 	public Object getValue();
+
+	static String getAttribute(String attribute, Collection<? extends SheetTemplateSerializable> collection) {
+		Stream<? extends SheetTemplateSerializable> stream = collection == null ? Stream.empty() : collection.stream();
+
+		return stream
+				.filter(entry -> entry.getName().equals(attribute))
+				.map(SheetTemplateSerializable::getValue)
+				.filter(Objects::nonNull)
+				.map(Object::toString)
+				.findFirst()
+				.orElse("");
+	}
 }
diff --git a/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/budget/report/BudgetReportService.java b/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/budget/report/BudgetReportService.java
index 30f4a1ad..8b6a9096 100644
--- a/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/budget/report/BudgetReportService.java
+++ b/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/budget/report/BudgetReportService.java
@@ -1,7 +1,6 @@
 package org.wickedsource.budgeteer.service.budget.report;
 
 import org.apache.poi.ss.formula.eval.NotImplementedException;
-import org.apache.poi.ss.formula.eval.NotImplementedFunctionException;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
@@ -29,10 +28,7 @@ import java.io.IOException;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.time.temporal.ChronoUnit;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
@@ -113,7 +109,7 @@ public class BudgetReportService {
 	private List<BudgetSummary> createBudgetSummary(List<BudgetReportData> budgetList) {
         Set<String> recipients = new HashSet<>();
         budgetList.forEach(
-				budget -> recipients.add(getAttribute("rechnungsempfaenger", budget.getAttributes())));
+				budget -> recipients.add(SheetTemplateSerializable.getAttribute("rechnungsempfaenger", budget.getAttributes())));
 
 		List<BudgetSummary> summary = recipients.stream().map(description -> new BudgetSummary(description))
 				.collect(Collectors.toList());
@@ -124,18 +120,6 @@ public class BudgetReportService {
     	return templateService.getById(id).getWb();
 	}
 
-	private String getAttribute(String string, List<? extends SheetTemplateSerializable> list) {
-		if (null == list) {
-			return "";
-		}
-		for (SheetTemplateSerializable listEntry : list) {
-			if (listEntry.getName().equals(string)) {
-				return listEntry.getValue().toString();
-			}
-		}
-		return "";
-	}
-
 	private File createOutputFile(XSSFWorkbook wb) {
 		File outputFile = null;
         FileOutputStream out;
diff --git a/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/contract/report/ContractReportService.java b/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/contract/report/ContractReportService.java
index 1f72158a..9944c267 100644
--- a/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/contract/report/ContractReportService.java
+++ b/budgeteer-web-interface/src/main/java/org/wickedsource/budgeteer/service/contract/report/ContractReportService.java
@@ -18,6 +18,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Transactional
 @Service
@@ -69,25 +70,13 @@ public class ContractReportService {
 	private List<ContractReportSummary> createSummary(List<ContractReportData> contractReportList) {
 		Set<String> recipients = new HashSet<>();
 		contractReportList.forEach(
-				contract -> recipients.add(getAttribute("rechnungsempfaenger", contract.getAttributes())));
+				contract -> recipients.add(SheetTemplateSerializable.getAttribute("rechnungsempfaenger", contract.getAttributes())));
 
 		List<ContractReportSummary> summary = recipients.stream().map(description -> new ContractReportSummary(description))
 				.collect(Collectors.toList());
 		return summary;
 	}
 
-	private String getAttribute(String string, List<? extends SheetTemplateSerializable> list) {
-		if (null == list) {
-			return "";
-		}
-		for (SheetTemplateSerializable listEntry : list) {
-			if (listEntry.getName().equals(string)) {
-				return listEntry.getValue().toString();
-			}
-		}
-		return "";
-	}
-
 	private File outputfile(XSSFWorkbook wb) {
 		File outputFile = null;
 		FileOutputStream out;