Skip to content

Commit 5dfe930

Browse files
committed
🚧 Code doesn't yet compile, but main flow is implemented.
TODO: - Add save in patcher - Allow display in partial retrieval Enhancements: - Remove Tuple usage from main signature -
1 parent 0e6fe90 commit 5dfe930

9 files changed

Lines changed: 366 additions & 12 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/patch/DataPatchFailureCause.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ public enum DataPatchFailureCause {
8080
*/
8181
FORBIDDEN_MULTI_GROUP_FIELD,
8282

83+
INVALID_CUSTOM_CONTEXT,
84+
8385
/**
8486
* This means there is a hole in the implementation.
8587
*/

sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,45 @@ private Predicate<Map.Entry<PatchField, Object>> buildEmptyValuePredicate() {
428428
// TODO: Extract & Rename
429429
public static final class SingleFieldPatchResult {
430430

431-
final PatchField field;
432-
final DataPatchFailureCause failureCause;
433-
final Object value;
431+
private PatchField field;
432+
private DataPatchFailureCause failureCause;
433+
private Object value;
434434

435435
SingleFieldPatchResult(PatchField fieldPath, DataPatchFailureCause cause, Object value) {
436436
this.field = fieldPath;
437437
this.failureCause = cause;
438438
this.value = value;
439439
}
440+
441+
public SingleFieldPatchResult() {
442+
}
443+
444+
public SingleFieldPatchResult setField(PatchField field) {
445+
this.field = field;
446+
return this;
447+
}
448+
449+
public SingleFieldPatchResult setFailureCause(DataPatchFailureCause failureCause) {
450+
this.failureCause = failureCause;
451+
return this;
452+
}
453+
454+
public SingleFieldPatchResult setValue(Object value) {
455+
this.value = value;
456+
return this;
457+
}
458+
459+
public PatchField getField() {
460+
return field;
461+
}
462+
463+
public DataPatchFailureCause getFailureCause() {
464+
return failureCause;
465+
}
466+
467+
public Object getValue() {
468+
return value;
469+
}
440470
}
441471

442472
}

sormas-backend/src/main/java/de/symeda/sormas/backend/patch/customizablefield/CustomizableFieldContextPatchMapping.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package de.symeda.sormas.backend.patch.customizablefield;
22

3+
import java.util.Arrays;
4+
import java.util.Map;
5+
import java.util.stream.Collectors;
6+
37
import de.symeda.sormas.api.caze.CaseDataDto;
48
import de.symeda.sormas.api.customizablefield.CustomizableFieldContext;
59
import de.symeda.sormas.api.epidata.EpiDataDto;
@@ -17,6 +21,10 @@ public enum CustomizableFieldContextPatchMapping {
1721
private final CustomizableFieldContext customizableFieldContext;
1822
private final String patchName;
1923

24+
public static final Map<String, CustomizableFieldContext> I18N_DICTIONARY = Arrays.stream(CustomizableFieldContextPatchMapping.values())
25+
.collect(
26+
Collectors.toMap(CustomizableFieldContextPatchMapping::getPatchName, CustomizableFieldContextPatchMapping::getCustomizableFieldContext));
27+
2028
CustomizableFieldContextPatchMapping(CustomizableFieldContext customizableFieldContext, String patchName) {
2129
this.customizableFieldContext = customizableFieldContext;
2230
this.patchName = patchName;

sormas-backend/src/main/java/de/symeda/sormas/backend/patch/customizablefield/CustomizableFieldDataPatcher.java

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,33 @@
22

33
import java.util.List;
44
import java.util.Map;
5+
import java.util.Optional;
6+
import java.util.Set;
7+
import java.util.stream.Collectors;
58

69
import javax.ejb.EJB;
710
import javax.enterprise.context.ApplicationScoped;
811
import javax.inject.Inject;
912
import javax.validation.constraints.NotNull;
1013

1114
import org.apache.commons.collections4.CollectionUtils;
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
1217

1318
import de.symeda.sormas.api.caze.CaseDataDto;
1419
import de.symeda.sormas.api.customizablefield.CustomizableFieldContext;
1520
import de.symeda.sormas.api.customizablefield.CustomizableFieldMetadataDto;
1621
import de.symeda.sormas.api.customizablefield.CustomizableFieldValueDto;
22+
import de.symeda.sormas.api.externalmessage.survey.PatchField;
1723
import de.symeda.sormas.api.patch.CaseDataPatchRequest;
24+
import de.symeda.sormas.api.patch.DataPatchFailureCause;
1825
import de.symeda.sormas.api.patch.SinglePatchResult;
26+
import de.symeda.sormas.api.patch.mapping.ValueMappingResult;
1927
import de.symeda.sormas.api.utils.Tuple;
2028
import de.symeda.sormas.backend.customizablefield.CustomizableFieldValueFacadeEjb;
2129
import de.symeda.sormas.backend.patch.DataPatcherImpl;
2230
import de.symeda.sormas.backend.patch.customizablefield.mappers.CustomizableFieldValuePatchMapperRegistry;
31+
import de.symeda.sormas.backend.util.CollectorUtils;
2332

2433
/**
2534
* Wrapper arround {@link de.symeda.sormas.api.customizablefield.CustomizableFieldValueFacade} to be able to patch customizable field
@@ -28,25 +37,99 @@
2837
@ApplicationScoped
2938
public class CustomizableFieldDataPatcher {
3039

40+
private final static Logger logger = LoggerFactory.getLogger(CustomizableFieldDataPatcher.class);
41+
3142
@EJB
3243
private CustomizableFieldValueFacadeEjb.CustomizableFieldValueFacadeEjbLocal facade;
3344

3445
@Inject
3546
private CustomizableFieldValuePatchMapperRegistry registry;
3647

37-
public List<Tuple<SinglePatchResult, CustomizableFieldValueDto>> patch(Request request) {
48+
@Inject
49+
private CustomizableFieldHelper customizableFieldHelper;
50+
51+
public List<Tuple<SinglePatchResult, ValueMappingResult<CustomizableFieldValueDto>>> patch(Request request) {
3852
List<DataPatcherImpl.SingleFieldPatchResult> patchingTuples = request.getPatchingTuples();
3953

4054
if (CollectionUtils.isEmpty(patchingTuples)) {
4155
return List.of();
4256
}
4357

44-
CaseDataPatchRequest caseDataPatchRequest = request.getCaseDataPatchRequest();
58+
List<HelperClass> tempCollect = patchingTuples.stream()
59+
.map(
60+
singleFieldResult -> customizableFieldHelper.from(singleFieldResult.getField())
61+
.map(context -> new HelperClass().setPatchField(context))
62+
.orElseGet(
63+
() -> new HelperClass().setSingleFieldPatchResult(
64+
new DataPatcherImpl.SingleFieldPatchResult().setField(singleFieldResult.getField())
65+
.setFailureCause(DataPatchFailureCause.INVALID_CUSTOM_CONTEXT))))
66+
.collect(Collectors.toList());
67+
68+
Set<CustomizableFieldContext> contexts =
69+
tempCollect.stream().map(HelperClass::getPatchField).map(CustomizablePatchField::getContext).collect(Collectors.toSet());
70+
71+
Map<CustomizableFieldContext, Map<CustomizableFieldMetadataDto, CustomizableFieldValueDto>> customizableByContextDictionary =
72+
contexts.stream()
73+
.map(context -> Tuple.of(context, facade.getValuesForEntity(extractEntityId(request.getCaseDataDto(), context), context)))
74+
.collect(Collectors.toMap(Tuple::getFirst, Tuple::getSecond));
75+
76+
List<ValueMappingResult<CustomizableFieldValueDto>> results = tempCollect.stream().peek(helperClass -> {
77+
CustomizablePatchField patchField = helperClass.getPatchField();
78+
79+
Map<CustomizableFieldMetadataDto, CustomizableFieldValueDto> map = customizableByContextDictionary.get(patchField);
80+
81+
Optional<Map.Entry<CustomizableFieldMetadataDto, CustomizableFieldValueDto>> singleOpt = map.entrySet()
82+
.stream()
83+
.filter(entry -> entry.getKey().getName().equals(patchField.getLeafFieldName()))
84+
.collect(CollectorUtils.toOptionalSingle());
85+
86+
if (singleOpt.isEmpty()) {
87+
logger.error("For context. [{}] the leaf field does not exist: [{}]", patchField.getContext(), patchField.getLeafFieldName());
88+
helperClass.setFailureCause(DataPatchFailureCause.FIELD_DOES_NOT_EXIST);
89+
} else {
90+
helperClass.setCustomizableFieldValue(singleOpt.get().getValue());
91+
helperClass.setMetadata(singleOpt.get().getKey());
92+
}
93+
// TODO: handle errors:
94+
// - leafFieldName not found
95+
// - other error during mapping.
96+
})
97+
.map(
98+
helperClass -> new CustomizableFieldValuePatchRequest().setValue(helperClass.getSingleFieldPatchResult().getValue())
99+
.setTargetType(helperClass.getMetadata().getFieldType())
100+
.setCustomizableFieldValueDto(helperClass.getCustomizableFieldValue()))
101+
.map(a -> registry.map(a))
102+
.collect(Collectors.toList());
103+
104+
return results;
105+
106+
// contextsToLoad.stream().map(a -> facade.getValuesForEntity("", a))
107+
// .flatMap(a-> a.entrySet().stream()
108+
// .map(b ->Tuple.of(b.getKey(), b.getValue())))
109+
// .filter(a -> );
110+
//
111+
//
112+
//
113+
// return valuesForEntity.entrySet().stream()
114+
// .filter(a -> patchingTuples.stream().anyMatch(b -> b.))
115+
// .map(a -> new CustomizableFieldValuePatchRequest())
116+
// .map(request1 -> Tuple.of(registry.map(request1)))
117+
// .tempCollect(Collectors.toList());
118+
}
45119

46-
Map<CustomizableFieldMetadataDto, CustomizableFieldValueDto> valuesForEntity =
47-
facade.getValuesForEntity(caseDataPatchRequest.getCaseUuid(), CustomizableFieldContext.CASE);
120+
public String extractEntityId(CaseDataDto caseDataDto, CustomizableFieldContext context) {
121+
if (context == CustomizableFieldContext.CASE) {
122+
return caseDataDto.getUuid();
123+
} else if (context == CustomizableFieldContext.EPIDATA) {
124+
return caseDataDto.getEpiData().getUuid();
125+
} else {
126+
logger.error("Unsupported for now.");
127+
return null;
128+
}
129+
}
48130

49-
return List.of();
131+
public boolean match(PatchField patchField, CustomizableFieldMetadataDto metadata) {
132+
return false;
50133
}
51134

52135
public void save(List<CustomizableFieldValueDto> values) {
@@ -91,4 +174,59 @@ public Request setCaseDataDto(CaseDataDto caseDataDto) {
91174
return this;
92175
}
93176
}
177+
178+
public static final class HelperClass {
179+
180+
private CustomizablePatchField patchField;
181+
private DataPatcherImpl.SingleFieldPatchResult singleFieldPatchResult;
182+
private DataPatchFailureCause failureCause;
183+
184+
private CustomizableFieldMetadataDto metadata;
185+
private CustomizableFieldValueDto customizableFieldValue;
186+
187+
public CustomizablePatchField getPatchField() {
188+
return patchField;
189+
}
190+
191+
public HelperClass setPatchField(CustomizablePatchField patchField) {
192+
this.patchField = patchField;
193+
return this;
194+
}
195+
196+
public DataPatcherImpl.SingleFieldPatchResult getSingleFieldPatchResult() {
197+
return singleFieldPatchResult;
198+
}
199+
200+
public HelperClass setSingleFieldPatchResult(DataPatcherImpl.SingleFieldPatchResult singleFieldPatchResult) {
201+
this.singleFieldPatchResult = singleFieldPatchResult;
202+
return this;
203+
}
204+
205+
public CustomizableFieldValueDto getCustomizableFieldValue() {
206+
return customizableFieldValue;
207+
}
208+
209+
public HelperClass setCustomizableFieldValue(CustomizableFieldValueDto customizableFieldValue) {
210+
this.customizableFieldValue = customizableFieldValue;
211+
return this;
212+
}
213+
214+
public DataPatchFailureCause getFailureCause() {
215+
return failureCause;
216+
}
217+
218+
public HelperClass setFailureCause(DataPatchFailureCause failureCause) {
219+
this.failureCause = failureCause;
220+
return this;
221+
}
222+
223+
public CustomizableFieldMetadataDto getMetadata() {
224+
return metadata;
225+
}
226+
227+
public HelperClass setMetadata(CustomizableFieldMetadataDto metadata) {
228+
this.metadata = metadata;
229+
return this;
230+
}
231+
}
94232
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package de.symeda.sormas.backend.patch.customizablefield;
2+
3+
import static de.symeda.sormas.backend.patch.customizablefield.CustomizableFieldContextPatchMapping.I18N_DICTIONARY;
4+
5+
import java.util.Optional;
6+
7+
import javax.enterprise.context.ApplicationScoped;
8+
9+
import de.symeda.sormas.api.externalmessage.survey.PatchField;
10+
import de.symeda.sormas.backend.patch.PatchFieldHelper;
11+
12+
@ApplicationScoped
13+
public class CustomizableFieldHelper {
14+
15+
public Optional<CustomizablePatchField> from(PatchField patchField) {
16+
String field = patchField.getField();
17+
18+
if (!field.contains(PatchFieldHelper.CUSTOM_PREFIX)) {
19+
return Optional.empty();
20+
}
21+
22+
String[] splittedField = field.split("\\.");
23+
24+
if (splittedField.length != 3) {
25+
return Optional.empty();
26+
}
27+
28+
return Optional.ofNullable(I18N_DICTIONARY.get(splittedField[1]))
29+
.map(context -> new CustomizablePatchField().setContext(context).setLeafFieldName(splittedField[2]));
30+
}
31+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package de.symeda.sormas.backend.patch.customizablefield;
2+
3+
import de.symeda.sormas.api.customizablefield.CustomizableFieldContext;
4+
5+
public class CustomizablePatchField {
6+
7+
private CustomizableFieldContext context;
8+
private String leafFieldName;
9+
10+
public CustomizableFieldContext getContext() {
11+
return context;
12+
}
13+
14+
public CustomizablePatchField setContext(CustomizableFieldContext context) {
15+
this.context = context;
16+
return this;
17+
}
18+
19+
public String getLeafFieldName() {
20+
return leafFieldName;
21+
}
22+
23+
public CustomizablePatchField setLeafFieldName(String leafFieldName) {
24+
this.leafFieldName = leafFieldName;
25+
return this;
26+
}
27+
}

sormas-backend/src/main/java/de/symeda/sormas/backend/util/CollectorUtils.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.HashMap;
44
import java.util.LinkedHashMap;
55
import java.util.Map;
6+
import java.util.Optional;
67
import java.util.function.Function;
78
import java.util.stream.Collector;
89

@@ -37,4 +38,43 @@ private CollectorUtils() {
3738
});
3839
}
3940

41+
/**
42+
* When you want a single response or none.
43+
*
44+
* @return Optional: empty or with value if present
45+
* @throws IllegalStateException
46+
* in case multiple matching elements are found.
47+
* @param <T>
48+
* stream type
49+
*/
50+
public static <T> Collector<T, ?, Optional<T>> toOptionalSingle() {
51+
class Box {
52+
53+
T value;
54+
boolean present;
55+
56+
void add(T element) {
57+
if (present) {
58+
throw new IllegalStateException(String.format("Expected at most one element but found multiple: [%s] [%s]", value, element));
59+
}
60+
value = element;
61+
present = true;
62+
}
63+
64+
Box merge(Box other) {
65+
if (this.present && other.present) {
66+
throw new IllegalStateException(
67+
String.format("Expected at most one element but found multiple: [%s] [%s]", this.value, other.value));
68+
}
69+
return this.present ? this : other;
70+
}
71+
72+
Optional<T> finish() {
73+
return present ? Optional.of(value) : Optional.empty();
74+
}
75+
}
76+
77+
return Collector.of(Box::new, Box::add, Box::merge, Box::finish);
78+
}
79+
4080
}

0 commit comments

Comments
 (0)