Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Endpoints for Instance Datums #1312

Merged
merged 16 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions src/cli/java/org/commcare/util/screen/MultiSelectEntityScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.commcare.core.interfaces.VirtualDataInstanceStorage;
import org.commcare.data.xml.VirtualInstances;
import org.commcare.modern.session.SessionWrapper;
import org.commcare.modern.util.Pair;
import org.commcare.session.CommCareSession;
import org.commcare.suite.model.MultiSelectEntityDatum;
import org.commcare.util.FormDataUtil;
Expand Down Expand Up @@ -80,7 +81,7 @@ public boolean autoSelectEntities(SessionWrapper session) {
for (int i = 0; i < selectionSize; i++) {
evaluatedValues[i] = getReturnValueFromSelection(references.elementAt(i));
}
processSelectionIntoInstance(evaluatedValues);
processSelectionIntoInstance(evaluatedValues, getNeededDatumId());
updateSession(session);
return true;
}
Expand Down Expand Up @@ -134,9 +135,18 @@ private void prcessSelectionAsGuid(String guid) throws CommCareSessionException
"Could not make selection with reference id " + guid + " on this screen. " +
" If this error persists please report a bug to CommCareHQ.");
}
validateEntitiesInInstance(cachedInstance);
storageReferenceId = guid;
}

private void validateEntitiesInInstance(ExternalDataInstance instance) throws CommCareSessionException {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is currently an issue with this validation that I am verifying with delivery

AbstractTreeElement root = instance.getRoot();
for (int i = 0; i < root.getNumChildren(); i++) {
String entityVal = root.getChildAt(i).getValue().uncast().getString();
getAndValidateEntityReference(entityVal);
}
}

private String getNeededDatumId() {
return getSession().getNeededDatum().getDataId();
}
Expand All @@ -147,7 +157,7 @@ private void processSelectedReferences(TreeReference[] selectedRefs) {
for (int i = 0; i < selectedRefs.length; i++) {
evaluatedValues[i] = getReturnValueFromSelection(selectedRefs[i]);
}
processSelectionIntoInstance(evaluatedValues);
processSelectionIntoInstance(evaluatedValues, getNeededDatumId());
}
}

Expand All @@ -156,29 +166,28 @@ private void processSelectedValues(String[] selectedValues)
if (selectedValues != null && validateSelectionSize(selectedValues.length)) {
String[] evaluatedValues = new String[selectedValues.length];
for (int i = 0; i < selectedValues.length; i++) {
TreeReference currentReference = getEntityReference(selectedValues[i]);
if (currentReference == null) {
throw new CommCareSessionException(
"Could not select case " + selectedValues[i] + " on this screen. " +
" If this error persists please report a bug to CommCareHQ.");
}
TreeReference currentReference = getAndValidateEntityReference(selectedValues[i]);
evaluatedValues[i] = getReturnValueFromSelection(currentReference);
}
processSelectionIntoInstance(evaluatedValues);
processSelectionIntoInstance(evaluatedValues, getNeededDatumId());
}
}

private void processSelectionIntoInstance(String[] evaluatedValues) {
ExternalDataInstance instance = VirtualInstances.buildSelectedValuesInstance(
getSession().getNeededDatum().getDataId(),
evaluatedValues);
String guid = virtualDataInstanceStorage.write(instance);
storageReferenceId = guid;
private TreeReference getAndValidateEntityReference(String selectedValue) throws CommCareSessionException {
TreeReference currentReference = getEntityReference(selectedValue);
if (currentReference == null) {
throw new CommCareSessionException(
"Could not select case " + selectedValue + " on this screen. " +
" If this error persists please report a bug to CommCareHQ.");
}
return currentReference;
}

// rebuild instance with the source
ExternalDataInstanceSource instanceSource = ExternalDataInstanceSource.buildVirtual(instance,
storageReferenceId);
selectedValuesInstance = instanceSource.toInstance();
private void processSelectionIntoInstance(String[] evaluatedValues, String instanceId) {
Pair<String, ExternalDataInstance> guidAndInstance = VirtualInstances.storeSelectedValuesInInstance(
virtualDataInstanceStorage, evaluatedValues, instanceId);
storageReferenceId = guidAndInstance.first;
selectedValuesInstance = guidAndInstance.second;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public interface RemoteInstanceFetcher {
AbstractTreeElement getExternalRoot(String instanceId, ExternalDataInstanceSource source, String refId)
throws RemoteInstanceException;

VirtualDataInstanceStorage getVirtualDataInstanceStorage();

class RemoteInstanceException extends Exception {

public RemoteInstanceException(String message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import org.commcare.cases.instance.CaseDataInstance;
import org.commcare.cases.instance.CaseInstanceTreeElement;
import org.commcare.cases.instance.LedgerInstanceTreeElement;
import org.commcare.core.interfaces.RemoteInstanceFetcher;
import org.commcare.core.interfaces.UserSandbox;
import org.commcare.core.interfaces.VirtualDataInstanceStorage;
import org.commcare.core.sandbox.SandboxUtils;
import org.commcare.data.xml.VirtualInstances;
import org.commcare.session.CommCareSession;
import org.commcare.modern.session.SessionWrapper;
import org.commcare.session.SessionFrame;
import org.commcare.session.SessionInstanceBuilder;
import org.commcare.suite.model.StackFrameStep;
Expand Down Expand Up @@ -37,7 +39,7 @@
*/
public class CommCareInstanceInitializer extends InstanceInitializationFactory {

protected final CommCareSession session;
protected final SessionWrapper sessionWrapper;
protected CaseInstanceTreeElement casebase;
protected LedgerInstanceTreeElement stockbase;
private final LocalCacheTable<String, TreeElement> fixtureBases = new LocalCacheTable<>();
Expand All @@ -54,16 +56,9 @@ public CommCareInstanceInitializer(UserSandbox sandbox) {
this(null, sandbox, null);
}

public CommCareInstanceInitializer(UserSandbox sandbox, CommCarePlatform platform) {
this(null, sandbox, platform);
}

public CommCareInstanceInitializer(UserSandbox sandbox, CommCareSession session) {
this(session, sandbox, null);
}

public CommCareInstanceInitializer(CommCareSession session, UserSandbox sandbox, CommCarePlatform platform) {
this.session = session;
public CommCareInstanceInitializer(SessionWrapper sessionWrapper, UserSandbox sandbox,
CommCarePlatform platform) {
this.sessionWrapper = sessionWrapper;
this.mSandbox = sandbox;
this.mPlatform = platform;
}
Expand Down Expand Up @@ -92,13 +87,44 @@ public InstanceRoot generateRoot(ExternalDataInstance instance) {
} else if (ref.startsWith(ExternalDataInstance.JR_REMOTE_REFERENCE)) {
return setupExternalDataInstance(instance, ref, SessionFrame.STATE_QUERY_REQUEST);
} else if (ref.startsWith(JR_SELECTED_ENTITIES_REFERENCE)) {
return setupExternalDataInstance(instance, ref, SessionFrame.STATE_MULTIPLE_DATUM_VAL);
return setupSelectedEntitiesInstance(instance, ref);
} else if (ref.startsWith(JR_SEARCH_INPUT_REFERENCE)) {
return setupExternalDataInstance(instance, ref, SessionFrame.STATE_QUERY_REQUEST);
}
return ConcreteInstanceRoot.NULL;
}

private InstanceRoot setupSelectedEntitiesInstance(ExternalDataInstance instance, String ref) {
String stepType = SessionFrame.STATE_MULTIPLE_DATUM_VAL;
InstanceRoot instanceRoot = setupExternalDataInstance(instance, ref, stepType);
if (instanceRoot == ConcreteInstanceRoot.NULL) {
instanceRoot = getExternalDataInstanceSourceByStepValue(instance, stepType);
}
return instanceRoot;
}

// Tries to get instance by looking for the instance with id equal to the datum value in the storage
private InstanceRoot getExternalDataInstanceSourceByStepValue(ExternalDataInstance instance,
String stepType) {
RemoteInstanceFetcher instanceFetcher = sessionWrapper.getRemoteInstanceFetcher();
if (instanceFetcher != null) {
VirtualDataInstanceStorage instanceStorage = instanceFetcher.getVirtualDataInstanceStorage();
for (StackFrameStep step : sessionWrapper.getFrame().getSteps()) {
if (step.getType().equals(stepType)) {
try {
ExternalDataInstance loadedInstance = instanceStorage.read(step.getValue(),
instance.getInstanceId(),
instance.getReference());
return new ConcreteInstanceRoot(loadedInstance.getRoot());
} catch (VirtualInstances.InstanceNotFoundException e) {
// continue looping
}
}
}
}
return ConcreteInstanceRoot.NULL;
}

/**
* Initialises instances with reference to 'selected_cases'
*
Expand Down Expand Up @@ -216,7 +242,7 @@ protected InstanceRoot setupSessionData(ExternalDataInstance instance) {
}
User u = mSandbox.getLoggedInUserUnsafe();
TreeElement root =
SessionInstanceBuilder.getSessionInstance(session.getFrame(), getDeviceId(),
SessionInstanceBuilder.getSessionInstance(sessionWrapper.getFrame(), getDeviceId(),
getVersionString(), getCurrentDrift(), u.getUsername(), u.getUniqueId(),
u.getProperties());
root.setParent(instance.getBase());
Expand All @@ -228,7 +254,7 @@ protected long getCurrentDrift() {
}

protected InstanceRoot getExternalDataInstanceSource(String reference, String stepType) {
for (StackFrameStep step : session.getFrame().getSteps()) {
for (StackFrameStep step : sessionWrapper.getFrame().getSteps()) {
if (step.getType().equals(stepType) && step.hasDataInstanceSource(reference)) {
return step.getDataInstanceSource(reference);
}
Expand All @@ -240,7 +266,7 @@ protected InstanceRoot getExternalDataInstanceSource(String reference, String st
* Required for legacy instance support
*/
protected InstanceRoot getExternalDataInstanceSourceById(String instanceId, String stepType) {
for (StackFrameStep step : session.getFrame().getSteps()) {
for (StackFrameStep step : sessionWrapper.getFrame().getSteps()) {
if (step.getType().equals(stepType)) {
ExternalDataInstanceSource source = step.getDataInstanceSourceById(instanceId);
if (source != null) {
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/commcare/data/xml/VirtualInstances.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

import com.google.common.collect.ImmutableMap;

import org.commcare.core.interfaces.VirtualDataInstanceStorage;
import org.commcare.modern.util.Pair;
import org.javarosa.core.model.instance.ExternalDataInstance;
import org.javarosa.core.model.instance.ExternalDataInstanceSource;
import org.javarosa.core.model.instance.TreeElement;

import java.util.ArrayList;
Expand Down Expand Up @@ -49,6 +52,27 @@ public static ExternalDataInstance buildSelectedValuesInstance(
return new ExternalDataInstance(getSelectedEntitiesReference(instanceId), instanceId, root);
}

/**
* Builds and stores the selected entitied into selected entities instance
*
* @param virtualDataInstanceStorage Instance Storage
* @param selectedValues Values to be stored into instance
* @param instanceId instance id for the new instance
* @return A pair of unique storage id for the instance and the newly generated instance
*/
public static Pair<String, ExternalDataInstance> storeSelectedValuesInInstance(
VirtualDataInstanceStorage virtualDataInstanceStorage, String[] selectedValues, String instanceId) {
ExternalDataInstance instance = VirtualInstances.buildSelectedValuesInstance(
instanceId,
selectedValues);
String guid = virtualDataInstanceStorage.write(instance);

// rebuild instance with the source
ExternalDataInstanceSource instanceSource = ExternalDataInstanceSource.buildVirtual(instance, guid);
ExternalDataInstance selectedValuesInstance = instanceSource.toInstance();
return new Pair<>(guid, selectedValuesInstance);
}


public static String getSelectedEntitiesReference(String referenceId) {
return getInstanceReference(JR_SELECTED_ENTITIES_REFERENCE, referenceId);
Expand Down Expand Up @@ -92,4 +116,18 @@ public static String getReferenceScheme(String reference) {
public static String getInstanceReference(String referenceScheme, String referenceId) {
return referenceScheme.concat("/").concat(referenceId);
}

/**
* Throw when the data instance with the given key doesn't exist in the DB
*/
public static class InstanceNotFoundException extends RuntimeException {

public InstanceNotFoundException(String key, String namespace) {
super(String.format(
"Could not find data instance with ID %s (namespace=%s)." +
"Redirecting to home screen. If this issue persists, please file a bug report.",
key, namespace
));
}
}
}
24 changes: 21 additions & 3 deletions src/main/java/org/commcare/modern/session/SessionWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,33 @@ public class SessionWrapper extends CommCareSession implements SessionWrapperInt
final protected UserSandbox mSandbox;
final protected CommCarePlatform mPlatform;
protected CommCareInstanceInitializer initializer;
protected RemoteInstanceFetcher remoteInstanceFetcher;

public SessionWrapper(CommCareSession session, CommCarePlatform platform, UserSandbox sandbox) {
this(platform, sandbox);
public SessionWrapper(CommCareSession session, CommCarePlatform platform, UserSandbox sandbox,
RemoteInstanceFetcher remoteInstanceFetcher) {
this(platform, sandbox, remoteInstanceFetcher);
this.frame = session.getFrame();
this.setFrameStack(session.getFrameStack());
}


public SessionWrapper(CommCareSession session, CommCarePlatform platform, UserSandbox sandbox) {
this(session, platform, sandbox, null);
}

public SessionWrapper(CommCarePlatform platform, UserSandbox sandbox) {
super(platform);
this.mSandbox = sandbox;
this.mPlatform = platform;
}

public SessionWrapper(CommCarePlatform platform, UserSandbox sandbox, RemoteInstanceFetcher remoteInstanceFetcher) {
super(platform);
this.mSandbox = sandbox;
this.mPlatform = platform;
this.remoteInstanceFetcher = remoteInstanceFetcher;
}

/**
* @return The evaluation context for the current state.
*/
Expand Down Expand Up @@ -72,7 +86,7 @@ public CommCareInstanceInitializer getIIF() {
return initializer;
}

public void prepareExternalSources(RemoteInstanceFetcher remoteInstanceFetcher) throws RemoteInstanceFetcher.RemoteInstanceException {
public void prepareExternalSources() throws RemoteInstanceFetcher.RemoteInstanceException {
for(StackFrameStep step : frame.getSteps()) {
step.initDataInstanceSources(remoteInstanceFetcher);
}
Expand Down Expand Up @@ -101,4 +115,8 @@ public String getNeededData() {
public void stepBack() {
super.stepBack(getEvaluationContext());
}

public RemoteInstanceFetcher getRemoteInstanceFetcher() {
return remoteInstanceFetcher;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ public interface SessionWrapperInterface {
EvaluationContext getRestrictedEvaluationContext(String commandId, Set<String> instancesToInclude);
EvaluationContext getEvaluationContextWithAccumulatedInstances(String commandID, XPathAnalyzable xPathAnalyzable);

void prepareExternalSources(RemoteInstanceFetcher fetcher) throws RemoteInstanceFetcher.RemoteInstanceException;
void prepareExternalSources() throws RemoteInstanceFetcher.RemoteInstanceException;
}
Loading