Skip to content

Commit ebbacae

Browse files
committed
Merge remote-tracking branch 'origin/feat/13832-ngsurvey-integration-external-api' into feat/13832-ngsurvey-integration-external-api
# Conflicts: # sormas-backend/src/main/java/de/symeda/sormas/backend/patch/mapping/impl/fieldmapper/PersonContactDetailsFieldMapper.java
2 parents b6c8ad7 + 47dfc98 commit ebbacae

7 files changed

Lines changed: 122 additions & 137 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/survey/ExternalMessageSurveyResponseRequest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* Mandatory fields that require
2020
* Will be present for {@link ExternalMessageType#SURVEY_RESPONSE}.
2121
*/
22-
public class ExternalMessageSurveyResponseRequest implements Serializable {
22+
public class ExternalMessageSurveyResponseRequest implements Serializable, Comparable<ExternalMessageSurveyResponseRequest> {
2323

2424
private static final long serialVersionUID = 1L;
2525

@@ -243,4 +243,19 @@ public String toString() {
243243
+ origin + '\'' + ", inputLanguages=" + inputLanguages + ", allowFallbackValues=" + allowFallbackValues + ", skipIfAlreadyProcessed="
244244
+ skipIfAlreadyProcessed + '}';
245245
}
246+
247+
@Override
248+
public int compareTo(ExternalMessageSurveyResponseRequest o) {
249+
250+
if (this.responseReceivedDate == null && o.responseReceivedDate == null) {
251+
return 0;
252+
}
253+
if (this.responseReceivedDate == null) {
254+
return -1;
255+
}
256+
if (o.responseReceivedDate == null) {
257+
return 1;
258+
}
259+
return this.responseReceivedDate.compareTo(o.responseReceivedDate);
260+
}
246261
}

sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public List<SurveyResponseProcessingResult> processSurveyResponses(List<External
104104
CaseDataPatchRequest dataPatchRequest = from(request, surveyTokenDto);
105105

106106
DataPatchResponse response = dataPatcher.patch(dataPatchRequest);
107-
logger.debug("Patch: request: [{}], response: [{}]", request, response);
107+
logger.info("Patch: request: [{}], response: [{}]", request, response);
108108

109109
latestResponseWrapper
110110
.setResult(new ExternalMessageSurveyResponseResult().setPatchResponse(response).setCaseUuid(dataPatchRequest.getCaseUuid()));

sormas-backend/src/main/java/de/symeda/sormas/backend/patch/partial_retrieval/ContactDetailsFieldValueRetriever.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import javax.enterprise.context.ApplicationScoped;
1111

12+
import org.apache.commons.lang3.StringUtils;
13+
1214
import de.symeda.sormas.api.EntityDto;
1315
import de.symeda.sormas.api.i18n.I18nProperties;
1416
import de.symeda.sormas.api.patch.partial_retrieval.FieldInfo;
@@ -30,12 +32,12 @@ public FieldInfo getFieldInfo(String fieldName, EntityDto entityDto) {
3032
.stream()
3133
.filter(detail -> targetType.equals(detail.getPersonContactDetailType()))
3234
.map(PersonContactDetailDto::getContactInformation)
35+
.filter(StringUtils::isNotBlank)
36+
.sorted()
3337
.collect(Collectors.joining("; "));
3438

3539
String captionKey = isPhone ? PersonContactDetailDto.PHONE_NUMBER_TYPE : PersonContactDetailDto.CONTACT_INFORMATION;
36-
String translatedFieldName = I18nProperties.getCaption(
37-
PersonContactDetailDto.I18N_PREFIX + PATH_SEPARATOR + captionKey,
38-
captionKey);
40+
String translatedFieldName = I18nProperties.getCaption(PersonContactDetailDto.I18N_PREFIX + PATH_SEPARATOR + captionKey, captionKey);
3941

4042
return new FieldInfo().setFieldType(List.class).setFieldValue(contactValues).setTranslatedFieldName(translatedFieldName);
4143
}

sormas-backend/src/main/java/de/symeda/sormas/backend/patch/partial_retrieval/PartialRetrieverImpl.java

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,12 @@
2020
import de.symeda.sormas.api.EntityDto;
2121
import de.symeda.sormas.api.caze.CaseDataDto;
2222
import de.symeda.sormas.api.i18n.I18nProperties;
23-
import de.symeda.sormas.api.patch.partial_retrieval.DisplayableFieldInfo;
24-
import de.symeda.sormas.api.patch.partial_retrieval.DisplayablePartialRetrievalResponse;
25-
import de.symeda.sormas.api.patch.partial_retrieval.FieldInfo;
26-
import de.symeda.sormas.api.patch.partial_retrieval.PartialRetrievalFailureCause;
27-
import de.symeda.sormas.api.patch.partial_retrieval.PartialRetrievalRequest;
28-
import de.symeda.sormas.api.patch.partial_retrieval.PartialRetrievalResponse;
29-
import de.symeda.sormas.api.patch.partial_retrieval.PartialRetriever;
23+
import de.symeda.sormas.api.patch.partial_retrieval.*;
3024
import de.symeda.sormas.api.utils.Tuple;
3125
import de.symeda.sormas.api.utils.fieldvisibility.FieldVisibilityCheckers;
3226
import de.symeda.sormas.backend.common.ConfigFacadeEjb;
3327
import de.symeda.sormas.backend.feature.FeatureConfigurationFacadeEjb;
34-
import de.symeda.sormas.backend.patch.BusinessDtoFacade;
35-
import de.symeda.sormas.backend.patch.PatchFieldHelper;
36-
import de.symeda.sormas.backend.patch.PathFailureCause;
37-
import de.symeda.sormas.backend.patch.PropertyAccessFailure;
38-
import de.symeda.sormas.backend.patch.PropertyAccessor;
28+
import de.symeda.sormas.backend.patch.*;
3929
import de.symeda.sormas.backend.patch.alias.PathAliasHelper;
4030

4131
@ApplicationScoped
@@ -74,73 +64,83 @@ public PartialRetrievalResponse retrievePartial(PartialRetrievalRequest request)
7464
List<Tuple<String, Tuple<FieldInfo, PartialRetrievalFailureCause>>> results =
7565
patchFieldHelper.extractFieldTuples(request.getFieldsToRetrieve(), businessDtoFacade.fetchablePrefixes()).stream().map(tuple -> {
7666

77-
String originalFieldName = tuple.getFirst();
78-
PathFailureCause pathFailureCause = tuple.getSecond();
67+
try {
68+
return buildTupleImpl(tuple, caseData, beanCache);
69+
} catch (RuntimeException e) {
70+
logger.warn("Failure during retrieval for [{}]", tuple, e);
71+
return Tuple.of(tuple.getFirst(), new Tuple<>((FieldInfo) null, PartialRetrievalFailureCause.TECHNICAL));
72+
}
7973

80-
Tuple<String, PathFailureCause> unAliasedTuple = patchFieldHelper.resolveAlias(originalFieldName);
74+
}).collect(Collectors.toList());
8175

82-
PartialRetrievalFailureCause failureCause = Optional.ofNullable(pathFailureCause)
83-
.map(PathFailureCause::getRelatedRetrieveFailureCause)
84-
.or(() -> Optional.ofNullable(unAliasedTuple.getSecond()).map(PathFailureCause::getRelatedRetrieveFailureCause))
85-
.orElse(null);
76+
Map<String, FieldInfo> successes = results.stream()
77+
.filter(tuple -> tuple.getSecond().getSecond() == null)
78+
.collect(Collectors.toMap(Tuple::getFirst, tuple -> tuple.getSecond().getFirst()));
8679

87-
if (failureCause != null) {
88-
return Tuple.of(originalFieldName, new Tuple<>((FieldInfo) null, failureCause));
89-
}
80+
Map<String, PartialRetrievalFailureCause> failures = results.stream()
81+
.filter(tuple -> tuple.getSecond().getSecond() != null)
82+
.collect(Collectors.toMap(Tuple::getFirst, tuple -> tuple.getSecond().getSecond()));
9083

91-
String pathWithoutAlias = unAliasedTuple.getFirst();
92-
String physicalPathName = pathWithoutAlias.substring(pathWithoutAlias.indexOf('.') + 1);
84+
return new PartialRetrievalResponse().setFailuresDictionary(failures).setFieldInfoDictionary(successes);
85+
}
9386

94-
String aliasPath = pathAliasHelper.toAliasPath(pathWithoutAlias);
95-
Optional<EntityDto> adequateBeanOpt = getAdequateBean(pathWithoutAlias, caseData, beanCache);
87+
private Tuple<String, Tuple<FieldInfo, PartialRetrievalFailureCause>> buildTupleImpl(
88+
Tuple<String, PathFailureCause> tuple,
89+
CaseDataDto caseData,
90+
Map<String, Optional<EntityDto>> beanCache) {
91+
String originalFieldName = tuple.getFirst();
92+
PathFailureCause pathFailureCause = tuple.getSecond();
9693

97-
if (adequateBeanOpt.isEmpty()) {
98-
return Tuple.of(originalFieldName, new Tuple<>((FieldInfo) null, PartialRetrievalFailureCause.ENTITY_COULD_NOT_BE_FOUND));
99-
}
94+
Tuple<String, PathFailureCause> unAliasedTuple = patchFieldHelper.resolveAlias(originalFieldName);
10095

101-
EntityDto adequateBean = adequateBeanOpt.orElseThrow();
102-
Optional<FieldInfo> specificFieldInfo = specificFieldValueRetrieverRegistry.getFieldInfo(aliasPath, adequateBean);
96+
PartialRetrievalFailureCause failureCause = Optional.ofNullable(pathFailureCause)
97+
.map(PathFailureCause::getRelatedRetrieveFailureCause)
98+
.or(() -> Optional.ofNullable(unAliasedTuple.getSecond()).map(PathFailureCause::getRelatedRetrieveFailureCause))
99+
.orElse(null);
103100

104-
if (specificFieldInfo.isPresent()) {
105-
return Tuple.of(originalFieldName, new Tuple<>(specificFieldInfo.get(), (PartialRetrievalFailureCause) null));
106-
}
101+
if (failureCause != null) {
102+
return Tuple.of(originalFieldName, new Tuple<>((FieldInfo) null, failureCause));
103+
}
107104

108-
Tuple<Tuple<Class<?>, Object>, PropertyAccessFailure> propertyType = PropertyAccessor
109-
.getPropertyTypeAndValue(adequateBean, physicalPathName, getFieldVisibilityCheckers(caseData.getDisease()));
105+
String pathWithoutAlias = unAliasedTuple.getFirst();
106+
String physicalPathName = pathWithoutAlias.substring(pathWithoutAlias.indexOf('.') + 1);
110107

111-
PropertyAccessFailure propertyAccessFailure = propertyType.getSecond();
112-
if (propertyAccessFailure != null) {
113-
return Tuple.of(originalFieldName, new Tuple<>((FieldInfo) null, propertyAccessFailure.getRelatedRetrieveFailureCause()));
114-
}
108+
String aliasPath = pathAliasHelper.toAliasPath(pathWithoutAlias);
109+
Optional<EntityDto> adequateBeanOpt = getAdequateBean(pathWithoutAlias, caseData, beanCache);
115110

116-
Tuple<Class<?>, Object> fieldInfo = propertyType.getFirst();
111+
if (adequateBeanOpt.isEmpty()) {
112+
return Tuple.of(originalFieldName, new Tuple<>((FieldInfo) null, PartialRetrievalFailureCause.ENTITY_COULD_NOT_BE_FOUND));
113+
}
117114

118-
// Some fields are translated only by there "physical-path" from root level
119-
// example: Person.firstName has translation key "firstName"
120-
// example: CaseData.disease has translation key "firstName"
121-
String translatedFieldName = Optional.ofNullable(I18nProperties.getCaption(aliasPath, null))
122-
.or(() -> Optional.ofNullable(I18nProperties.getCaption(physicalPathName, null)))
123-
.orElseGet(() -> I18nProperties.getDescription(aliasPath, aliasPath));
115+
EntityDto adequateBean = adequateBeanOpt.orElseThrow();
116+
Optional<FieldInfo> specificFieldInfo = specificFieldValueRetrieverRegistry.getFieldInfo(aliasPath, adequateBean);
124117

125-
return Tuple.of(
126-
originalFieldName,
127-
new Tuple<>(
128-
new FieldInfo().setFieldType(fieldInfo.getFirst())
129-
.setFieldValue(fieldInfo.getSecond())
130-
.setTranslatedFieldName(translatedFieldName),
131-
(PartialRetrievalFailureCause) null));
118+
if (specificFieldInfo.isPresent()) {
119+
return Tuple.of(originalFieldName, new Tuple<>(specificFieldInfo.get(), (PartialRetrievalFailureCause) null));
120+
}
132121

133-
}).collect(Collectors.toList());
122+
Tuple<Tuple<Class<?>, Object>, PropertyAccessFailure> propertyType =
123+
PropertyAccessor.getPropertyTypeAndValue(adequateBean, physicalPathName, getFieldVisibilityCheckers(caseData.getDisease()));
134124

135-
Map<String, FieldInfo> successes = results.stream()
136-
.filter(tuple -> tuple.getSecond().getSecond() == null)
137-
.collect(Collectors.toMap(Tuple::getFirst, tuple -> tuple.getSecond().getFirst()));
125+
PropertyAccessFailure propertyAccessFailure = propertyType.getSecond();
126+
if (propertyAccessFailure != null) {
127+
return Tuple.of(originalFieldName, new Tuple<>((FieldInfo) null, propertyAccessFailure.getRelatedRetrieveFailureCause()));
128+
}
138129

139-
Map<String, PartialRetrievalFailureCause> failures = results.stream()
140-
.filter(tuple -> tuple.getSecond().getSecond() != null)
141-
.collect(Collectors.toMap(Tuple::getFirst, tuple -> tuple.getSecond().getSecond()));
130+
Tuple<Class<?>, Object> fieldInfo = propertyType.getFirst();
142131

143-
return new PartialRetrievalResponse().setFailuresDictionary(failures).setFieldInfoDictionary(successes);
132+
// Some fields are translated only by there "physical-path" from root level
133+
// example: Person.firstName has translation key "firstName"
134+
// example: CaseData.disease has translation key "firstName"
135+
String translatedFieldName = Optional.ofNullable(I18nProperties.getCaption(aliasPath, null))
136+
.or(() -> Optional.ofNullable(I18nProperties.getCaption(physicalPathName, null)))
137+
.orElseGet(() -> I18nProperties.getDescription(aliasPath, aliasPath));
138+
139+
return Tuple.of(
140+
originalFieldName,
141+
new Tuple<>(
142+
new FieldInfo().setFieldType(fieldInfo.getFirst()).setFieldValue(fieldInfo.getSecond()).setTranslatedFieldName(translatedFieldName),
143+
(PartialRetrievalFailureCause) null));
144144
}
145145

146146
@Override
@@ -161,13 +161,13 @@ public DisplayablePartialRetrievalResponse retrievePartialForDisplay(PartialRetr
161161
}
162162

163163
private Optional<EntityDto> getAdequateBean(
164-
@NotNull String aliasPath,
164+
@NotNull String path,
165165
@NotNull CaseDataDto caseData,
166166
@NotNull Map<String, Optional<EntityDto>> beanCache) {
167167

168-
int i = aliasPath.indexOf(".");
168+
int i = path.indexOf(".");
169169

170-
String prefix = StringUtils.substring(aliasPath, 0, i);
170+
String prefix = StringUtils.substring(path, 0, i);
171171

172172
if (CaseDataDto.I18N_PREFIX.equals(prefix)) {
173173
return Optional.of(caseData);
@@ -182,7 +182,7 @@ private Optional<EntityDto> getAdequateBean(
182182
}
183183

184184
if (entitiesSize != 1) {
185-
logger.warn("Only first element is supported for now: [{}], was: [{}]", aliasPath, entitiesSize);
185+
logger.warn("Only first element is supported for now: [{}], was: [{}]", path, entitiesSize);
186186
}
187187

188188
return Optional.ofNullable(entityDtos).map(actualEntities -> actualEntities.get(0));

sormas-backend/src/main/java/de/symeda/sormas/backend/survey/SurveyFacadeEjb.java

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@
1919

2020
import java.io.ByteArrayInputStream;
2121
import java.io.IOException;
22-
import java.util.ArrayList;
23-
import java.util.Collections;
24-
import java.util.Date;
25-
import java.util.List;
26-
import java.util.Properties;
22+
import java.util.*;
2723
import java.util.concurrent.atomic.AtomicBoolean;
2824
import java.util.stream.Collectors;
2925
import java.util.stream.Stream;
@@ -35,54 +31,31 @@
3531
import javax.persistence.EntityManager;
3632
import javax.persistence.PersistenceContext;
3733
import javax.persistence.Tuple;
38-
import javax.persistence.criteria.CriteriaBuilder;
39-
import javax.persistence.criteria.CriteriaQuery;
40-
import javax.persistence.criteria.Order;
41-
import javax.persistence.criteria.Path;
42-
import javax.persistence.criteria.Predicate;
43-
import javax.persistence.criteria.Root;
44-
import javax.persistence.criteria.Selection;
34+
import javax.persistence.criteria.*;
4535
import javax.validation.Valid;
4636
import javax.validation.constraints.NotNull;
4737

4838
import de.symeda.sormas.api.Disease;
4939
import de.symeda.sormas.api.ReferenceDto;
5040
import de.symeda.sormas.api.caze.CaseDataDto;
51-
import de.symeda.sormas.api.docgeneneration.DocumentTemplateCriteria;
52-
import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto;
53-
import de.symeda.sormas.api.docgeneneration.DocumentTemplateEntities;
54-
import de.symeda.sormas.api.docgeneneration.DocumentTemplateException;
55-
import de.symeda.sormas.api.docgeneneration.DocumentVariables;
56-
import de.symeda.sormas.api.docgeneneration.DocumentWorkflow;
57-
import de.symeda.sormas.api.docgeneneration.RootEntityType;
41+
import de.symeda.sormas.api.docgeneneration.*;
5842
import de.symeda.sormas.api.document.DocumentDto;
5943
import de.symeda.sormas.api.externalemail.AttachmentException;
6044
import de.symeda.sormas.api.externalemail.ExternalEmailException;
6145
import de.symeda.sormas.api.externalemail.ExternalEmailOptionsDto;
6246
import de.symeda.sormas.api.i18n.I18nProperties;
6347
import de.symeda.sormas.api.i18n.Strings;
6448
import de.symeda.sormas.api.i18n.Validations;
65-
import de.symeda.sormas.api.survey.SurveyCriteria;
66-
import de.symeda.sormas.api.survey.SurveyDocumentOptionsDto;
67-
import de.symeda.sormas.api.survey.SurveyDto;
68-
import de.symeda.sormas.api.survey.SurveyFacade;
69-
import de.symeda.sormas.api.survey.SurveyIndexDto;
70-
import de.symeda.sormas.api.survey.SurveyReferenceDto;
49+
import de.symeda.sormas.api.survey.*;
7150
import de.symeda.sormas.api.user.UserRight;
7251
import de.symeda.sormas.api.utils.SortProperty;
7352
import de.symeda.sormas.api.utils.ValidationException;
7453
import de.symeda.sormas.api.utils.ValidationRuntimeException;
7554
import de.symeda.sormas.backend.FacadeHelper;
7655
import de.symeda.sormas.backend.caze.CaseService;
7756
import de.symeda.sormas.backend.common.CriteriaBuilderHelper;
78-
import de.symeda.sormas.backend.docgeneration.DocGenerationHelper;
79-
import de.symeda.sormas.backend.docgeneration.DocumentTemplate;
80-
import de.symeda.sormas.backend.docgeneration.DocumentTemplateEntitiesBuilder;
81-
import de.symeda.sormas.backend.docgeneration.DocumentTemplateFacadeEjb;
57+
import de.symeda.sormas.backend.docgeneration.*;
8258
import de.symeda.sormas.backend.docgeneration.DocumentTemplateFacadeEjb.DocumentTemplateFacadeEjbLocal;
83-
import de.symeda.sormas.backend.docgeneration.DocumentTemplateService;
84-
import de.symeda.sormas.backend.docgeneration.RootEntities;
85-
import de.symeda.sormas.backend.docgeneration.TemplateEngine;
8659
import de.symeda.sormas.backend.document.DocumentService;
8760
import de.symeda.sormas.backend.externalemail.ExternalEmailFacadeEjb.ExternalEmailFacadeEjbLocal;
8861
import de.symeda.sormas.backend.user.UserService;
@@ -241,7 +214,7 @@ public List<SurveyDto> getByExternalIds(List<String> externalIds) {
241214
CriteriaBuilder cb = em.getCriteriaBuilder();
242215
CriteriaQuery<Survey> cq = cb.createQuery(Survey.class);
243216
Root<Survey> from = cq.from(Survey.class);
244-
cq.where(cb.equal(from.get(Survey.EXTERNAL_ID), externalIds));
217+
cq.where(from.get(Survey.EXTERNAL_ID).in(externalIds));
245218
cq.orderBy(cb.desc(from.get(Survey.NAME)));
246219

247220
return getAsStream(cq).map(this::toDto).collect(Collectors.toList());

0 commit comments

Comments
 (0)