Skip to content

Commit

Permalink
Merge pull request #749 from seadowg/entity-updates
Browse files Browse the repository at this point in the history
Additions needed for offline entity updates
  • Loading branch information
lognaturel committed Mar 11, 2024
2 parents cff9f58 + 38d57e8 commit 6a32b4b
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 38 deletions.
7 changes: 6 additions & 1 deletion src/main/java/org/javarosa/entities/Entity.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.javarosa.entities;

import kotlin.Pair;
import org.jetbrains.annotations.Nullable;

import java.util.List;

Expand All @@ -9,12 +10,16 @@ public class Entity {
public final String dataset;
public final List<Pair<String, String>> properties;
public final String id;

@Nullable
public final String label;
public Integer version;

public Entity(String dataset, String id, String label, List<Pair<String, String>> properties) {
public Entity(String dataset, String id, @Nullable String label, Integer version, List<Pair<String, String>> properties) {
this.dataset = dataset;
this.id = id;
this.label = label;
this.version = version;
this.properties = properties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,38 @@ public void processForm(FormEntryModel formEntryModel) {
List<Pair<XPathReference, String>> saveTos = entityFormExtra.getSaveTos();

TreeElement entityElement = EntityFormParser.getEntityElement(mainInstance);
String dataset = EntityFormParser.parseFirstDatasetToCreate(entityElement);
if (dataset != null) {
List<Pair<String, String>> fields = saveTos.stream().map(saveTo -> {
IDataReference reference = saveTo.getFirst();
IAnswerData answerData = mainInstance.resolveReference(reference).getValue();

if (answerData != null) {
return new Pair<>(saveTo.getSecond(), answerData.getDisplayText());
} else {
return new Pair<>(saveTo.getSecond(), "");
}
}).collect(Collectors.toList());

String id = EntityFormParser.parseId(entityElement);
String label = EntityFormParser.parseLabel(entityElement);
formEntryModel.getExtras().put(new Entities(asList(new Entity(dataset, id, label, fields))));
} else {
formEntryModel.getExtras().put(new Entities(emptyList()));
if (entityElement != null) {
EntityFormParser.EntityAction action = EntityFormParser.parseAction(entityElement);
String dataset = EntityFormParser.parseDataset(entityElement);

if (action == EntityFormParser.EntityAction.CREATE) {
Entity entity = createEntity(entityElement, 1, dataset, saveTos, mainInstance);
formEntryModel.getExtras().put(new Entities(asList(entity)));
} else if (action == EntityFormParser.EntityAction.UPDATE){
int baseVersion = EntityFormParser.parseBaseVersion(entityElement);
int newVersion = baseVersion + 1;
Entity entity = createEntity(entityElement, newVersion, dataset, saveTos, mainInstance);
formEntryModel.getExtras().put(new Entities(asList(entity)));
} else {
formEntryModel.getExtras().put(new Entities(emptyList()));
}
}
}

private Entity createEntity(TreeElement entityElement, int version, String dataset, List<Pair<XPathReference, String>> saveTos, FormInstance mainInstance) {
List<Pair<String, String>> fields = saveTos.stream().map(saveTo -> {
IDataReference reference = saveTo.getFirst();
IAnswerData answerData = mainInstance.resolveReference(reference).getValue();

if (answerData != null) {
return new Pair<>(saveTo.getSecond(), answerData.getDisplayText());
} else {
return new Pair<>(saveTo.getSecond(), "");
}
}).collect(Collectors.toList());

String id = EntityFormParser.parseId(entityElement);
String label = EntityFormParser.parseLabel(entityElement);
return new Entity(dataset, id, label, version, fields);
}
}
54 changes: 38 additions & 16 deletions src/main/java/org/javarosa/entities/internal/EntityFormParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,42 @@
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.core.model.instance.TreeElement;
import org.javarosa.xpath.expr.XPathFuncExpr;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class EntityFormParser {

private static final String ENTITIES_NAMESPACE = "http:https://www.opendatakit.org/xforms/entities";

private EntityFormParser() {

}

@Nullable
public static String parseFirstDatasetToCreate(TreeElement entity) {
if (entity != null) {
String create = entity.getAttributeValue(null, "create");

if (create != null) {
if (XPathFuncExpr.boolStr(create)) {
return entity.getAttributeValue(null, "dataset");
}
}
}

return null;
public static String parseDataset(TreeElement entity) {
return entity.getAttributeValue(null, "dataset");
}

@Nullable
public static String parseLabel(TreeElement entity) {
TreeElement labelElement = entity.getFirstChild("label");

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

public static String parseId(TreeElement entity) {
return entity.getAttributeValue("", "id");
}

public static Integer parseBaseVersion(TreeElement entity) {
try {
return Integer.valueOf(entity.getAttributeValue("", "baseVersion"));
} catch (NumberFormatException e) {
return 0;
}
}

@Nullable
public static TreeElement getEntityElement(FormInstance mainInstance) {
TreeElement root = mainInstance.getRoot();
Expand All @@ -53,4 +50,29 @@ public static TreeElement getEntityElement(FormInstance mainInstance) {
return null;
}
}

@Nullable
public static EntityAction parseAction(@NotNull TreeElement entity) {
String create = entity.getAttributeValue(null, "create");
String update = entity.getAttributeValue(null, "update");

if (update != null) {
if (XPathFuncExpr.boolStr(update)) {
return EntityAction.UPDATE;
}
}

if (create != null) {
if (XPathFuncExpr.boolStr(create)) {
return EntityAction.CREATE;
}
}

return null;
}

public enum EntityAction {
CREATE,
UPDATE
}
}
172 changes: 172 additions & 0 deletions src/test/java/org/javarosa/entities/EntitiesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,178 @@ public void fillingFormWithCreate_makesEntityAvailable() throws IOException, XFo
assertThat(entities.get(0).dataset, equalTo("people"));
assertThat(entities.get(0).id, equalTo(scenario.answerOf("/data/meta/entity/@id").getValue()));
assertThat(entities.get(0).label, equalTo("Tom Wambsgans"));
assertThat(entities.get(0).version, equalTo(1));
assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans"))));
}

@Test
public void fillingFormWithUpdate_makesEntityAvailable() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
asList(
new Pair<>("entities", "http:https://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("name"),
t("meta",
t("entity dataset=\"people\" update=\"1\" id=\"123\" baseVersion=\"1\"",
t("label")
)
)
)
),
bind("/data/name").type("string").withAttribute("entities", "saveto", "name"),
bind("/data/meta/entity/@id").type("string"),
bind("/data/meta/entity/label").type("string").calculate("/data/name")
)
),
body(
input("/data/name")
)
));

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

scenario.next();
scenario.answer("Tom Wambsgans");

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("123"));
assertThat(entities.get(0).label, equalTo("Tom Wambsgans"));
assertThat(entities.get(0).version, equalTo(2));
assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans"))));
}

@Test
public void fillingFormWithUpdate_andNoLabel_makesEntityAvailableWithNullLabel() throws IOException, XFormParser.ParseException {
Scenario scenario = Scenario.init("Create entity form", XFormsElement.html(
asList(
new Pair<>("entities", "http:https://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("name"),
t("meta",
t("entity dataset=\"people\" update=\"1\" id=\"123\" baseVersion=\"1\"")
)
)
),
bind("/data/name").type("string").withAttribute("entities", "saveto", "name"),
bind("/data/meta/entity/@id").type("string")
)
),
body(
input("/data/name")
)
));

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

scenario.next();
scenario.answer("Tom Wambsgans");

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("123"));
assertThat(entities.get(0).label, equalTo(null));
assertThat(entities.get(0).version, equalTo(2));
assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans"))));
}

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

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

scenario.next();
scenario.answer("Tom Wambsgans");

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("123"));
assertThat(entities.get(0).label, equalTo("Tom Wambsgans"));
assertThat(entities.get(0).version, equalTo(2));
assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans"))));
}

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

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

scenario.next();
scenario.answer("Tom Wambsgans");

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("123"));
assertThat(entities.get(0).label, equalTo("Tom Wambsgans"));
assertThat(entities.get(0).version, equalTo(1));
assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans"))));
}

Expand Down
Loading

0 comments on commit 6a32b4b

Please sign in to comment.