Skip to content

Commit

Permalink
Handle entities without IDs (#763)
Browse files Browse the repository at this point in the history
* Add failing test for null ID entities

* Don't return entities without an ID when updating

* Fix null ID on create case

* Allow clients to handle null entity IDs

* Correct form name
  • Loading branch information
seadowg committed May 27, 2024
1 parent 9e9e443 commit 9408533
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 16 deletions.
24 changes: 16 additions & 8 deletions src/main/java/org/javarosa/core/model/instance/TreeElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@
*/
package org.javarosa.core.model.instance;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.javarosa.core.model.Constants;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.FormElementStateListener;
Expand Down Expand Up @@ -52,6 +45,13 @@
import org.javarosa.xpath.expr.XPathStringLiteral;
import org.jetbrains.annotations.Nullable;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
* <p>An element of a FormInstance.</p>
*
Expand Down Expand Up @@ -580,11 +580,18 @@ private String getAttributeValue(TreeElement attribute) {
}
}

@Nullable
public String getAttributeValue() {
if ( !isAttribute() ) {
throw new IllegalStateException("this is not an attribute");
}
return getValue().uncast().getString();

IAnswerData value = getValue();
if (value != null) {
return value.uncast().getString();
} else {
return null;
}
}

@Override
Expand All @@ -593,6 +600,7 @@ public TreeElement getAttribute(String namespace, String name) {
}

@Override
@Nullable
public String getAttributeValue(String namespace, String name) {
TreeElement element = getAttribute(namespace,name);
return element == null ? null: getAttributeValue(element);
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/org/javarosa/entities/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ public class Entity {

public final String dataset;
public final List<Pair<String, String>> properties;

@Nullable
public final String id;

@Nullable
public final String label;

public final Integer version;
public final EntityAction action;

public Entity(EntityAction action, String dataset, String id, @Nullable String label, Integer version, List<Pair<String, String>> properties) {
public Entity(EntityAction action, String dataset, @Nullable String id, @Nullable String label, Integer version, List<Pair<String, String>> properties) {
this.dataset = dataset;
this.id = id;
this.label = label;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.javarosa.entities.internal;

import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.core.model.instance.TreeElement;
import org.javarosa.entities.EntityAction;
Expand All @@ -22,12 +23,19 @@ public static String parseLabel(TreeElement entity) {
TreeElement labelElement = entity.getFirstChild("label");

if (labelElement != null) {
return (String) labelElement.getValue().getValue();
IAnswerData labelValue = labelElement.getValue();

if (labelValue != null) {
return (String) labelValue.getValue();
} else {
return null;
}
} else {
return null;
}
}

@Nullable
public static String parseId(TreeElement entity) {
return entity.getAttributeValue("", "id");
}
Expand Down
89 changes: 83 additions & 6 deletions src/test/java/org/javarosa/entities/EntitiesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,47 @@ public void fillingFormWithCreate_makesEntityAvailable() throws IOException, XFo
assertThat(entities.get(0).action, equalTo(EntityAction.CREATE));
}

@Test
public void fillingFormWithCreate_withoutAnId_makesEntityAvailable() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
asList(
new Pair<>("entities", "http://www.opendatakit.org/xforms/entities")
),
head(
title("Create entity form"),
model(asList(new Pair<>("entities:entities-version", "2022.1.1")),
mainInstance(
t("data id=\"create-entity-form\"",
t("id"),
t("name"),
t("meta",
t("entity dataset=\"people\" create=\"1\" id=\"\"",
t("label")
)
)
)
),
bind("/data/id").type("string"),
bind("/data/meta/entity/@id").type("string").calculate("/data/id"),
bind("/data/meta/entity/label").type("string").calculate("/data/name")
)
),
body(
input("/data/id"),
input("/data/name")
)
));

scenario.getFormEntryController().addPostProcessor(new EntityFormFinalizationProcessor());
scenario.finalizeInstance();

List<Entity> entities = scenario.getFormEntryController().getModel().getExtras().get(Entities.class).getEntities();
assertThat(entities.size(), equalTo(1));
assertThat(entities.get(0).dataset, equalTo("people"));
assertThat(entities.get(0).id, equalTo(null));
assertThat(entities.get(0).action, equalTo(EntityAction.CREATE));
}

@Test
public void fillingFormWithUpdate_makesEntityAvailable() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
Expand Down Expand Up @@ -174,7 +215,7 @@ public void fillingFormWithUpdate_makesEntityAvailable() throws IOException, XFo

@Test
public void fillingFormWithUpdate_andNoLabel_makesEntityAvailableWithNullLabel() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
Scenario scenario = Scenario.init("Update entity form", XFormsElement.html(
asList(
new Pair<>("entities", "http://www.opendatakit.org/xforms/entities")
),
Expand Down Expand Up @@ -213,6 +254,42 @@ public void fillingFormWithUpdate_andNoLabel_makesEntityAvailableWithNullLabel()
assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans"))));
}

@Test
public void fillingFormWithUpdate_withNullId_makesEntityAvailable() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
asList(
new Pair<>("entities", "http://www.opendatakit.org/xforms/entities")
),
head(
title("Update entity form"),
model(asList(new Pair<>("entities:entities-version", "2023.1.0")),
mainInstance(
t("data id=\"update-entity-form\"",
t("id"),
t("meta",
t("entity dataset=\"people\" update=\"1\" id=\"\" baseVersion=\"\"")
)
)
),
bind("/data/id").type("string"),
bind("/data/meta/entity/@id").type("string").calculate("/data/id").readonly()
)
),
body(
input("/data/id")
)
));

scenario.getFormEntryController().addPostProcessor(new EntityFormFinalizationProcessor());
scenario.finalizeInstance();

List<Entity> entities = scenario.getFormEntryController().getModel().getExtras().get(Entities.class).getEntities();
assertThat(entities.size(), equalTo(1));
assertThat(entities.get(0).dataset, equalTo("people"));
assertThat(entities.get(0).id, equalTo(null));
assertThat(entities.get(0).action, equalTo(EntityAction.UPDATE));
}

@Test
public void fillingFormWithCreateAndUpdate_makesEntityAvailableAsSecondVersion() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
Expand Down Expand Up @@ -315,7 +392,7 @@ public void fillingFormWithDynamicCreateExpression_conditionallyCreatesEntities(
t("name"),
t("join"),
t("meta",
t("entity dataset=\"members\" create=\"\"")
t("entity dataset=\"members\" create=\"\" id=\"1\"")
)
)
),
Expand Down Expand Up @@ -365,7 +442,7 @@ public void entityFormCanBeSerialized() throws IOException, DeserializationExcep
t("data id=\"create-entity-form\"",
t("name"),
t("meta",
t("entities:entity dataset=\"people\" create=\"1\"")
t("entities:entity dataset=\"people\" create=\"1\" id=\"1\"")
)
)
),
Expand Down Expand Up @@ -405,7 +482,7 @@ public void entitiesNamespaceWorksRegardlessOfName() throws IOException, Deseria
t("data id=\"create-entity-form\"",
t("name"),
t("meta",
t("entity dataset=\"people\" create=\"1\"")
t("entity dataset=\"people\" create=\"1\" id=\"1\"")
)
)
),
Expand Down Expand Up @@ -441,7 +518,7 @@ public void fillingFormWithSelectSaveTo_andWithCreate_savesValuesCorrectlyToEnti
t("data id=\"create-entity-form\"",
t("team"),
t("meta",
t("entity dataset=\"people\" create=\"1\"")
t("entity dataset=\"people\" create=\"1\" id=\"1\"")
)
)
),
Expand Down Expand Up @@ -477,7 +554,7 @@ public void whenSaveToQuestionIsNotAnswered_entityPropertyIsEmptyString() throws
t("data id=\"create-entity-form\"",
t("name"),
t("meta",
t("entity dataset=\"people\" create=\"1\"")
t("entity dataset=\"people\" create=\"1\" id=\"1\"")
)
)
),
Expand Down

0 comments on commit 9408533

Please sign in to comment.