From a51950d296c2e8705f0bce7ffed9224a16e97466 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Wed, 21 Jan 2026 12:49:04 -0600 Subject: [PATCH 01/21] Skip cache in CaseSearchHelper --- .../java/org/commcare/formplayer/services/CaseSearchHelper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index 43b0c3401..e72cb9efc 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -82,6 +82,8 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException, IOException { + skipCache = true; + Multimap requestData = source.getRequestData(); String url = source.getSourceUri(); From fc87d6c5e1b693224c852b6128245cceb836e5b5 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Thu, 22 Jan 2026 13:02:25 -0600 Subject: [PATCH 02/21] Add logging for case type missmatch --- .../formplayer/services/CaseSearchHelper.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index e72cb9efc..a13e26d1e 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -3,6 +3,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import io.sentry.SentryLevel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commcare.cases.instance.CaseInstanceTreeElement; @@ -16,6 +17,7 @@ import org.commcare.formplayer.session.MenuSession; import org.commcare.formplayer.sqlitedb.CaseSearchDB; import org.commcare.formplayer.sqlitedb.SQLiteDB; +import org.commcare.formplayer.util.FormplayerSentry; import org.commcare.formplayer.util.SerializationUtil; import org.commcare.formplayer.web.client.WebClient; import org.commcare.util.screen.ScreenUtils; @@ -82,7 +84,10 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException, IOException { + + skipCache = true; +// System.out.println("getExternalRoot"); Multimap requestData = source.getRequestData(); String url = source.getSourceUri(); @@ -100,6 +105,7 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan IStorageUtilityIndexed caseSearchStorage = caseSearchSandbox.getCaseStorage(); FormplayerCaseIndexTable caseSearchIndexTable = getCaseIndexTable(caseSearchSandbox, caseSearchTableName); if (skipCache || !caseSearchStorage.isStorageExists()) { + Collection requestCaseTypes = requestData.get("case_type"); String responseString = webClient.postFormData(url, requestData); if (responseString != null) { byte[] responseBytes = responseString.getBytes(StandardCharsets.UTF_8); @@ -112,6 +118,17 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan if (root != null) { cache.put(cacheKey, root); } + + for (int i = 0; i < root.getNumChildren(); i++) { + TreeElement child = root.getChildAt(i); + String attributeValue = child.getAttributeValue(null, "case_type"); + + if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { + Exception e = new Exception("Returned case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + + "Url: " + url + " request data: " + requestData.toString()); + FormplayerSentry.captureException(e, SentryLevel.WARNING); + } + } return root; } } From 76ca03a9752c967bea773a911265eb9e228a5ec7 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Thu, 22 Jan 2026 14:05:10 -0600 Subject: [PATCH 03/21] log getExternalRoot --- .../java/org/commcare/formplayer/services/CaseSearchHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index a13e26d1e..1b353edca 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -87,7 +87,7 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan skipCache = true; -// System.out.println("getExternalRoot"); + FormplayerSentry.captureException(new Exception("getExternalRoot"), SentryLevel.WARNING); Multimap requestData = source.getRequestData(); String url = source.getSourceUri(); From 2d9d5becb74b7feb3703d222da9272787a9525b8 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Thu, 22 Jan 2026 15:58:03 -0600 Subject: [PATCH 04/21] More logging --- .../formplayer/services/CaseSearchHelper.java | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index 1b353edca..e302aca68 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -27,6 +27,7 @@ import org.javarosa.core.model.instance.InstanceBase; import org.javarosa.core.model.instance.TreeElement; import org.javarosa.core.model.instance.utils.TreeUtilities; +import org.javarosa.core.services.storage.IStorageIterator; import org.javarosa.core.services.storage.IStorageUtilityIndexed; import org.javarosa.core.util.MD5; import org.javarosa.core.util.externalizable.ExtUtil; @@ -109,26 +110,20 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan String responseString = webClient.postFormData(url, requestData); if (responseString != null) { byte[] responseBytes = responseString.getBytes(StandardCharsets.UTF_8); + validateCaseTypesInResponse(responseBytes, instanceId, requestCaseTypes, url, requestData); ByteArrayInputStream responeStream = new ByteArrayInputStream(responseBytes); if (shouldParseIntoCaseSearchStorage(source.useCaseTemplate())) { parseIntoCaseSearchStorage(caseSearchDb, caseSearchSandbox, caseSearchStorage, responeStream, caseSearchIndexTable); + // Validate case types after parsing into storage + validateCaseTypesInStorage(caseSearchStorage, requestCaseTypes, url, requestData); } else { TreeElement root = TreeUtilities.xmlStreamToTreeElement(responeStream, instanceId); if (root != null) { cache.put(cacheKey, root); } + // Validate case types in TreeElement - for (int i = 0; i < root.getNumChildren(); i++) { - TreeElement child = root.getChildAt(i); - String attributeValue = child.getAttributeValue(null, "case_type"); - - if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { - Exception e = new Exception("Returned case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + - "Url: " + url + " request data: " + requestData.toString()); - FormplayerSentry.captureException(e, SentryLevel.WARNING); - } - } return root; } } @@ -187,6 +182,48 @@ private boolean shouldParseIntoCaseSearchStorage(boolean useCaseTemplate) { return useCaseTemplate && storageFactory.getPropertyManager().isIndexCaseSearchResults(); } + private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId, Collection requestCaseTypes, + String url, Multimap requestData) throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException,IOException { + + ByteArrayInputStream responseStream = new ByteArrayInputStream(responseBytes); + TreeElement root = TreeUtilities.xmlStreamToTreeElement(responseStream, instanceId); + + if (root == null) { + return; + } + for (int i = 0; i < root.getNumChildren(); i++) { + TreeElement child = root.getChildAt(i); + String attributeValue = child.getAttributeValue(null, "case_type"); + + if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { + Exception e = new Exception("Response case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + + "Url: " + url + " request data: " + requestData.toString()); + FormplayerSentry.captureException(e, SentryLevel.WARNING); + } + } + } + + private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchStorage, + Collection requestCaseTypes, String url, Multimap requestData) { + if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { + return; + } + try { + org.javarosa.core.services.storage.IStorageIterator iterator = caseSearchStorage.iterate(); + while (iterator.hasMore()) { + Case caseObj = iterator.nextRecord(); + String caseType = caseObj.getTypeId(); + if (caseType != null && !requestCaseTypes.contains(caseType)) { + Exception e = new Exception("Storage case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + caseType + "\n" + + "Url: " + url + " request data: " + requestData.toString()); + FormplayerSentry.captureException(e, SentryLevel.WARNING); + } + } + } catch (Exception e) { + log.error("Error validating case types from storage", e); + } + } + private TreeElement getCachedRoot(Cache cache, String cacheKey, String url, boolean skipCache) { if (skipCache) { log.info("Skipping cache check for case search results"); From c97d1bbc67b43c69f37d6ab0fdb49ed9d251a86c Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Fri, 23 Jan 2026 05:43:36 -0600 Subject: [PATCH 05/21] More logging --- .../formplayer/services/CaseSearchHelper.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index e302aca68..7677ad445 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -88,7 +88,10 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan skipCache = true; - FormplayerSentry.captureException(new Exception("getExternalRoot"), SentryLevel.WARNING); + FormplayerSentry.captureException(new Exception("getExternalRoot skipCache: " + skipCache), SentryLevel.WARNING); + log.info("Info getExternalRoot skipCache: " + skipCache); + log.warn("Warn getExternalRoot skipCache: " + skipCache); + log.error("Error getExternalRoot skipCache: " + skipCache); Multimap requestData = source.getRequestData(); String url = source.getSourceUri(); @@ -107,6 +110,7 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan FormplayerCaseIndexTable caseSearchIndexTable = getCaseIndexTable(caseSearchSandbox, caseSearchTableName); if (skipCache || !caseSearchStorage.isStorageExists()) { Collection requestCaseTypes = requestData.get("case_type"); + log.info(String.format("Case search validation - requestCaseTypes: %s, requestData: %s", requestCaseTypes, requestData)); String responseString = webClient.postFormData(url, requestData); if (responseString != null) { byte[] responseBytes = responseString.getBytes(StandardCharsets.UTF_8); @@ -185,40 +189,62 @@ private boolean shouldParseIntoCaseSearchStorage(boolean useCaseTemplate) { private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId, Collection requestCaseTypes, String url, Multimap requestData) throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException,IOException { + log.info(String.format("validateCaseTypesInResponse called - requestCaseTypes: %s, responseLength: %d", requestCaseTypes, responseBytes.length)); + + if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { + log.warn("No case_type in request data - skipping validation"); + return; + } + ByteArrayInputStream responseStream = new ByteArrayInputStream(responseBytes); TreeElement root = TreeUtilities.xmlStreamToTreeElement(responseStream, instanceId); if (root == null) { + log.warn("Root element is null - cannot validate"); return; } + + log.info(String.format("Root element has %d children", root.getNumChildren())); + for (int i = 0; i < root.getNumChildren(); i++) { TreeElement child = root.getChildAt(i); String attributeValue = child.getAttributeValue(null, "case_type"); + log.info(String.format("Child %d: name=%s, case_type=%s", i, child.getName(), attributeValue)); if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { Exception e = new Exception("Response case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + "Url: " + url + " request data: " + requestData.toString()); FormplayerSentry.captureException(e, SentryLevel.WARNING); + log.error("CASE TYPE MISMATCH DETECTED!", e); } } } private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchStorage, Collection requestCaseTypes, String url, Multimap requestData) { + log.info(String.format("validateCaseTypesInStorage called - requestCaseTypes: %s", requestCaseTypes)); + if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { + log.warn("No case_type in request data - skipping storage validation"); return; } try { - org.javarosa.core.services.storage.IStorageIterator iterator = caseSearchStorage.iterate(); + IStorageIterator iterator = caseSearchStorage.iterate(); + int caseCount = 0; while (iterator.hasMore()) { Case caseObj = iterator.nextRecord(); + caseCount++; String caseType = caseObj.getTypeId(); + log.info(String.format("Storage case %d: id=%s, type=%s", caseCount, caseObj.getCaseId(), caseType)); + if (caseType != null && !requestCaseTypes.contains(caseType)) { Exception e = new Exception("Storage case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + caseType + "\n" + "Url: " + url + " request data: " + requestData.toString()); FormplayerSentry.captureException(e, SentryLevel.WARNING); + log.error("STORAGE CASE TYPE MISMATCH DETECTED!", e); } } + log.info(String.format("Validated %d cases from storage", caseCount)); } catch (Exception e) { log.error("Error validating case types from storage", e); } From 9378f175d6f1267a9066c891d3c9a43c0a0e2c52 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Fri, 23 Jan 2026 06:01:30 -0600 Subject: [PATCH 06/21] Only error logs make it to sentry --- .../formplayer/services/CaseSearchHelper.java | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index 7677ad445..dc55d5139 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -88,9 +88,6 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan skipCache = true; - FormplayerSentry.captureException(new Exception("getExternalRoot skipCache: " + skipCache), SentryLevel.WARNING); - log.info("Info getExternalRoot skipCache: " + skipCache); - log.warn("Warn getExternalRoot skipCache: " + skipCache); log.error("Error getExternalRoot skipCache: " + skipCache); Multimap requestData = source.getRequestData(); @@ -110,7 +107,7 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan FormplayerCaseIndexTable caseSearchIndexTable = getCaseIndexTable(caseSearchSandbox, caseSearchTableName); if (skipCache || !caseSearchStorage.isStorageExists()) { Collection requestCaseTypes = requestData.get("case_type"); - log.info(String.format("Case search validation - requestCaseTypes: %s, requestData: %s", requestCaseTypes, requestData)); + log.error(String.format("Case search validation - requestCaseTypes: %s, requestData: %s", requestCaseTypes, requestData)); String responseString = webClient.postFormData(url, requestData); if (responseString != null) { byte[] responseBytes = responseString.getBytes(StandardCharsets.UTF_8); @@ -189,10 +186,10 @@ private boolean shouldParseIntoCaseSearchStorage(boolean useCaseTemplate) { private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId, Collection requestCaseTypes, String url, Multimap requestData) throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException,IOException { - log.info(String.format("validateCaseTypesInResponse called - requestCaseTypes: %s, responseLength: %d", requestCaseTypes, responseBytes.length)); + log.error(String.format("validateCaseTypesInResponse called - requestCaseTypes: %s, responseLength: %d", requestCaseTypes, responseBytes.length)); if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { - log.warn("No case_type in request data - skipping validation"); + log.error("No case_type in request data - skipping validation"); return; } @@ -200,32 +197,30 @@ private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId TreeElement root = TreeUtilities.xmlStreamToTreeElement(responseStream, instanceId); if (root == null) { - log.warn("Root element is null - cannot validate"); + log.error("Root element is null - cannot validate"); return; } - log.info(String.format("Root element has %d children", root.getNumChildren())); + log.error(String.format("Root element has %d children", root.getNumChildren())); for (int i = 0; i < root.getNumChildren(); i++) { TreeElement child = root.getChildAt(i); String attributeValue = child.getAttributeValue(null, "case_type"); - log.info(String.format("Child %d: name=%s, case_type=%s", i, child.getName(), attributeValue)); + log.error(String.format("Child %d: name=%s, case_type=%s", i, child.getName(), attributeValue)); if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { - Exception e = new Exception("Response case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + + log.error("Response case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + "Url: " + url + " request data: " + requestData.toString()); - FormplayerSentry.captureException(e, SentryLevel.WARNING); - log.error("CASE TYPE MISMATCH DETECTED!", e); } } } private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchStorage, Collection requestCaseTypes, String url, Multimap requestData) { - log.info(String.format("validateCaseTypesInStorage called - requestCaseTypes: %s", requestCaseTypes)); + log.error(String.format("validateCaseTypesInStorage called - requestCaseTypes: %s", requestCaseTypes)); if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { - log.warn("No case_type in request data - skipping storage validation"); + log.error("No case_type in request data - skipping storage validation"); return; } try { @@ -235,16 +230,16 @@ private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchS Case caseObj = iterator.nextRecord(); caseCount++; String caseType = caseObj.getTypeId(); - log.info(String.format("Storage case %d: id=%s, type=%s", caseCount, caseObj.getCaseId(), caseType)); + log.error(String.format("Storage case %d: id=%s, type=%s", caseCount, caseObj.getCaseId(), caseType)); if (caseType != null && !requestCaseTypes.contains(caseType)) { - Exception e = new Exception("Storage case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + caseType + "\n" + - "Url: " + url + " request data: " + requestData.toString()); + Exception e = new Exception(); FormplayerSentry.captureException(e, SentryLevel.WARNING); - log.error("STORAGE CASE TYPE MISMATCH DETECTED!", e); + log.error("Storage case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + caseType + "\n" + + "Url: " + url + " request data: " + requestData.toString()); } } - log.info(String.format("Validated %d cases from storage", caseCount)); + log.error(String.format("Validated %d cases from storage", caseCount)); } catch (Exception e) { log.error("Error validating case types from storage", e); } From 68ebdb19dc38cedbc1a123e828012effd5cd2d95 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Fri, 23 Jan 2026 06:13:37 -0600 Subject: [PATCH 07/21] Remove per child logging. to verbose --- .../org/commcare/formplayer/services/CaseSearchHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index dc55d5139..9b07850d6 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -206,7 +206,7 @@ private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId for (int i = 0; i < root.getNumChildren(); i++) { TreeElement child = root.getChildAt(i); String attributeValue = child.getAttributeValue(null, "case_type"); - log.error(String.format("Child %d: name=%s, case_type=%s", i, child.getName(), attributeValue)); +// log.error(String.format("Child %d: name=%s, case_type=%s", i, child.getName(), attributeValue)); if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { log.error("Response case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + @@ -230,7 +230,7 @@ private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchS Case caseObj = iterator.nextRecord(); caseCount++; String caseType = caseObj.getTypeId(); - log.error(String.format("Storage case %d: id=%s, type=%s", caseCount, caseObj.getCaseId(), caseType)); +// log.error(String.format("Storage case %d: id=%s, type=%s", caseCount, caseObj.getCaseId(), caseType)); if (caseType != null && !requestCaseTypes.contains(caseType)) { Exception e = new Exception(); From 9a1b285c420f6d0b4be12d54b00fefe6eceb8da7 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Fri, 23 Jan 2026 07:25:17 -0600 Subject: [PATCH 08/21] Add logging the entity conversion --- .../beans/menus/EntityListResponse.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index 36478af50..0377a71c0 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -2,6 +2,8 @@ import static org.commcare.formplayer.util.Constants.TOGGLE_SPLIT_SCREEN_CASE_SEARCH; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.commcare.cases.entity.Entity; import org.commcare.core.graph.model.GraphData; import org.commcare.core.graph.util.GraphException; @@ -42,6 +44,8 @@ * Created by willpride on 4/13/16. */ public class EntityListResponse extends MenuBean { + private static final Log log = LogFactory.getLog(EntityListResponse.class); + private EntityBean[] entities; private DisplayElement[] actions; private String redoLast; @@ -168,10 +172,25 @@ private static EntityBean[] processEntitiesForCaseDetail(Detail detail, TreeRefe public static List processEntitiesForCaseList(List> entityList, EvaluationContext ec, EntityDatum neededDatum) { + log.error(String.format("=== processEntitiesForCaseList: Processing %d entities ===", entityList.size())); + List entities = new ArrayList<>(); + int entityIndex = 0; for (Entity entity : entityList) { - entities.add(toEntityBean(entity, ec, neededDatum)); + EntityBean bean = toEntityBean(entity, ec, neededDatum); + entities.add(bean); + + // Log first few and last entity + if (entityIndex < 3 || entityIndex == entityList.size() - 1) { + log.error(String.format("EntityBean %d: id=%s, data[0]=%s", + entityIndex, + bean.getId(), + bean.getData() != null && bean.getData().length > 0 ? bean.getData()[0] : "none")); + } + entityIndex++; } + + log.error(String.format("=== Completed: Created %d EntityBeans ===", entities.size())); return entities; } @@ -239,9 +258,21 @@ private static EntityBean toEntityBean(Entity entity, Object[] entityData = entity.getData(); Object[] data = new Object[entityData.length]; String id = getEntityId(entity.getElement(), neededDatum, ec); + + // Log entity conversion details + log.error(String.format("toEntityBean: ref=%s, id=%s, rawData[0]=%s", + entity.getElement().toString(), + id, + entityData.length > 0 ? entityData[0] : "none")); + EntityBean entityBean = new EntityBean(id); for (int i = 0; i < entityData.length; i++) { data[i] = processData(entityData[i]); + // Log if data changes during processing + if (i == 0 && !String.valueOf(entityData[i]).equals(String.valueOf(data[i]))) { + log.error(String.format(" Data[0] changed during processing: %s -> %s", + entityData[i], data[i])); + } } entityBean.setData(data); entityBean.setGroupKey(entity.getGroupKey()); From 53d910e7114b01c2e74190bc2812bc06c46df37b Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 09:07:48 -0600 Subject: [PATCH 09/21] Log more than one case type in controller response --- .../application/MenuController.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index 2176f24e0..43d1a1fe5 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -11,10 +11,7 @@ import org.commcare.formplayer.beans.ResponseMetaData; import org.commcare.formplayer.beans.SessionNavigationBean; import org.commcare.formplayer.beans.SubmitResponseBean; -import org.commcare.formplayer.beans.menus.BaseResponseBean; -import org.commcare.formplayer.beans.menus.EntityDetailListResponse; -import org.commcare.formplayer.beans.menus.EntityDetailResponse; -import org.commcare.formplayer.beans.menus.LocationRelevantResponseBean; +import org.commcare.formplayer.beans.menus.*; import org.commcare.formplayer.services.FormplayerStorageFactory; import org.commcare.formplayer.services.MenuSessionFactory; import org.commcare.formplayer.services.ResponseMetaDataTracker; @@ -35,6 +32,8 @@ import org.springframework.web.server.ResponseStatusException; import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; import jakarta.servlet.http.HttpServletRequest; @@ -185,6 +184,20 @@ public BaseResponseBean navigateSessionWithAuth(@RequestBody SessionNavigationBe sessionNavigationBean.getFormSessionId() ); + if (response instanceof EntityListResponse entityListResponse && + entityListResponse.getEntities().length > 0 && + entityListResponse.getHeaders().length > 0 && + entityListResponse.getHeaders()[0].equals("Case Type") + ) { + Set caseTypes = new HashSet<>(); + for (EntityBean entity : entityListResponse.getEntities()) { + caseTypes.add(entity.getData()[0].toString()); + } + if (caseTypes.size() > 1) { + log.error("Expected all 'Case Type's to be the same. Got: " + caseTypes); + } + } + setResponseMetaData(response); SubmitResponseBean formSubmissionResponse = handleAutoFormSubmission(request, sessionNavigationBean, From e72a318a020962603245f9b96dce95298b6a2bf4 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 10:43:52 -0600 Subject: [PATCH 10/21] Log individual entities in controller --- .../org/commcare/formplayer/application/MenuController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index 43d1a1fe5..4baef2d72 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -195,6 +195,9 @@ public BaseResponseBean navigateSessionWithAuth(@RequestBody SessionNavigationBe } if (caseTypes.size() > 1) { log.error("Expected all 'Case Type's to be the same. Got: " + caseTypes); + for (EntityBean entity : entityListResponse.getEntities()) { + log.error(entity.getId() + ": " + entity.getData()[0].toString()); + } } } From a65a508620cb722f2c70f5ff59ab908923c34de1 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 11:56:14 -0600 Subject: [PATCH 11/21] Add logging to more layers --- .../application/MenuController.java | 31 +++++++++++-------- .../services/MenuSessionRunnerService.java | 6 ++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index 4baef2d72..a42c5d565 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -184,6 +184,22 @@ public BaseResponseBean navigateSessionWithAuth(@RequestBody SessionNavigationBe sessionNavigationBean.getFormSessionId() ); + logCaseTypeColumnIfPresent(response, "controller", log); + + setResponseMetaData(response); + + SubmitResponseBean formSubmissionResponse = handleAutoFormSubmission(request, sessionNavigationBean, + response); + if (formSubmissionResponse != null) { + return formSubmissionResponse; + } else { + notificationLogger.logNotification(response.getNotification(), request); + return setLocationNeeds(response, menuSession); + } + } + + public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String label, Log log) { + log.error("Checking at '" + label + "'"); if (response instanceof EntityListResponse entityListResponse && entityListResponse.getEntities().length > 0 && entityListResponse.getHeaders().length > 0 && @@ -193,24 +209,13 @@ public BaseResponseBean navigateSessionWithAuth(@RequestBody SessionNavigationBe for (EntityBean entity : entityListResponse.getEntities()) { caseTypes.add(entity.getData()[0].toString()); } - if (caseTypes.size() > 1) { - log.error("Expected all 'Case Type's to be the same. Got: " + caseTypes); + if (caseTypes.size() > 0) { + log.error("Expected all 'Case Type's to be the same at '" + label + "'. Got: " + caseTypes); for (EntityBean entity : entityListResponse.getEntities()) { log.error(entity.getId() + ": " + entity.getData()[0].toString()); } } } - - setResponseMetaData(response); - - SubmitResponseBean formSubmissionResponse = handleAutoFormSubmission(request, sessionNavigationBean, - response); - if (formSubmissionResponse != null) { - return formSubmissionResponse; - } else { - notificationLogger.logNotification(response.getNotification(), request); - return setLocationNeeds(response, menuSession); - } } private void setResponseMetaData(BaseResponseBean response) { diff --git a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java index d935bb2b3..c28bd44ed 100644 --- a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java +++ b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java @@ -14,6 +14,7 @@ import org.apache.commons.logging.LogFactory; import org.commcare.core.interfaces.RemoteInstanceFetcher; import org.commcare.data.xml.VirtualInstances; +import org.commcare.formplayer.application.MenuController; import org.commcare.formplayer.beans.NewFormResponse; import org.commcare.formplayer.beans.NotificationMessage; import org.commcare.formplayer.beans.auth.FeatureFlagChecker; @@ -191,6 +192,7 @@ public BaseResponseBean getNextMenu(@Nullable Screen nextScreen, MenuSession men } addHereFuncHandler((EntityScreen)nextScreen, menuSession); menuResponseBean = new EntityListResponse((EntityScreen)nextScreen); + MenuController.logCaseTypeColumnIfPresent(menuResponseBean, "getNextMenu", log); datadog.addRequestScopedTag(Constants.MODULE_TAG, "case_list"); Sentry.setTag(Constants.MODULE_TAG, "case_list"); // using getBestTitle to eliminate risk of showing private information @@ -301,6 +303,7 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, NotificationMessage.Tag.sync), true); } + log.error("advanceSessionWithSelections: postSyncResponse"); return postSyncResponse; } } else { @@ -325,6 +328,7 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, queryData, entityScreenContext ); + MenuController.logCaseTypeColumnIfPresent(nextResponse, "advanceSessionWithSelections", log); restoreFactory.cacheSessionSelections(menuSession.getSelections()); if (nextResponse != null) { @@ -333,11 +337,13 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, } log.info("Returning menu: " + nextResponse); nextResponse.setSelections(menuSession.getSelections()); + log.error("advanceSessionWithSelections: nextResponse"); return nextResponse; } else { if (notificationMessage == null) { notificationMessage = new NotificationMessage(null, false, NotificationMessage.Tag.selection); } + log.error("advanceSessionWithSelections: BaseResponseBean"); return new BaseResponseBean(null, notificationMessage, true); } } From f443b3354211e6df89e20cba79d217ca6b42cc16 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 12:10:43 -0600 Subject: [PATCH 12/21] Fix case type check --- .../org/commcare/formplayer/application/MenuController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index a42c5d565..2ff32efda 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -209,7 +209,7 @@ public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String for (EntityBean entity : entityListResponse.getEntities()) { caseTypes.add(entity.getData()[0].toString()); } - if (caseTypes.size() > 0) { + if (caseTypes.size() > 1) { log.error("Expected all 'Case Type's to be the same at '" + label + "'. Got: " + caseTypes); for (EntityBean entity : entityListResponse.getEntities()) { log.error(entity.getId() + ": " + entity.getData()[0].toString()); From fc678edca45aa715c9b35cdac01c5d81576b0101 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 13:10:31 -0600 Subject: [PATCH 13/21] Make log message easier to filter --- .../org/commcare/formplayer/application/MenuController.java | 6 +++--- .../formplayer/services/MenuSessionRunnerService.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index 2ff32efda..3f5902108 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -199,7 +199,7 @@ public BaseResponseBean navigateSessionWithAuth(@RequestBody SessionNavigationBe } public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String label, Log log) { - log.error("Checking at '" + label + "'"); + log.error("USH-6370_1: Checking at '" + label + "'"); if (response instanceof EntityListResponse entityListResponse && entityListResponse.getEntities().length > 0 && entityListResponse.getHeaders().length > 0 && @@ -210,9 +210,9 @@ public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String caseTypes.add(entity.getData()[0].toString()); } if (caseTypes.size() > 1) { - log.error("Expected all 'Case Type's to be the same at '" + label + "'. Got: " + caseTypes); + log.error("USH-6370_1: Expected all 'Case Type's to be the same at '" + label + "'. Got: " + caseTypes); for (EntityBean entity : entityListResponse.getEntities()) { - log.error(entity.getId() + ": " + entity.getData()[0].toString()); + log.error("USH-6370_2: " + entity.getId() + ": " + entity.getData()[0].toString()); } } } diff --git a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java index c28bd44ed..aa6b7b9ee 100644 --- a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java +++ b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java @@ -303,7 +303,7 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, NotificationMessage.Tag.sync), true); } - log.error("advanceSessionWithSelections: postSyncResponse"); + log.error("USH-6370_2: advanceSessionWithSelections: postSyncResponse"); return postSyncResponse; } } else { @@ -337,13 +337,13 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, } log.info("Returning menu: " + nextResponse); nextResponse.setSelections(menuSession.getSelections()); - log.error("advanceSessionWithSelections: nextResponse"); + log.error("USH-6370_2: advanceSessionWithSelections: nextResponse"); return nextResponse; } else { if (notificationMessage == null) { notificationMessage = new NotificationMessage(null, false, NotificationMessage.Tag.selection); } - log.error("advanceSessionWithSelections: BaseResponseBean"); + log.error("USH-6370_2: advanceSessionWithSelections: BaseResponseBean"); return new BaseResponseBean(null, notificationMessage, true); } } From fca728a6996957b2d62e86cfb84cf4e58722dc89 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 15:25:52 -0600 Subject: [PATCH 14/21] Simplify logging and remove unused logs Sentry might be filtering out logs if there are to many errors. So restrict the logging to the necessary --- .../application/MenuController.java | 17 +++++++- .../beans/menus/EntityListResponse.java | 42 +++++++++---------- .../formplayer/services/CaseSearchHelper.java | 12 +++--- .../services/MenuSessionRunnerService.java | 6 +-- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index 3f5902108..c21c8e3b7 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -199,22 +199,35 @@ public BaseResponseBean navigateSessionWithAuth(@RequestBody SessionNavigationBe } public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String label, Log log) { - log.error("USH-6370_1: Checking at '" + label + "'"); + if (response instanceof EntityListResponse entityListResponse && entityListResponse.getEntities().length > 0 && entityListResponse.getHeaders().length > 0 && entityListResponse.getHeaders()[0].equals("Case Type") ) { + StringBuilder sb = new StringBuilder("USH-6370 Checking at '" + label + "' "); + Set caseTypes = new HashSet<>(); for (EntityBean entity : entityListResponse.getEntities()) { caseTypes.add(entity.getData()[0].toString()); } if (caseTypes.size() > 1) { + sb.append("mismatch"); + sb.append("\nExpected all 'Case Type's to be the same at '") + .append(label) + .append("'. Got: ") + .append(caseTypes); log.error("USH-6370_1: Expected all 'Case Type's to be the same at '" + label + "'. Got: " + caseTypes); for (EntityBean entity : entityListResponse.getEntities()) { - log.error("USH-6370_2: " + entity.getId() + ": " + entity.getData()[0].toString()); + sb.append("\n") + .append(entity.getId()) + .append(": ") + .append(entity.getData()[0].toString()); } + } else { + sb.append("ok"); } + log.error(sb.toString()); } } diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index 0377a71c0..92335e4f0 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -172,25 +172,25 @@ private static EntityBean[] processEntitiesForCaseDetail(Detail detail, TreeRefe public static List processEntitiesForCaseList(List> entityList, EvaluationContext ec, EntityDatum neededDatum) { - log.error(String.format("=== processEntitiesForCaseList: Processing %d entities ===", entityList.size())); +// log.error(String.format("=== processEntitiesForCaseList: Processing %d entities ===", entityList.size())); List entities = new ArrayList<>(); - int entityIndex = 0; +// int entityIndex = 0; for (Entity entity : entityList) { EntityBean bean = toEntityBean(entity, ec, neededDatum); entities.add(bean); - - // Log first few and last entity - if (entityIndex < 3 || entityIndex == entityList.size() - 1) { - log.error(String.format("EntityBean %d: id=%s, data[0]=%s", - entityIndex, - bean.getId(), - bean.getData() != null && bean.getData().length > 0 ? bean.getData()[0] : "none")); - } - entityIndex++; +// +// // Log first few and last entity +// if (entityIndex < 3 || entityIndex == entityList.size() - 1) { +// log.error(String.format("EntityBean %d: id=%s, data[0]=%s", +// entityIndex, +// bean.getId(), +// bean.getData() != null && bean.getData().length > 0 ? bean.getData()[0] : "none")); +// } +// entityIndex++; } - log.error(String.format("=== Completed: Created %d EntityBeans ===", entities.size())); +// log.error(String.format("=== Completed: Created %d EntityBeans ===", entities.size())); return entities; } @@ -260,19 +260,19 @@ private static EntityBean toEntityBean(Entity entity, String id = getEntityId(entity.getElement(), neededDatum, ec); // Log entity conversion details - log.error(String.format("toEntityBean: ref=%s, id=%s, rawData[0]=%s", - entity.getElement().toString(), - id, - entityData.length > 0 ? entityData[0] : "none")); +// log.error(String.format("toEntityBean: ref=%s, id=%s, rawData[0]=%s", +// entity.getElement().toString(), +// id, +// entityData.length > 0 ? entityData[0] : "none")); EntityBean entityBean = new EntityBean(id); for (int i = 0; i < entityData.length; i++) { data[i] = processData(entityData[i]); - // Log if data changes during processing - if (i == 0 && !String.valueOf(entityData[i]).equals(String.valueOf(data[i]))) { - log.error(String.format(" Data[0] changed during processing: %s -> %s", - entityData[i], data[i])); - } +// // Log if data changes during processing +// if (i == 0 && !String.valueOf(entityData[i]).equals(String.valueOf(data[i]))) { +// log.error(String.format(" Data[0] changed during processing: %s -> %s", +// entityData[i], data[i])); +// } } entityBean.setData(data); entityBean.setGroupKey(entity.getGroupKey()); diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index 9b07850d6..d66e5e6b3 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -87,8 +87,8 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan - skipCache = true; - log.error("Error getExternalRoot skipCache: " + skipCache); +// skipCache = true; +// log.error("Error getExternalRoot skipCache: " + skipCache); Multimap requestData = source.getRequestData(); String url = source.getSourceUri(); @@ -106,18 +106,18 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan IStorageUtilityIndexed caseSearchStorage = caseSearchSandbox.getCaseStorage(); FormplayerCaseIndexTable caseSearchIndexTable = getCaseIndexTable(caseSearchSandbox, caseSearchTableName); if (skipCache || !caseSearchStorage.isStorageExists()) { - Collection requestCaseTypes = requestData.get("case_type"); - log.error(String.format("Case search validation - requestCaseTypes: %s, requestData: %s", requestCaseTypes, requestData)); +// Collection requestCaseTypes = requestData.get("case_type"); +// log.error(String.format("Case search validation - requestCaseTypes: %s, requestData: %s", requestCaseTypes, requestData)); String responseString = webClient.postFormData(url, requestData); if (responseString != null) { byte[] responseBytes = responseString.getBytes(StandardCharsets.UTF_8); - validateCaseTypesInResponse(responseBytes, instanceId, requestCaseTypes, url, requestData); +// validateCaseTypesInResponse(responseBytes, instanceId, requestCaseTypes, url, requestData); ByteArrayInputStream responeStream = new ByteArrayInputStream(responseBytes); if (shouldParseIntoCaseSearchStorage(source.useCaseTemplate())) { parseIntoCaseSearchStorage(caseSearchDb, caseSearchSandbox, caseSearchStorage, responeStream, caseSearchIndexTable); // Validate case types after parsing into storage - validateCaseTypesInStorage(caseSearchStorage, requestCaseTypes, url, requestData); +// validateCaseTypesInStorage(caseSearchStorage, requestCaseTypes, url, requestData); } else { TreeElement root = TreeUtilities.xmlStreamToTreeElement(responeStream, instanceId); if (root != null) { diff --git a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java index aa6b7b9ee..6b0d24153 100644 --- a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java +++ b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java @@ -303,7 +303,7 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, NotificationMessage.Tag.sync), true); } - log.error("USH-6370_2: advanceSessionWithSelections: postSyncResponse"); +// log.error("USH-6370_2: advanceSessionWithSelections: postSyncResponse"); return postSyncResponse; } } else { @@ -337,13 +337,13 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, } log.info("Returning menu: " + nextResponse); nextResponse.setSelections(menuSession.getSelections()); - log.error("USH-6370_2: advanceSessionWithSelections: nextResponse"); +// log.error("USH-6370_2: advanceSessionWithSelections: nextResponse"); return nextResponse; } else { if (notificationMessage == null) { notificationMessage = new NotificationMessage(null, false, NotificationMessage.Tag.selection); } - log.error("USH-6370_2: advanceSessionWithSelections: BaseResponseBean"); +// log.error("USH-6370_2: advanceSessionWithSelections: BaseResponseBean"); return new BaseResponseBean(null, notificationMessage, true); } } From 38d47f115445ef3f146a3c515bce24501107cba2 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 17:07:40 -0600 Subject: [PATCH 15/21] Add logging back into EntityListResponse --- .../application/MenuController.java | 2 +- .../beans/menus/EntityListResponse.java | 34 ++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index c21c8e3b7..f6efaccae 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -217,7 +217,7 @@ public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String .append(label) .append("'. Got: ") .append(caseTypes); - log.error("USH-6370_1: Expected all 'Case Type's to be the same at '" + label + "'. Got: " + caseTypes); + for (EntityBean entity : entityListResponse.getEntities()) { sb.append("\n") .append(entity.getId()) diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index 92335e4f0..f98d04645 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -31,11 +31,7 @@ import org.springframework.web.util.UriBuilder; import org.springframework.web.util.UriTemplate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Vector; +import java.util.*; import java.util.function.Predicate; import datadog.trace.api.Trace; @@ -87,7 +83,8 @@ public EntityListResponse(EntityScreen nextScreen) { // subscreen should be of type EntityListSubscreen in order to init this response class Subscreen subScreen = nextScreen.getCurrentScreen(); if (subScreen instanceof EntityListSubscreen) { - EntityListSubscreen entityListScreen = ((EntityListSubscreen)nextScreen.getCurrentScreen()); + StringBuilder sb = new StringBuilder("USH-6370 EntityListResponse() "); + EntityListSubscreen entityListScreen = ((EntityListSubscreen) nextScreen.getCurrentScreen()); Vector entityListActions = entityListScreen.getActions(); this.actions = processActions(nextScreen.getSession(), entityListActions); this.redoLast = processRedoLast(entityListActions); @@ -102,10 +99,29 @@ public EntityListResponse(EntityScreen nextScreen) { offset); EvaluationContext ec = nextScreen.getEvalContext(); SessionWrapper session = nextScreen.getSession(); - EntityDatum neededDatum = (EntityDatum)session.getNeededDatum(); + EntityDatum neededDatum = (EntityDatum) session.getNeededDatum(); List entityBeans = processEntitiesForCaseList(entitesForPage, ec, neededDatum); entities = new EntityBean[entityBeans.size()]; entityBeans.toArray(entities); + + Set caseTypes = new HashSet<>(); + for (EntityBean entity : entities) { + caseTypes.add(entity.getData()[0].toString()); + } + if (caseTypes.size() > 1) { + sb.append("mismatch"); + sb.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") + .append(caseTypes); + for (EntityBean entity : entities) { + sb.append("\n") + .append(entity.getId()) + .append(": ") + .append(entity.getData()[0].toString()); + } + } else { + sb.append("ok"); + } + setNoItemsText(getNoItemsTextLocaleString(detail)); setSelectText(getSelectTextLocaleString(detail)); hasDetails = nextScreen.getLongDetail() != null; @@ -121,7 +137,7 @@ public EntityListResponse(EntityScreen nextScreen) { this.sortIndices = detail.getOrderedFieldIndicesForSorting(); isMultiSelect = nextScreen instanceof MultiSelectEntityScreen; if (isMultiSelect) { - maxSelectValue = ((MultiSelectEntityScreen)nextScreen).getMaxSelectValue(); + maxSelectValue = ((MultiSelectEntityScreen) nextScreen).getMaxSelectValue(); } if (detail.getGroup() != null) { groupHeaderRows = detail.getGroup().getHeaderRows(); @@ -129,11 +145,13 @@ public EntityListResponse(EntityScreen nextScreen) { QueryScreen queryScreen = nextScreen.getQueryScreen(); if (queryScreen != null) { + sb.append("\nqueryScreen"); setQueryKey(queryScreen.getQueryKey()); if (FeatureFlagChecker.isToggleEnabled(TOGGLE_SPLIT_SCREEN_CASE_SEARCH)) { queryResponse = new QueryResponseBean(queryScreen); } } + log.error(sb.toString()); } } From 2a7f6d3fc33ac7cba6eb2cefc25eff077ec4f615 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 17:48:33 -0600 Subject: [PATCH 16/21] Add more logging back into EntityListResponse --- .../beans/menus/EntityListResponse.java | 42 +++++++++---------- .../services/MenuSessionRunnerService.java | 4 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index f98d04645..a14b5a976 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -83,7 +83,7 @@ public EntityListResponse(EntityScreen nextScreen) { // subscreen should be of type EntityListSubscreen in order to init this response class Subscreen subScreen = nextScreen.getCurrentScreen(); if (subScreen instanceof EntityListSubscreen) { - StringBuilder sb = new StringBuilder("USH-6370 EntityListResponse() "); + StringBuilder sb = new StringBuilder("USH-6370 Checking at 'EntityListResponse()' "); EntityListSubscreen entityListScreen = ((EntityListSubscreen) nextScreen.getCurrentScreen()); Vector entityListActions = entityListScreen.getActions(); this.actions = processActions(nextScreen.getSession(), entityListActions); @@ -190,25 +190,22 @@ private static EntityBean[] processEntitiesForCaseDetail(Detail detail, TreeRefe public static List processEntitiesForCaseList(List> entityList, EvaluationContext ec, EntityDatum neededDatum) { -// log.error(String.format("=== processEntitiesForCaseList: Processing %d entities ===", entityList.size())); + StringBuilder sb = new StringBuilder("USH-6370 Checking at 'processEntitiesForCaseList' "); List entities = new ArrayList<>(); -// int entityIndex = 0; + StringBuilder innerSb = new StringBuilder(); for (Entity entity : entityList) { - EntityBean bean = toEntityBean(entity, ec, neededDatum); + EntityBean bean = toEntityBean(entity, ec, neededDatum, innerSb); entities.add(bean); -// -// // Log first few and last entity -// if (entityIndex < 3 || entityIndex == entityList.size() - 1) { -// log.error(String.format("EntityBean %d: id=%s, data[0]=%s", -// entityIndex, -// bean.getId(), -// bean.getData() != null && bean.getData().length > 0 ? bean.getData()[0] : "none")); -// } -// entityIndex++; + } + if (innerSb.isEmpty()) { + sb.append("ok"); + } else { + sb.append("missmatch") + .append(innerSb); } -// log.error(String.format("=== Completed: Created %d EntityBeans ===", entities.size())); + log.error(sb.toString()); return entities; } @@ -272,7 +269,7 @@ public boolean isHasDetails() { // Converts the Given Entity to EntityBean @Trace private static EntityBean toEntityBean(Entity entity, - EvaluationContext ec, EntityDatum neededDatum) { + EvaluationContext ec, EntityDatum neededDatum, StringBuilder sb) { Object[] entityData = entity.getData(); Object[] data = new Object[entityData.length]; String id = getEntityId(entity.getElement(), neededDatum, ec); @@ -282,16 +279,19 @@ private static EntityBean toEntityBean(Entity entity, // entity.getElement().toString(), // id, // entityData.length > 0 ? entityData[0] : "none")); - + + EntityBean entityBean = new EntityBean(id); for (int i = 0; i < entityData.length; i++) { data[i] = processData(entityData[i]); -// // Log if data changes during processing -// if (i == 0 && !String.valueOf(entityData[i]).equals(String.valueOf(data[i]))) { -// log.error(String.format(" Data[0] changed during processing: %s -> %s", -// entityData[i], data[i])); -// } + if (i == 0 && !String.valueOf(entityData[i]).equals(String.valueOf(data[i]))) { + sb.append("\n").append(id) + .append(" Data[").append(i).append("] changed during processing: ") + .append(entityData[i]).append(" -> ").append(data[i]); + } } + + entityBean.setData(data); entityBean.setGroupKey(entity.getGroupKey()); entityBean.setAltText(entity.getAltText()); diff --git a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java index 6b0d24153..309b23374 100644 --- a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java +++ b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java @@ -192,7 +192,7 @@ public BaseResponseBean getNextMenu(@Nullable Screen nextScreen, MenuSession men } addHereFuncHandler((EntityScreen)nextScreen, menuSession); menuResponseBean = new EntityListResponse((EntityScreen)nextScreen); - MenuController.logCaseTypeColumnIfPresent(menuResponseBean, "getNextMenu", log); +// MenuController.logCaseTypeColumnIfPresent(menuResponseBean, "getNextMenu", log); datadog.addRequestScopedTag(Constants.MODULE_TAG, "case_list"); Sentry.setTag(Constants.MODULE_TAG, "case_list"); // using getBestTitle to eliminate risk of showing private information @@ -328,7 +328,7 @@ public BaseResponseBean advanceSessionWithSelections(MenuSession menuSession, queryData, entityScreenContext ); - MenuController.logCaseTypeColumnIfPresent(nextResponse, "advanceSessionWithSelections", log); +// MenuController.logCaseTypeColumnIfPresent(nextResponse, "advanceSessionWithSelections", log); restoreFactory.cacheSessionSelections(menuSession.getSelections()); if (nextResponse != null) { From f20be0f8e12df831c3256947a571a557430b7fc5 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 27 Jan 2026 20:46:06 -0600 Subject: [PATCH 17/21] More logging for steps in EntityListResponse --- .../beans/menus/EntityListResponse.java | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index a14b5a976..fccc28961 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -83,13 +83,24 @@ public EntityListResponse(EntityScreen nextScreen) { // subscreen should be of type EntityListSubscreen in order to init this response class Subscreen subScreen = nextScreen.getCurrentScreen(); if (subScreen instanceof EntityListSubscreen) { - StringBuilder sb = new StringBuilder("USH-6370 Checking at 'EntityListResponse()' "); EntityListSubscreen entityListScreen = ((EntityListSubscreen) nextScreen.getCurrentScreen()); Vector entityListActions = entityListScreen.getActions(); this.actions = processActions(nextScreen.getSession(), entityListActions); this.redoLast = processRedoLast(entityListActions); List> entityList = entityListScreen.getEntities(); + StringBuilder sb1 = new StringBuilder("USH-6370 Checking at 'entityListScreen.getEntities' "); + Set caseTypes = new HashSet<>(); + for (Entity e: entityList) { + caseTypes.add(e.getData()[0].toString()); + } + if (caseTypes.size() > 1) { + sb1.append("mismatch"); + sb1.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") + .append(caseTypes); + } else { + sb1.append("ok"); + } EntityScreenContext entityScreenContext = nextScreen.getEntityScreenContext(); int casesPerPage = entityScreenContext.getCasesPerPage(); casesPerPage = Math.min(casesPerPage, MAX_CASES_PER_PAGE); @@ -97,6 +108,18 @@ public EntityListResponse(EntityScreen nextScreen) { Detail detail = nextScreen.getShortDetail(); List> entitesForPage = paginateEntities(entityList, detail, casesPerPage, offset); + StringBuilder sb2 = new StringBuilder("USH-6370 Checking at 'paginateEntities' "); + caseTypes = new HashSet<>(); + for (Entity e: entityList) { + caseTypes.add(e.getData()[0].toString()); + } + if (caseTypes.size() > 1) { + sb2.append("mismatch"); + sb2.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") + .append(caseTypes); + } else { + sb2.append("ok"); + } EvaluationContext ec = nextScreen.getEvalContext(); SessionWrapper session = nextScreen.getSession(); EntityDatum neededDatum = (EntityDatum) session.getNeededDatum(); @@ -104,10 +127,11 @@ public EntityListResponse(EntityScreen nextScreen) { entities = new EntityBean[entityBeans.size()]; entityBeans.toArray(entities); - Set caseTypes = new HashSet<>(); + caseTypes = new HashSet<>(); for (EntityBean entity : entities) { caseTypes.add(entity.getData()[0].toString()); } + StringBuilder sb = new StringBuilder("USH-6370 Checking at 'processEntitiesForCaseList' "); if (caseTypes.size() > 1) { sb.append("mismatch"); sb.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") @@ -151,7 +175,11 @@ public EntityListResponse(EntityScreen nextScreen) { queryResponse = new QueryResponseBean(queryScreen); } } - log.error(sb.toString()); + if (this.headers.length > 0 && this.headers[0].equals("Case Type")) { + log.error(sb1.toString()); + log.error(sb2.toString()); + log.error(sb.toString()); + } } } From 8d999d369b847e5edda4985ed023c2e9519150a7 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Wed, 28 Jan 2026 07:19:30 -0600 Subject: [PATCH 18/21] Add CaseSearchHelper logging back in Not so sure about this anymore skip cache again --- .../beans/menus/EntityListResponse.java | 8 +- .../formplayer/services/CaseSearchHelper.java | 132 +++++++++--------- 2 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index fccc28961..4aab3d275 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -96,7 +96,7 @@ public EntityListResponse(EntityScreen nextScreen) { } if (caseTypes.size() > 1) { sb1.append("mismatch"); - sb1.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") + sb1.append("\nExpected all 'Case Type's to be the same at. Got: ") .append(caseTypes); } else { sb1.append("ok"); @@ -115,7 +115,7 @@ public EntityListResponse(EntityScreen nextScreen) { } if (caseTypes.size() > 1) { sb2.append("mismatch"); - sb2.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") + sb2.append("\nExpected all 'Case Type's to be the same. Got: ") .append(caseTypes); } else { sb2.append("ok"); @@ -134,7 +134,7 @@ public EntityListResponse(EntityScreen nextScreen) { StringBuilder sb = new StringBuilder("USH-6370 Checking at 'processEntitiesForCaseList' "); if (caseTypes.size() > 1) { sb.append("mismatch"); - sb.append("\nExpected all 'Case Type's to be the same at 'processEntitiesForCaseList'. Got: ") + sb.append("\nExpected all 'Case Type's to be the same. Got: ") .append(caseTypes); for (EntityBean entity : entities) { sb.append("\n") @@ -218,7 +218,7 @@ private static EntityBean[] processEntitiesForCaseDetail(Detail detail, TreeRefe public static List processEntitiesForCaseList(List> entityList, EvaluationContext ec, EntityDatum neededDatum) { - StringBuilder sb = new StringBuilder("USH-6370 Checking at 'processEntitiesForCaseList' "); + StringBuilder sb = new StringBuilder("USH-6370 Checking in 'processEntitiesForCaseList' "); List entities = new ArrayList<>(); StringBuilder innerSb = new StringBuilder(); diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index d66e5e6b3..f45a7a674 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -6,6 +6,7 @@ import io.sentry.SentryLevel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.commcare.cases.entity.Entity; import org.commcare.cases.instance.CaseInstanceTreeElement; import org.commcare.cases.model.Case; import org.commcare.core.parse.CaseInstanceXmlTransactionParserFactory; @@ -21,11 +22,7 @@ import org.commcare.formplayer.util.SerializationUtil; import org.commcare.formplayer.web.client.WebClient; import org.commcare.util.screen.ScreenUtils; -import org.javarosa.core.model.instance.AbstractTreeElement; -import org.javarosa.core.model.instance.ExternalDataInstance; -import org.javarosa.core.model.instance.ExternalDataInstanceSource; -import org.javarosa.core.model.instance.InstanceBase; -import org.javarosa.core.model.instance.TreeElement; +import org.javarosa.core.model.instance.*; import org.javarosa.core.model.instance.utils.TreeUtilities; import org.javarosa.core.services.storage.IStorageIterator; import org.javarosa.core.services.storage.IStorageUtilityIndexed; @@ -46,10 +43,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; @CacheConfig(cacheNames = "case_search") @Component @@ -85,9 +79,7 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException, IOException { - - -// skipCache = true; + skipCache = true; // log.error("Error getExternalRoot skipCache: " + skipCache); Multimap requestData = source.getRequestData(); @@ -106,24 +98,21 @@ public AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstan IStorageUtilityIndexed caseSearchStorage = caseSearchSandbox.getCaseStorage(); FormplayerCaseIndexTable caseSearchIndexTable = getCaseIndexTable(caseSearchSandbox, caseSearchTableName); if (skipCache || !caseSearchStorage.isStorageExists()) { -// Collection requestCaseTypes = requestData.get("case_type"); -// log.error(String.format("Case search validation - requestCaseTypes: %s, requestData: %s", requestCaseTypes, requestData)); + Collection requestCaseTypes = requestData.get("case_type"); String responseString = webClient.postFormData(url, requestData); if (responseString != null) { byte[] responseBytes = responseString.getBytes(StandardCharsets.UTF_8); -// validateCaseTypesInResponse(responseBytes, instanceId, requestCaseTypes, url, requestData); + validateCaseTypesInResponse(responseBytes, instanceId, requestCaseTypes); ByteArrayInputStream responeStream = new ByteArrayInputStream(responseBytes); if (shouldParseIntoCaseSearchStorage(source.useCaseTemplate())) { parseIntoCaseSearchStorage(caseSearchDb, caseSearchSandbox, caseSearchStorage, responeStream, caseSearchIndexTable); - // Validate case types after parsing into storage -// validateCaseTypesInStorage(caseSearchStorage, requestCaseTypes, url, requestData); + validateCaseTypesInStorage(caseSearchStorage, requestCaseTypes, url, requestData); } else { TreeElement root = TreeUtilities.xmlStreamToTreeElement(responeStream, instanceId); if (root != null) { cache.put(cacheKey, root); } - // Validate case types in TreeElement return root; } @@ -183,66 +172,79 @@ private boolean shouldParseIntoCaseSearchStorage(boolean useCaseTemplate) { return useCaseTemplate && storageFactory.getPropertyManager().isIndexCaseSearchResults(); } - private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId, Collection requestCaseTypes, - String url, Multimap requestData) throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException,IOException { + private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId, Collection requestCaseTypes) throws UnfullfilledRequirementsException, XmlPullParserException, InvalidStructureException,IOException { - log.error(String.format("validateCaseTypesInResponse called - requestCaseTypes: %s, responseLength: %d", requestCaseTypes, responseBytes.length)); + StringBuilder sb = new StringBuilder("USH-6370 Checking in 'validateCaseTypesInResponse' "); if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { - log.error("No case_type in request data - skipping validation"); - return; - } - - ByteArrayInputStream responseStream = new ByteArrayInputStream(responseBytes); - TreeElement root = TreeUtilities.xmlStreamToTreeElement(responseStream, instanceId); - - if (root == null) { - log.error("Root element is null - cannot validate"); - return; - } - - log.error(String.format("Root element has %d children", root.getNumChildren())); - - for (int i = 0; i < root.getNumChildren(); i++) { - TreeElement child = root.getChildAt(i); - String attributeValue = child.getAttributeValue(null, "case_type"); -// log.error(String.format("Child %d: name=%s, case_type=%s", i, child.getName(), attributeValue)); - - if (attributeValue != null && !requestCaseTypes.contains(attributeValue)) { - log.error("Response case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + attributeValue + "\n" + - "Url: " + url + " request data: " + requestData.toString()); + sb.append("No case_type in request data - skipping validation"); + } else { + ByteArrayInputStream responseStream = new ByteArrayInputStream(responseBytes); + TreeElement root = TreeUtilities.xmlStreamToTreeElement(responseStream, instanceId); + + if (root == null) { + sb.append("Root element is null - cannot validate"); + return; + } else { + Set caseTypes = new HashSet<>(); + for (int i = 0; i < root.getNumChildren(); i++) { + TreeElement child = root.getChildAt(i); + caseTypes.add(child.getAttributeValue(null, "case_type")); + } + if (caseTypes.stream().anyMatch(type -> !requestCaseTypes.contains(type))) { + sb.append("mismatch requested: ") + .append(requestCaseTypes) + .append(" got: ") + .append(caseTypes); + for (int i = 0; i < root.getNumChildren(); i++) { + TreeElement child = root.getChildAt(i); + String caseType = child.getAttributeValue(null, "case_type"); + String caseId = child.getAttributeValue(null, "case_id"); + sb.append("\n") + .append(caseId) + .append(": ") + .append(caseType); + } + } else { + sb.append("ok"); + } } } + + log.error(sb.toString()); + } private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchStorage, Collection requestCaseTypes, String url, Multimap requestData) { - log.error(String.format("validateCaseTypesInStorage called - requestCaseTypes: %s", requestCaseTypes)); - + + StringBuilder sb = new StringBuilder("USH-6370 Checking in 'validateCaseTypesInStorage' "); if (requestCaseTypes == null || requestCaseTypes.isEmpty()) { - log.error("No case_type in request data - skipping storage validation"); - return; - } - try { - IStorageIterator iterator = caseSearchStorage.iterate(); - int caseCount = 0; - while (iterator.hasMore()) { - Case caseObj = iterator.nextRecord(); - caseCount++; - String caseType = caseObj.getTypeId(); -// log.error(String.format("Storage case %d: id=%s, type=%s", caseCount, caseObj.getCaseId(), caseType)); - - if (caseType != null && !requestCaseTypes.contains(caseType)) { - Exception e = new Exception(); - FormplayerSentry.captureException(e, SentryLevel.WARNING); - log.error("Storage case type did not match request. Expected: " + requestCaseTypes.toString() + " Got: " + caseType + "\n" + - "Url: " + url + " request data: " + requestData.toString()); + sb.append("No case_type in request data - skipping validation"); + } else { + try { + Set caseTypes = new HashSet<>(); + IStorageIterator iterator = caseSearchStorage.iterate(); + int caseCount = 0; + while (iterator.hasMore()) { + Case caseObj = iterator.nextRecord(); + caseCount++; + String caseType = caseObj.getTypeId(); + caseTypes.add(caseType); } + if (caseTypes.stream().anyMatch(type -> !requestCaseTypes.contains(type))) { + sb.append("mismatch requested: ") + .append(requestCaseTypes) + .append(" got: ") + .append(caseTypes); + } else { + sb.append("ok"); + } + } catch (Exception e) { + sb.append("Error validating case types from storage"); } - log.error(String.format("Validated %d cases from storage", caseCount)); - } catch (Exception e) { - log.error("Error validating case types from storage", e); } + log.error(sb.toString()); } private TreeElement getCachedRoot(Cache cache, String cacheKey, String url, boolean skipCache) { From 1d21aa92a5902fbe61c74b677d76cf9845644d5e Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Wed, 28 Jan 2026 09:31:08 -0600 Subject: [PATCH 19/21] Log case search cases on ok --- .../beans/menus/EntityListResponse.java | 66 +++++++++---------- .../formplayer/services/CaseSearchHelper.java | 21 +++--- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java index 4aab3d275..8f5acec43 100644 --- a/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java +++ b/src/main/java/org/commcare/formplayer/beans/menus/EntityListResponse.java @@ -108,18 +108,18 @@ public EntityListResponse(EntityScreen nextScreen) { Detail detail = nextScreen.getShortDetail(); List> entitesForPage = paginateEntities(entityList, detail, casesPerPage, offset); - StringBuilder sb2 = new StringBuilder("USH-6370 Checking at 'paginateEntities' "); - caseTypes = new HashSet<>(); - for (Entity e: entityList) { - caseTypes.add(e.getData()[0].toString()); - } - if (caseTypes.size() > 1) { - sb2.append("mismatch"); - sb2.append("\nExpected all 'Case Type's to be the same. Got: ") - .append(caseTypes); - } else { - sb2.append("ok"); - } +// StringBuilder sb2 = new StringBuilder("USH-6370 Checking at 'paginateEntities' "); +// caseTypes = new HashSet<>(); +// for (Entity e: entityList) { +// caseTypes.add(e.getData()[0].toString()); +// } +// if (caseTypes.size() > 1) { +// sb2.append("mismatch"); +// sb2.append("\nExpected all 'Case Type's to be the same. Got: ") +// .append(caseTypes); +// } else { +// sb2.append("ok"); +// } EvaluationContext ec = nextScreen.getEvalContext(); SessionWrapper session = nextScreen.getSession(); EntityDatum neededDatum = (EntityDatum) session.getNeededDatum(); @@ -127,24 +127,24 @@ public EntityListResponse(EntityScreen nextScreen) { entities = new EntityBean[entityBeans.size()]; entityBeans.toArray(entities); - caseTypes = new HashSet<>(); - for (EntityBean entity : entities) { - caseTypes.add(entity.getData()[0].toString()); - } - StringBuilder sb = new StringBuilder("USH-6370 Checking at 'processEntitiesForCaseList' "); - if (caseTypes.size() > 1) { - sb.append("mismatch"); - sb.append("\nExpected all 'Case Type's to be the same. Got: ") - .append(caseTypes); - for (EntityBean entity : entities) { - sb.append("\n") - .append(entity.getId()) - .append(": ") - .append(entity.getData()[0].toString()); - } - } else { - sb.append("ok"); - } +// caseTypes = new HashSet<>(); +// for (EntityBean entity : entities) { +// caseTypes.add(entity.getData()[0].toString()); +// } +// StringBuilder sb = new StringBuilder("USH-6370 Checking at 'processEntitiesForCaseList' "); +// if (caseTypes.size() > 1) { +// sb.append("mismatch"); +// sb.append("\nExpected all 'Case Type's to be the same. Got: ") +// .append(caseTypes); +// for (EntityBean entity : entities) { +// sb.append("\n") +// .append(entity.getId()) +// .append(": ") +// .append(entity.getData()[0].toString()); +// } +// } else { +// sb.append("ok"); +// } setNoItemsText(getNoItemsTextLocaleString(detail)); setSelectText(getSelectTextLocaleString(detail)); @@ -169,7 +169,7 @@ public EntityListResponse(EntityScreen nextScreen) { QueryScreen queryScreen = nextScreen.getQueryScreen(); if (queryScreen != null) { - sb.append("\nqueryScreen"); +// sb.append("\nqueryScreen"); setQueryKey(queryScreen.getQueryKey()); if (FeatureFlagChecker.isToggleEnabled(TOGGLE_SPLIT_SCREEN_CASE_SEARCH)) { queryResponse = new QueryResponseBean(queryScreen); @@ -177,8 +177,8 @@ public EntityListResponse(EntityScreen nextScreen) { } if (this.headers.length > 0 && this.headers[0].equals("Case Type")) { log.error(sb1.toString()); - log.error(sb2.toString()); - log.error(sb.toString()); +// log.error(sb2.toString()); +// log.error(sb.toString()); } } } diff --git a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java index f45a7a674..15d57f266 100644 --- a/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java +++ b/src/main/java/org/commcare/formplayer/services/CaseSearchHelper.java @@ -196,18 +196,19 @@ private void validateCaseTypesInResponse(byte[] responseBytes, String instanceId .append(requestCaseTypes) .append(" got: ") .append(caseTypes); - for (int i = 0; i < root.getNumChildren(); i++) { - TreeElement child = root.getChildAt(i); - String caseType = child.getAttributeValue(null, "case_type"); - String caseId = child.getAttributeValue(null, "case_id"); - sb.append("\n") - .append(caseId) - .append(": ") - .append(caseType); - } + } else { sb.append("ok"); } + for (int i = 0; i < root.getNumChildren(); i++) { + TreeElement child = root.getChildAt(i); + String caseType = child.getAttributeValue(null, "case_type"); + String caseId = child.getAttributeValue(null, "case_id"); + sb.append("\n") + .append(caseId) + .append(": ") + .append(caseType); + } } } @@ -225,10 +226,8 @@ private void validateCaseTypesInStorage(IStorageUtilityIndexed caseSearchS try { Set caseTypes = new HashSet<>(); IStorageIterator iterator = caseSearchStorage.iterate(); - int caseCount = 0; while (iterator.hasMore()) { Case caseObj = iterator.nextRecord(); - caseCount++; String caseType = caseObj.getTypeId(); caseTypes.add(caseType); } From 9dc80ba7995554d48cdb508072cae6da2a709c88 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Fri, 6 Feb 2026 14:07:48 -0600 Subject: [PATCH 20/21] Log screen sb --- .../org/commcare/formplayer/services/MenuSessionFactory.java | 4 +++- .../formplayer/services/MenuSessionRunnerService.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/services/MenuSessionFactory.java b/src/main/java/org/commcare/formplayer/services/MenuSessionFactory.java index b1d8d8ce6..e0339656e 100644 --- a/src/main/java/org/commcare/formplayer/services/MenuSessionFactory.java +++ b/src/main/java/org/commcare/formplayer/services/MenuSessionFactory.java @@ -119,7 +119,9 @@ public Screen rebuildSessionFromFrame(MenuSession menuSession, CaseSearchHelper } } else if (screen instanceof EntityScreen) { EntityScreen entityScreen = (EntityScreen)screen; - entityScreen.initReferences(menuSession.getSessionWrapper()); + StringBuilder sb = new StringBuilder(); + entityScreen.initReferences(menuSession.getSessionWrapper(), sb); + log.error(sb.toString()); SessionDatum neededDatum = entityScreen.getSession().getNeededDatum(); for (StackFrameStep step : steps) { if (step.getId().equals(neededDatum.getDataId())) { diff --git a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java index 309b23374..74d8cebff 100644 --- a/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java +++ b/src/main/java/org/commcare/formplayer/services/MenuSessionRunnerService.java @@ -396,7 +396,9 @@ private Screen autoAdvanceSession( // Auto select if we have not advanced as part of auto launch // avoiding unnecessary screen init by skipping the original screen if (!sessionAdvanced && iterationCount != 0) { - nextScreen.init(menuSession.getSessionWrapper()); + StringBuilder sb = new StringBuilder(); + nextScreen.init(menuSession.getSessionWrapper(), sb); + log.error(sb.toString()); if (nextScreen.shouldBeSkipped()) { sessionAdvanced = ((EntityScreen)nextScreen).autoSelectEntities( menuSession.getSessionWrapper()); From 0e4ab6e1598c493e9ef30333292511d733920e54 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Mon, 9 Feb 2026 12:17:30 -0600 Subject: [PATCH 21/21] Log case ids on success --- .../formplayer/application/MenuController.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/commcare/formplayer/application/MenuController.java b/src/main/java/org/commcare/formplayer/application/MenuController.java index f6efaccae..e046ce96a 100644 --- a/src/main/java/org/commcare/formplayer/application/MenuController.java +++ b/src/main/java/org/commcare/formplayer/application/MenuController.java @@ -217,16 +217,15 @@ public static void logCaseTypeColumnIfPresent(BaseResponseBean response, String .append(label) .append("'. Got: ") .append(caseTypes); - - for (EntityBean entity : entityListResponse.getEntities()) { - sb.append("\n") - .append(entity.getId()) - .append(": ") - .append(entity.getData()[0].toString()); - } } else { sb.append("ok"); } + for (EntityBean entity : entityListResponse.getEntities()) { + sb.append("\n") + .append(entity.getId()) + .append(": ") + .append(entity.getData()[0].toString()); + } log.error(sb.toString()); } }