From 765c0777acb8f3438d45900b848deb001a850eb7 Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Thu, 28 Aug 2025 12:47:42 +0530 Subject: [PATCH 1/6] Adds a model for apps to define credentials --- .../org/commcare/suite/model/Credential.java | 59 +++++++++++++++++++ .../org/commcare/suite/model/Profile.java | 13 ++++ .../java/org/commcare/xml/ProfileParser.java | 27 +++++++++ .../suite/model/test/ProfileTests.java | 11 ++++ src/test/resources/basic_profile.ccpr | 5 ++ 5 files changed, 115 insertions(+) create mode 100644 src/main/java/org/commcare/suite/model/Credential.java diff --git a/src/main/java/org/commcare/suite/model/Credential.java b/src/main/java/org/commcare/suite/model/Credential.java new file mode 100644 index 0000000000..600b55ec7b --- /dev/null +++ b/src/main/java/org/commcare/suite/model/Credential.java @@ -0,0 +1,59 @@ +package org.commcare.suite.model; + +import org.javarosa.core.util.externalizable.DeserializationException; +import org.javarosa.core.util.externalizable.ExtUtil; +import org.javarosa.core.util.externalizable.Externalizable; +import org.javarosa.core.util.externalizable.PrototypeFactory; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Apps use this model to convey the types of credentials it issues + */ +public class Credential implements Externalizable { + + private String level; + private String type; + + /** + * Serialization Only!!! + */ + public Credential() { + } + + public Credential(String level, String type) { + this.level = level; + this.type = type; + } + + @Override + public void readExternal(DataInputStream in, PrototypeFactory pf) + throws IOException, DeserializationException { + level = ExtUtil.readString(in); + type = ExtUtil.readString(in); + } + + @Override + public void writeExternal(DataOutputStream out) throws IOException { + ExtUtil.writeString(out, level); + ExtUtil.writeString(out, type); + } + + public String getLevel() { + return level; + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return "Credential{" + + "level='" + level + '\'' + + ", type='" + type + '\'' + + '}'; + } +} diff --git a/src/main/java/org/commcare/suite/model/Profile.java b/src/main/java/org/commcare/suite/model/Profile.java index 92d535a0b4..42dfa6de5e 100644 --- a/src/main/java/org/commcare/suite/model/Profile.java +++ b/src/main/java/org/commcare/suite/model/Profile.java @@ -48,6 +48,7 @@ public class Profile implements Persistable { * were added for multiple app seating functionality */ private boolean fromOld; + private Vector credentials; @SuppressWarnings("unused") public Profile() { @@ -77,6 +78,7 @@ public Profile(int version, String authRef, String uniqueId, String displayName, properties = new Vector<>(); roots = new Vector<>(); dependencies = new Vector<>(); + credentials = new Vector<>(); featureStatus = new Hashtable<>(); //turn on default features featureStatus.put("users", true); @@ -191,6 +193,14 @@ public void setDependencies(Vector dependencies) { this.dependencies = dependencies; } + public void setCredentials(Vector credentials) { + this.credentials = credentials; + } + + public Vector getCredentials() { + return credentials; + } + /** * A helper method which initializes the properties specified * by this profile definition. @@ -228,6 +238,8 @@ public void readExternal(DataInputStream in, PrototypeFactory pf) buildProfileId = ExtUtil.readString(in); dependencies = (Vector)ExtUtil.read(in, new ExtWrapList(AndroidPackageDependency.class), pf); + credentials = (Vector)ExtUtil.read(in, + new ExtWrapList(Credential.class), pf); } @Override @@ -244,5 +256,6 @@ public void writeExternal(DataOutputStream out) throws IOException { ExtUtil.write(out, new ExtWrapMap(featureStatus)); ExtUtil.writeString(out, buildProfileId); ExtUtil.write(out, new ExtWrapList(dependencies)); + ExtUtil.write(out, new ExtWrapList(credentials)); } } diff --git a/src/main/java/org/commcare/xml/ProfileParser.java b/src/main/java/org/commcare/xml/ProfileParser.java index 54e2183976..6eed7d6975 100644 --- a/src/main/java/org/commcare/xml/ProfileParser.java +++ b/src/main/java/org/commcare/xml/ProfileParser.java @@ -6,6 +6,7 @@ import org.commcare.resources.model.Resource; import org.commcare.resources.model.ResourceTable; import org.commcare.suite.model.AndroidPackageDependency; +import org.commcare.suite.model.Credential; import org.commcare.suite.model.Profile; import org.commcare.util.CommCarePlatform; import org.javarosa.core.reference.RootTranslator; @@ -27,7 +28,11 @@ public class ProfileParser extends ElementParser { private static final String NAME_DEPENDENCIES = "dependencies"; private static final String NAME_ANDROID_PACKAGE = "android_package"; + private static final String NAME_CREDENTIALS = "credentials"; + private static final String NAME_CREDENTIAL = "credential"; private static final String ATTR_ID = "id"; + private static final String ATTR_CREDENTIAL_LEVEL = "level"; + private static final String ATTR_CREDENTIAL_TYPE = "type"; ResourceTable table; String resourceId; @@ -244,6 +249,8 @@ private void parseFeatures(Profile profile) throws XmlPullParserException, IOExc } else if (tag.equals(NAME_DEPENDENCIES)) { profile.setDependencies(parseDependencies()); } else if (tag.equals("sense")) { + }else if (tag.equals(NAME_CREDENTIALS)) { + profile.setCredentials(parseCredentials()); } profile.setFeatureActive(tag, isActive); @@ -251,6 +258,26 @@ private void parseFeatures(Profile profile) throws XmlPullParserException, IOExc } } + private Vector parseCredentials() + throws InvalidStructureException, XmlPullParserException, IOException { + Vector appCredentials = new Vector<>(); + while (nextTagInBlock(NAME_CREDENTIALS)) { + String tag = parser.getName().toLowerCase(); + if (tag.equals(NAME_CREDENTIAL)) { + String level = parser.getAttributeValue(null, ATTR_CREDENTIAL_LEVEL); + String type = parser.getAttributeValue(null, ATTR_CREDENTIAL_TYPE); + if (level == null) { + throw new InvalidStructureException("No level defined for credential"); + } + if (type == null) { + throw new InvalidStructureException("No type defined for credential"); + } + appCredentials.add(new Credential(level, type)); + } + } + return appCredentials; + } + private Vector parseDependencies() throws InvalidStructureException, XmlPullParserException, IOException { Vector appDependencies = new Vector<>(); diff --git a/src/test/java/org/commcare/backend/suite/model/test/ProfileTests.java b/src/test/java/org/commcare/backend/suite/model/test/ProfileTests.java index 3493c724d2..29cb575b8d 100644 --- a/src/test/java/org/commcare/backend/suite/model/test/ProfileTests.java +++ b/src/test/java/org/commcare/backend/suite/model/test/ProfileTests.java @@ -9,6 +9,7 @@ import org.commcare.resources.model.Resource; import org.commcare.resources.model.ResourceTable; import org.commcare.suite.model.AndroidPackageDependency; +import org.commcare.suite.model.Credential; import org.commcare.suite.model.Profile; import org.commcare.test.utilities.PersistableSandbox; import org.commcare.util.engine.CommCareConfigEngine; @@ -92,6 +93,16 @@ public void testDependenciesParse() { assertEquals(Arrays.toString(expectedDependencies),Arrays.toString(p.getDependencies().toArray())); } + @Test + public void testCredentialsParse() { + Profile p = getProfile(BASIC_PROFILE_PATH); + assertTrue(p.isFeatureActive("credentials")); + Credential[] expectedCredentials = new Credential[2]; + expectedCredentials[0] = new Credential("3MON_ACTIVE", "APP_ACTIVITY"); + expectedCredentials[1] = new Credential("6MON_ACTIVE", "APP_ACTIVITY"); + assertEquals(Arrays.toString(expectedCredentials),Arrays.toString(p.getCredentials().toArray())); + } + private void compareProfiles(Profile a, Profile b) { if(!ArrayUtilities.arraysEqual(a.getPropertySetters(), b.getPropertySetters())) { fail("Mismatch of property setters between profiles"); diff --git a/src/test/resources/basic_profile.ccpr b/src/test/resources/basic_profile.ccpr index ff9f0c2dfa..0fd3095017 100644 --- a/src/test/resources/basic_profile.ccpr +++ b/src/test/resources/basic_profile.ccpr @@ -91,6 +91,11 @@ + + + + + From e1947a47710febcbd8635dfc0368b038ac5cec3c Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Mon, 8 Sep 2025 15:33:01 +0530 Subject: [PATCH 2/6] private -> public --- src/main/java/org/commcare/resources/model/ResourceTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/commcare/resources/model/ResourceTable.java b/src/main/java/org/commcare/resources/model/ResourceTable.java index 11a06d761d..38b9e8db15 100644 --- a/src/main/java/org/commcare/resources/model/ResourceTable.java +++ b/src/main/java/org/commcare/resources/model/ResourceTable.java @@ -942,7 +942,7 @@ public void initializeResources(CommCarePlatform platform, boolean isUpgrade) th setMissingResources(missingResources); } - private void attemptResourceInitialization(CommCarePlatform platform, boolean isUpgrade, + public void attemptResourceInitialization(CommCarePlatform platform, boolean isUpgrade, Resource r, Vector missingResources) throws ResourceInitializationException { try { r.getInstaller().initialize(platform, isUpgrade); From f10e49350d8329b178bf416a9d700b18c27012b5 Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Tue, 9 Sep 2025 09:09:11 +0530 Subject: [PATCH 3/6] check for empty values --- src/main/java/org/commcare/xml/ProfileParser.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/xml/ProfileParser.java b/src/main/java/org/commcare/xml/ProfileParser.java index 6eed7d6975..cbc46802ae 100644 --- a/src/main/java/org/commcare/xml/ProfileParser.java +++ b/src/main/java/org/commcare/xml/ProfileParser.java @@ -3,6 +3,7 @@ */ package org.commcare.xml; +import org.commcare.cases.util.StringUtils; import org.commcare.resources.model.Resource; import org.commcare.resources.model.ResourceTable; import org.commcare.suite.model.AndroidPackageDependency; @@ -266,10 +267,10 @@ private Vector parseCredentials() if (tag.equals(NAME_CREDENTIAL)) { String level = parser.getAttributeValue(null, ATTR_CREDENTIAL_LEVEL); String type = parser.getAttributeValue(null, ATTR_CREDENTIAL_TYPE); - if (level == null) { + if (StringUtils.isEmpty(level)) { throw new InvalidStructureException("No level defined for credential"); } - if (type == null) { + if (StringUtils.isEmpty(type)) { throw new InvalidStructureException("No type defined for credential"); } appCredentials.add(new Credential(level, type)); From 44a1942d3f886143404ae321fff5424b46e73c64 Mon Sep 17 00:00:00 2001 From: Martin Riese Date: Tue, 9 Sep 2025 16:16:13 -0500 Subject: [PATCH 4/6] Add reference field to FixtureInitializationException --- .../commcare/core/process/CommCareInstanceInitializer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/commcare/core/process/CommCareInstanceInitializer.java b/src/main/java/org/commcare/core/process/CommCareInstanceInitializer.java index 467708c978..eecbc1b620 100644 --- a/src/main/java/org/commcare/core/process/CommCareInstanceInitializer.java +++ b/src/main/java/org/commcare/core/process/CommCareInstanceInitializer.java @@ -295,10 +295,14 @@ public String getVersionString() { public static class FixtureInitializationException extends RuntimeException { + public final String reference; + public FixtureInitializationException(String lookupReference) { super(Localization.getWithDefault("lookup.table.missing.error", new String[]{lookupReference}, "Unable to find lookup table: " + lookupReference)); + + this.reference = lookupReference; } } } From ffc8dd95ae9e3b3ad171de143aea1e2af2bdf531 Mon Sep 17 00:00:00 2001 From: parthmittal Date: Tue, 24 Jun 2025 16:54:21 +0530 Subject: [PATCH 5/6] -text change in contributing.md --- .github/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/contributing.md b/.github/contributing.md index 4ac0117c46..b9db33bd66 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -21,9 +21,9 @@ To do this - ##### Duplicating A CommCare Android change to Formplayer -1. If you are working on a CommCare Android change, you will want to start by checking out `your_feature_branch` from `master` as the base branch. Make changes on `your_feature_branch` and create your original PR against `master` branch. +1. If you are working on a CommCare Android change, you have to start by checking out `your_feature_branch` from `master` as the base branch. Make changes on `your_feature_branch` and create your original PR against `master` branch. Get it approved and not merge it. -2. Now you will need to duplicate this PR by making another PR against `formplayer`. Make sure the branch for this PR is is merged and is not deleted. Then create the comment `duplicate this PR `. The `ending-commit-id` should be the last non-merge commit in the PR. This should result in a Github Actions workflow duplicating your PR against `formplayer`. Go to the duplicate PR, close and re-open it to run the Github checks against it. +2. Now you will need to duplicate this PR by making another PR against `formplayer`. For this create the comment `duplicate this PR ` of the `your_feature_branch`. The `ending-commit-id` should be the last non-merge commit in the PR. This should result in a Github Actions workflow duplicating your PR against `formplayer`. Go to the duplicate PR, close and re-open it to run the Github checks against it. 3. In order for us to test that your PR against `formplayer` doesn't break anything on Formplayer, we need to run formplayer side tests with your PR. To do this - From 90e10bc6495aaeddcb5c3c898361bc9c1288e1ea Mon Sep 17 00:00:00 2001 From: parthmittal Date: Tue, 24 Jun 2025 17:35:02 +0530 Subject: [PATCH 6/6] -text correction --- .github/contributing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/contributing.md b/.github/contributing.md index b9db33bd66..dad8cb7c7f 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -21,11 +21,12 @@ To do this - ##### Duplicating A CommCare Android change to Formplayer -1. If you are working on a CommCare Android change, you have to start by checking out `your_feature_branch` from `master` as the base branch. Make changes on `your_feature_branch` and create your original PR against `master` branch. Get it approved and not merge it. +1. If you are working on a CommCare Android change, you have to start by checking out `your_feature_branch` from `master` as the base branch. Make changes on `your_feature_branch` and create your original PR against `master` branch. Get it approved and do not merge it. 2. Now you will need to duplicate this PR by making another PR against `formplayer`. For this create the comment `duplicate this PR ` of the `your_feature_branch`. The `ending-commit-id` should be the last non-merge commit in the PR. This should result in a Github Actions workflow duplicating your PR against `formplayer`. Go to the duplicate PR, close and re-open it to run the Github checks against it. +3. After creating the successful duplicate PR we can merge the `your_feature_branch` to the `master` of Commcare. -3. In order for us to test that your PR against `formplayer` doesn't break anything on Formplayer, we need to run formplayer side tests with your PR. +4. In order for us to test that your PR against `formplayer` doesn't break anything on Formplayer, we need to run formplayer side tests with your PR. To do this - - Check out a new branch say `test_cc_1189` from `master` in [Formplayer](https://github.com/dimagi/formplayer) - Point the submodule in `libs/commcare` to your CommCare Core `your_feature_branch_dupe` branch and push your formplayer branch -