diff --git a/android-json-form-wizard/build.gradle b/android-json-form-wizard/build.gradle index c6a14222e..c8e429578 100644 --- a/android-json-form-wizard/build.gradle +++ b/android-json-form-wizard/build.gradle @@ -112,6 +112,9 @@ dependencies { exclude group: 'com.android.support', module: 'support-annotations' exclude group: 'com.android.support', module: 'recyclerview-v7' } + + implementation 'org.smartregister:opensrp-client-utils:0.0.2-SNAPSHOT' + implementation 'org.jeasy:easy-rules-core:3.3.0' implementation 'org.jeasy:easy-rules-mvel:3.3.0' implementation 'joda-time:joda-time:2.10.5' diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/NativeFormLibrary.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/NativeFormLibrary.java new file mode 100644 index 000000000..298edb5cb --- /dev/null +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/NativeFormLibrary.java @@ -0,0 +1,36 @@ +package com.vijay.jsonwizard; + +import android.support.annotation.NonNull; + +import org.smartregister.client.utils.contract.ClientFormContract; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 29-06-2020. + */ +public class NativeFormLibrary { + + private ClientFormContract.Dao clientFormDao; + private static NativeFormLibrary instance; + + @NonNull + public static NativeFormLibrary getInstance() { + if (instance == null) { + instance = new NativeFormLibrary(); + } + + return instance; + } + +/* + public static final boolean init() { + return false; + }*/ + + public ClientFormContract.Dao getClientFormDao() { + return clientFormDao; + } + + public void setClientFormDao(@NonNull ClientFormContract.Dao clientFormDao) { + this.clientFormDao = clientFormDao; + } +} diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/FormConfigurationJsonFormActivity.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/FormConfigurationJsonFormActivity.java new file mode 100644 index 000000000..3af09c003 --- /dev/null +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/FormConfigurationJsonFormActivity.java @@ -0,0 +1,134 @@ +package com.vijay.jsonwizard.activities; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; +import android.support.v7.app.AlertDialog; +import android.widget.Toast; + +import com.vijay.jsonwizard.NativeFormLibrary; +import com.vijay.jsonwizard.R; +import com.vijay.jsonwizard.interfaces.OnFormFetchedCallback; +import com.vijay.jsonwizard.utils.AppExecutors; +import com.vijay.jsonwizard.utils.FormUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; + +import timber.log.Timber; + + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 29-06-2020. + */ +public class FormConfigurationJsonFormActivity extends JsonFormActivity { + + private FormUtils formUtils; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + formUtils = new FormUtils(); + JSONObject jsonObject = getmJSONObject(); + checkIfFormUpdate(jsonObject); + } + + private void checkIfFormUpdate(@NonNull JSONObject formJsonObject) { + if (FormUtils.isFormNew(formJsonObject)) { + showFormVersionUpdateDialog(formJsonObject, getString(R.string.form_update_title), getString(R.string.form_update_message)); + } + } + + public void showFormVersionUpdateDialog(@NonNull JSONObject formJsonObject, @NonNull String title, @NonNull String message) { + final int clientId = FormUtils.getClientFormId(formJsonObject); + new AlertDialog.Builder(this) + .setTitle(title) + .setMessage(message) + .setCancelable(true) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + negateIsNewClientForm(clientId); + dialog.dismiss(); + } + }) + .setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + negateIsNewClientForm(clientId); + } + }) + .show(); + } + + @VisibleForTesting + protected void negateIsNewClientForm(final int clientFormId) { + AppExecutors appExecutors = new AppExecutors(); + + appExecutors.diskIO() + .execute(new Runnable() { + @Override + public void run() { + NativeFormLibrary.getInstance().getClientFormDao() + .setIsNew(false, clientFormId); + } + }); + } + + @Nullable + @Override + public BufferedReader getRules(@NonNull Context context, @NonNull String fileName) throws IOException { + try { + BufferedReader bufferedReader = formUtils.getRulesFromRepository(context, NativeFormLibrary.getInstance().getClientFormDao(), fileName); + if (bufferedReader != null) { + return bufferedReader; + } + } catch (Exception e) { + Timber.e(e); + } + + return super.getRules(context, fileName); + } + + @Nullable + @Override + public JSONObject getSubForm(String formIdentity, String subFormsLocation, Context context, boolean translateSubForm) throws Exception { + JSONObject dbForm = null; + try { + dbForm = formUtils.getSubFormJsonFromRepository(context, NativeFormLibrary.getInstance().getClientFormDao(), formIdentity, subFormsLocation, translateSubForm); + + } catch (JSONException ex) { + Timber.e(ex); + handleFormError(false, formIdentity); + return null; + } + + if (dbForm == null) { + return super.getSubForm(formIdentity, subFormsLocation, context, translateSubForm); + } + + return dbForm; + } + + @Override + public void handleFormError(boolean isRulesFile, @NonNull String formIdentifier) { + formUtils.handleJsonFormOrRulesError(this, NativeFormLibrary.getInstance().getClientFormDao(), isRulesFile, formIdentifier, new OnFormFetchedCallback() { + @Override + public void onFormFetched(@Nullable String form) { + if (form != null) { + Toast.makeText(FormConfigurationJsonFormActivity.this, R.string.form_changed_reopen_to_take_effect, Toast.LENGTH_LONG) + .show(); + } + + FormConfigurationJsonFormActivity.this.finish(); + } + }); + } +} diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java index 5c0498950..b149df98b 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormBaseActivity.java @@ -16,7 +16,6 @@ import com.vijay.jsonwizard.constants.JsonFormConstants; import com.vijay.jsonwizard.domain.Form; import com.vijay.jsonwizard.fragments.JsonFormFragment; -import com.vijay.jsonwizard.interfaces.JsonSubFormAndRulesLoader; import com.vijay.jsonwizard.interfaces.LifeCycleListener; import com.vijay.jsonwizard.interfaces.OnActivityRequestPermissionResultListener; import com.vijay.jsonwizard.interfaces.OnActivityResultListener; @@ -28,6 +27,7 @@ import org.json.JSONException; import org.json.JSONObject; +import org.smartregister.client.utils.contract.ClientFormContract; import java.io.BufferedReader; import java.io.IOException; @@ -44,7 +44,7 @@ import static com.vijay.jsonwizard.utils.NativeFormLangUtils.getTranslatedString; -abstract class JsonFormBaseActivity extends MultiLanguageActivity implements OnFieldsInvalid, JsonSubFormAndRulesLoader { +abstract class JsonFormBaseActivity extends MultiLanguageActivity implements OnFieldsInvalid, ClientFormContract.View { protected static final String TAG = JsonFormActivity.class.getSimpleName(); protected static final String JSON_STATE = "jsonState"; protected static final String FORM_STATE = "formState"; diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java index a252d02a0..591b4bc68 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/constants/JsonFormConstants.java @@ -221,6 +221,9 @@ public class JsonFormConstants { public static final String FORM_NAME = "form_name"; public static final String FORM_CONFIG_LOCATION = "json.form/json.form.config.json"; + public static final String JSON_FILE_EXTENSION = ".json"; + public static final String CLIENT_FORM_ASSET_VERSION = "base version"; + public interface MultiSelectUtils { String IS_HEADER = "isHeader"; String TEXT = "text"; @@ -244,6 +247,9 @@ public static class Properties { public static final String APP_VERSION_NAME = "appVersionName"; public static final String APP_FORM_VERSION = "formVersion"; public static final String DETAILS = "details"; + public static final String IS_NEW = "is_new"; + public static final String CLIENT_FORM_ID = "client_form_id"; + public static final String FORM_VERSION = "form_version"; } public static class JsonFormConstantsUtils { diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonApi.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonApi.java index 7329d31ef..18aafa3f2 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonApi.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonApi.java @@ -9,6 +9,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.smartregister.client.utils.contract.ClientFormContract; import java.util.Collection; import java.util.Map; @@ -16,7 +17,8 @@ /** * Created by vijay on 5/16/15. */ -public interface JsonApi extends JsonSubFormAndRulesLoader { +public interface JsonApi extends ClientFormContract.View { + JSONObject getStep(String stepName); void writeValue(String stepName, String key, String value, String openMrsEntityParent, diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonSubFormAndRulesLoader.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonSubFormAndRulesLoader.java deleted file mode 100644 index ed1d51bc3..000000000 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/JsonSubFormAndRulesLoader.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.vijay.jsonwizard.interfaces; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.IOException; - -/** - * Created by Ephraim Kigamba - nek.eam@gmail.com on 29-04-2020. - */ -public interface JsonSubFormAndRulesLoader { - - @Nullable - JSONObject getSubForm(String formIdentity, String subFormsLocation, Context context, boolean translateSubForm) throws Exception; - - @Nullable - BufferedReader getRules(@NonNull Context context, @NonNull String fileName) throws IOException; - - void handleFormError(boolean isRulesFile, @NonNull String formIdentifier); - - void setVisibleFormErrorAndRollbackDialog(boolean isVisible); - - boolean isVisibleFormErrorAndRollbackDialog(); -} diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/OnFormFetchedCallback.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/OnFormFetchedCallback.java new file mode 100644 index 000000000..f4b1f58f7 --- /dev/null +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/OnFormFetchedCallback.java @@ -0,0 +1,11 @@ +package com.vijay.jsonwizard.interfaces; + +import android.support.annotation.Nullable; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 21-05-2020. + */ +public interface OnFormFetchedCallback { + + void onFormFetched(@Nullable T form); +} diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/RollbackDialogCallback.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/RollbackDialogCallback.java new file mode 100644 index 000000000..88b9b8522 --- /dev/null +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/interfaces/RollbackDialogCallback.java @@ -0,0 +1,15 @@ +package com.vijay.jsonwizard.interfaces; + +import android.support.annotation.NonNull; + +import org.smartregister.client.utils.contract.ClientFormContract; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 21-05-2020. + */ +public interface RollbackDialogCallback { + + void onFormSelected(@NonNull ClientFormContract.Model selectedForm); + + void onCancelClicked(); +} diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/rules/RulesEngineFactory.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/rules/RulesEngineFactory.java index 6a6f681ae..c3df795ff 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/rules/RulesEngineFactory.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/rules/RulesEngineFactory.java @@ -5,7 +5,6 @@ import com.google.gson.Gson; import com.vijay.jsonwizard.constants.JsonFormConstants; -import com.vijay.jsonwizard.interfaces.JsonSubFormAndRulesLoader; import com.vijay.jsonwizard.utils.Utils; import org.jeasy.rules.api.Facts; @@ -19,6 +18,7 @@ import org.jeasy.rules.mvel.MVELRuleFactory; import org.json.JSONArray; import org.json.JSONObject; +import org.smartregister.client.utils.contract.ClientFormContract; import java.io.BufferedReader; import java.io.IOException; @@ -144,8 +144,8 @@ private Rules getRulesFromAsset(String fileName) { if (!ruleMap.containsKey(fileName)) { BufferedReader bufferedReader; boolean loadedFromDb = false; - if (context instanceof JsonSubFormAndRulesLoader) { - bufferedReader = ((JsonSubFormAndRulesLoader) context).getRules(context, fileName); + if (context instanceof ClientFormContract.View) { + bufferedReader = ((ClientFormContract.View) context).getRules(context, fileName); loadedFromDb = true; } else { bufferedReader = new BufferedReader(new InputStreamReader(context.getAssets().open(fileName))); @@ -158,7 +158,7 @@ private Rules getRulesFromAsset(String fileName) { Timber.e(ex); if (loadedFromDb) { - ((JsonSubFormAndRulesLoader) context).handleFormError(true, fileName); + ((ClientFormContract.View) context).handleFormError(true, fileName); } return null; diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormRollbackDialogUtil.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormRollbackDialogUtil.java new file mode 100644 index 000000000..99c65cc66 --- /dev/null +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormRollbackDialogUtil.java @@ -0,0 +1,120 @@ +package com.vijay.jsonwizard.utils; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; +import android.widget.ArrayAdapter; +import android.widget.Toast; + +import com.vijay.jsonwizard.R; +import com.vijay.jsonwizard.constants.JsonFormConstants; +import com.vijay.jsonwizard.interfaces.RollbackDialogCallback; + +import org.smartregister.client.utils.contract.ClientFormContract; + +import java.util.List; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 22-05-2020. + */ +public class FormRollbackDialogUtil { + + public static AlertDialog showAvailableRollbackFormsDialog(@NonNull final Context context + , @NonNull final ClientFormContract.Dao clientFormRepository + , @NonNull final List clientFormList + , @NonNull final ClientFormContract.Model currentClientForm + , final @NonNull RollbackDialogCallback rollbackDialogCallback) { + AlertDialog.Builder builderSingle = new AlertDialog.Builder(context); + builderSingle.setIcon(R.drawable.ic_icon_danger); + builderSingle.setTitle(R.string.rollback_dialog_title); + int selectedItem = -1; + //builderSingle.setMessage("Due to an error on the current form, the form cannot be openned. Kindly select another rollback form to use for the time being."); + + final ArrayAdapter arrayAdapter = new ArrayAdapter(context, android.R.layout.select_dialog_singlechoice); + + int counter = 0; + for (ClientFormContract.Model clientForm : clientFormList) { + if (clientForm.getVersion().equals(currentClientForm.getVersion())) { + selectedItem = counter; + arrayAdapter.add("v" + clientForm.getVersion() + context.getString(R.string.current_corrupted_form)); + } else { + arrayAdapter.add("v" + clientForm.getVersion()); + } + + counter++; + } + + arrayAdapter.add(JsonFormConstants.CLIENT_FORM_ASSET_VERSION); + + builderSingle.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + rollbackDialogCallback.onCancelClicked(); + } + }); + + builderSingle.setSingleChoiceItems(arrayAdapter, selectedItem, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String formVersion = arrayAdapter.getItem(which); + + if (formVersion != null) { + boolean wasClickHandled = selectForm(clientFormRepository, which, formVersion, context, clientFormList, currentClientForm, rollbackDialogCallback); + + if (wasClickHandled) { + dialog.dismiss(); + } + } + } + }); + builderSingle.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (context instanceof ClientFormContract.View) { + ((ClientFormContract.View) context).setVisibleFormErrorAndRollbackDialog(false); + } + } + }); + + + if (context instanceof ClientFormContract.View) { + ((ClientFormContract.View) context).setVisibleFormErrorAndRollbackDialog(true); + } + return builderSingle.show(); + } + + @VisibleForTesting + protected static boolean selectForm(@NonNull ClientFormContract.Dao clientFormRepository, int pos, @NonNull String formVersion, @NonNull Context context, @NonNull List clientFormList, @NonNull ClientFormContract.Model currentClientForm, @NonNull RollbackDialogCallback rollbackDialogCallback) { + if (formVersion.contains(context.getString(R.string.current_corrupted_form))) { + Toast.makeText(context, R.string.cannot_select_corrupted_form_rollback, Toast.LENGTH_LONG).show(); + return false; + } else { + ClientFormContract.Model selectedClientForm; + if (formVersion.equals(JsonFormConstants.CLIENT_FORM_ASSET_VERSION)) { + selectedClientForm = clientFormRepository.createNewClientFormModel(); + selectedClientForm.setVersion(JsonFormConstants.CLIENT_FORM_ASSET_VERSION); + } else { + if (pos >= clientFormList.size()) { + return false; + } + + selectedClientForm = clientFormList.get(pos); + + if (selectedClientForm == null) { + return false; + } else { + selectedClientForm.setActive(true); + clientFormRepository.addOrUpdate(selectedClientForm); + } + } + + currentClientForm.setActive(false); + clientFormRepository.addOrUpdate(currentClientForm); + rollbackDialogCallback.onFormSelected(selectedClientForm); + return true; + } + } +} diff --git a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormUtils.java b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormUtils.java index d28052a18..5565078b0 100644 --- a/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormUtils.java +++ b/android-json-form-wizard/src/main/java/com/vijay/jsonwizard/utils/FormUtils.java @@ -10,6 +10,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.constraint.ConstraintLayout; import android.support.v7.widget.AppCompatEditText; import android.support.v7.widget.AppCompatTextView; @@ -40,21 +41,26 @@ import com.vijay.jsonwizard.interfaces.CommonListener; import com.vijay.jsonwizard.interfaces.GenericDialogInterface; import com.vijay.jsonwizard.interfaces.JsonApi; +import com.vijay.jsonwizard.interfaces.OnFormFetchedCallback; +import com.vijay.jsonwizard.interfaces.RollbackDialogCallback; import com.vijay.jsonwizard.rules.RuleConstant; import com.vijay.jsonwizard.views.CustomTextView; +import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; import org.jeasy.rules.api.Facts; import org.jetbrains.annotations.NotNull; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.smartregister.client.utils.contract.ClientFormContract; import java.io.BufferedReader; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import java.io.StringReader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -1775,4 +1781,253 @@ public String setValues(JSONArray jsonArray, String type) { return value.replaceAll(", $", ""); } + + @Nullable + public JSONObject getFormJsonFromRepositoryOrAssets(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormRepository, @NonNull String formIdentity) { + return getFormJsonFromRepositoryOrAssetsWithOptionalCallback(context, clientFormRepository, formIdentity, null); + } + + public void getFormJsonFromRepositoryOrAssets(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormRepository, @NonNull String formIdentity, @Nullable OnFormFetchedCallback onFormFetchedCallback) { + getFormJsonFromRepositoryOrAssetsWithOptionalCallback(context, clientFormRepository, formIdentity, onFormFetchedCallback); + } + + private JSONObject getFormJsonFromRepositoryOrAssetsWithOptionalCallback(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormRepository, String formIdentity, @Nullable final OnFormFetchedCallback onFormFetchedCallback) { + ClientFormContract.Model clientForm = getClientFormFromRepository(context, clientFormRepository, formIdentity); + + try { + if (clientForm != null) { + Timber.d("============%s form loaded from db============", formIdentity); + + JSONObject formJson = new JSONObject(clientForm.getJson()); + injectFormStatus(formJson, clientForm); + + if (onFormFetchedCallback != null) { + onFormFetchedCallback.onFormFetched(formJson); + return null; + } else { + return formJson; + } + } + } catch (JSONException e) { + Timber.e(e); + + if (onFormFetchedCallback != null) { + handleJsonFormOrRulesError(context, clientFormRepository, false, formIdentity, new OnFormFetchedCallback() { + @Override + public void onFormFetched(@Nullable String form) { + try { + JSONObject jsonObject = form == null ? null : new JSONObject(form); + onFormFetchedCallback.onFormFetched(jsonObject); + } catch (JSONException ex) { + Timber.e(ex); + } + } + }); + } else { + return null; + } + } + + Timber.d("============%s form loaded from Assets=============", formIdentity); + JSONObject jsonObject = getFormJson(context, clientFormRepository, formIdentity); + + if (onFormFetchedCallback != null) { + onFormFetchedCallback.onFormFetched(jsonObject); + return null; + } else { + return jsonObject; + } + } + + public JSONObject getFormJson(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormRepository, String formIdentity) { + if (context != null) { + try { + String locale = context.getResources().getConfiguration().locale.getLanguage(); + locale = locale.equalsIgnoreCase(Locale.ENGLISH.getLanguage()) ? "" : "-" + locale; + + InputStream inputStream; + try { + inputStream = context.getApplicationContext().getAssets() + .open("json.form" + locale + "/" + formIdentity + JsonFormConstants.JSON_FILE_EXTENSION); + } catch (FileNotFoundException e) { + // file for the language not found, defaulting to english language + inputStream = context.getApplicationContext().getAssets() + .open("json.form/" + formIdentity + JsonFormConstants.JSON_FILE_EXTENSION); + } + BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream, CharEncoding.UTF_8)); + String jsonString; + StringBuilder stringBuilder = new StringBuilder(); + + while ((jsonString = reader.readLine()) != null) { + stringBuilder.append(jsonString); + } + inputStream.close(); + + return new JSONObject(stringBuilder.toString()); + } catch (IOException | JSONException e) { + Timber.e(e); + } + } + return null; + } + + private ClientFormContract.Model getClientFormFromRepository(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormRepository, String formIdentity) { + //Check the current locale of the app to load the correct version of the form in the desired language + String locale = context.getResources().getConfiguration().locale.getLanguage(); + String localeFormIdentity = formIdentity; + if (!Locale.ENGLISH.getLanguage().equals(locale)) { + localeFormIdentity = localeFormIdentity + "-" + locale; + } + + ClientFormContract.Model clientForm = clientFormRepository.getActiveClientFormByIdentifier(localeFormIdentity); + + if (clientForm == null) { + String revisedFormName = extractFormNameWithoutExtension(localeFormIdentity); + clientForm = clientFormRepository.getActiveClientFormByIdentifier(revisedFormName); + } + return clientForm; + } + + public void handleJsonFormOrRulesError(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormRepository, @NonNull String formIdentity, @NonNull OnFormFetchedCallback onFormFetchedCallback) { + handleJsonFormOrRulesError(context, clientFormRepository, false, formIdentity, onFormFetchedCallback); + } + + public void handleJsonFormOrRulesError(@NonNull final Context context, @NonNull final ClientFormContract.Dao clientFormRepository, final boolean isRulesFile, @NonNull final String formIdentity, @NonNull final OnFormFetchedCallback onFormFetchedCallback) { + final ClientFormContract.Model clientForm = getClientFormFromRepository(context, clientFormRepository, formIdentity); + List clientForms = clientFormRepository.getClientFormByIdentifier(clientForm.getIdentifier()); + + if (clientForms.size() > 0) { + // Show dialog asking user if they want to rollback to the previous available version X + // if YES, then provide that form instead + // if NO, then continue down + + boolean dialogIsShowing = (context instanceof ClientFormContract.View) && ((ClientFormContract.View) context).isVisibleFormErrorAndRollbackDialog(); + + if (!dialogIsShowing) { + FormRollbackDialogUtil.showAvailableRollbackFormsDialog(context, clientFormRepository, clientForms, clientForm, new RollbackDialogCallback() { + @Override + public void onFormSelected(@NonNull ClientFormContract.Model selectedForm) { + if (selectedForm.getJson() == null && selectedForm.getVersion().equals(JsonFormConstants.CLIENT_FORM_ASSET_VERSION)) { + + if (isRulesFile) { + try { + clientForm.setJson(convertStreamToString(context.getAssets().open(formIdentity))); + } catch (IOException e) { + Timber.e(e); + } + } else { + JSONObject jsonObject = getFormJson(context, clientFormRepository, formIdentity); + + if (jsonObject != null) { + clientForm.setJson(jsonObject.toString()); + } + } + } + + onFormFetchedCallback.onFormFetched(clientForm.getJson()); + } + + @Override + public void onCancelClicked() { + onFormFetchedCallback.onFormFetched(null); + } + }); + } + } + } + + @Nullable + public JSONObject getSubFormJsonFromRepository(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormDao, String formIdentity, String subFormsLocation, boolean translateSubForm) throws JSONException { + String locale = context.getResources().getConfiguration().locale.getLanguage(); + + //Check the current locale of the app to load the correct version of the form in the desired language + String localeFormIdentity = formIdentity; + if (!Locale.ENGLISH.getLanguage().equals(locale)) { + localeFormIdentity = localeFormIdentity + "-" + locale; + } + + String dbFormName = StringUtils.isBlank(subFormsLocation) ? localeFormIdentity : subFormsLocation + "/" + localeFormIdentity; + ClientFormContract.Model clientForm = clientFormDao.getActiveClientFormByIdentifier(dbFormName); + + if (clientForm == null) { + String revisedFormName = extractFormNameWithoutExtension(dbFormName); + clientForm = clientFormDao.getActiveClientFormByIdentifier(revisedFormName); + + if (clientForm == null) { + String finalSubFormsLocation = getSubFormLocation(subFormsLocation); + dbFormName = StringUtils.isBlank(finalSubFormsLocation) ? localeFormIdentity : finalSubFormsLocation + "/" + localeFormIdentity; + clientForm = clientFormDao.getActiveClientFormByIdentifier(dbFormName); + + } + } + + if (clientForm != null) { + Timber.d("============%s form loaded from db============", dbFormName); + String originalJson = clientForm.getJson(); + + if (translateSubForm) { + originalJson = NativeFormLangUtils.getTranslatedString(originalJson, context); + } + + return new JSONObject(originalJson); + } + + return null; + } + + @Nullable + public BufferedReader getRulesFromRepository(@NonNull Context context, @NonNull ClientFormContract.Dao clientFormDao, @NonNull String fileName) { + String locale = context.getResources().getConfiguration().locale.getLanguage(); + + //Check the current locale of the app to load the correct version of the form in the desired language + String localeFormIdentity = fileName; + if (!Locale.ENGLISH.getLanguage().equals(locale)) { + localeFormIdentity = localeFormIdentity + "-" + locale; + } + + ClientFormContract.Model clientForm = clientFormDao.getActiveClientFormByIdentifier(localeFormIdentity); + if (clientForm != null) { + Timber.d("============%s form loaded from db============", localeFormIdentity); + String originalJson = clientForm.getJson(); + + return new BufferedReader(new StringReader(originalJson)); + } + + return null; + } + + @NonNull + protected String extractFormNameWithoutExtension(String localeFormIdentity) { + return localeFormIdentity.endsWith(JsonFormConstants.JSON_FILE_EXTENSION) + ? localeFormIdentity.substring(0, localeFormIdentity.length() - JsonFormConstants.JSON_FILE_EXTENSION.length()) : + localeFormIdentity + JsonFormConstants.JSON_FILE_EXTENSION; + } + + public void injectFormStatus(@NonNull JSONObject jsonObject, @NonNull ClientFormContract.Model clientForm) { + if (clientForm.isNew()) { + try { + jsonObject.put(JsonFormConstants.Properties.IS_NEW, clientForm.isNew()); + jsonObject.put(JsonFormConstants.Properties.CLIENT_FORM_ID, clientForm.getId()); + jsonObject.put(JsonFormConstants.Properties.FORM_VERSION, clientForm.getVersion()); + } catch (JSONException e) { + Timber.e(e); + } + } + } + + public static int getClientFormId(@NonNull JSONObject jsonObject) { + try { + return jsonObject.getInt(JsonFormConstants.Properties.CLIENT_FORM_ID); + } catch (JSONException e) { + Timber.e(e); + return 0; + } + } + + public static boolean isFormNew(@NonNull JSONObject jsonObject) { + return jsonObject.optBoolean(JsonFormConstants.Properties.IS_NEW, false); + } + + } diff --git a/android-json-form-wizard/src/main/res/values/strings.xml b/android-json-form-wizard/src/main/res/values/strings.xml index 9f4119d36..65bb61cb1 100644 --- a/android-json-form-wizard/src/main/res/values/strings.xml +++ b/android-json-form-wizard/src/main/res/values/strings.xml @@ -107,4 +107,11 @@ %1$dy %1$dy %2$dm + Form Update + This form has content updates + Form has been successfully selected! Reopen this form for the changes to take effect + Select a rollback form + (Current Corrupted Form) + You cannot select this form because it\'s corrupted! + \ No newline at end of file diff --git a/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/activities/FormConfigurationJsonFormActivityTest.java b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/activities/FormConfigurationJsonFormActivityTest.java new file mode 100644 index 000000000..014393a3f --- /dev/null +++ b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/activities/FormConfigurationJsonFormActivityTest.java @@ -0,0 +1,200 @@ +package com.vijay.jsonwizard.activities; + + +import android.app.Dialog; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.view.Window; + +import com.vijay.jsonwizard.NativeFormLibrary; +import com.vijay.jsonwizard.R; +import com.vijay.jsonwizard.constants.JsonFormConstants; +import com.vijay.jsonwizard.interfaces.OnFormFetchedCallback; +import com.vijay.jsonwizard.utils.FormUtils; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowDialog; +import org.robolectric.util.ReflectionHelpers; +import org.smartregister.client.utils.contract.ClientFormContract; + +import java.io.BufferedReader; +import java.io.StringReader; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 19-05-2020. + */ +public class FormConfigurationJsonFormActivityTest extends BaseActivityTest { + + private FormConfigurationJsonFormActivity formConfigurationJsonFormActivity; + + @Before + public void setUp() throws Exception { + formConfigurationJsonFormActivity = Robolectric.buildActivity(FormConfigurationJsonFormActivity.class) + .get(); + } + + @Test + public void getRulesShouldReturnCallFormUtils() throws Exception { + FormUtils formUtils = Mockito.mock(FormUtils.class); + + String rulesFileIdentifier = "registration_calculation.yml"; + + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + NativeFormLibrary.getInstance().setClientFormDao(clientFormRepository); + ReflectionHelpers.setField(formConfigurationJsonFormActivity, "formUtils", formUtils); + + Mockito.doReturn(new BufferedReader(new StringReader(""))).when(formUtils).getRulesFromRepository( + Mockito.eq(RuntimeEnvironment.application), + Mockito.eq(clientFormRepository), + Mockito.eq(rulesFileIdentifier)); + + + formConfigurationJsonFormActivity.getRules(RuntimeEnvironment.application, rulesFileIdentifier); + Mockito.verify(formUtils).getRulesFromRepository( + Mockito.eq(RuntimeEnvironment.application), + Mockito.eq(clientFormRepository),Mockito.eq(rulesFileIdentifier)); + } + + @Test + public void getSubFormShouldCallFormUtils() throws Exception { + FormUtils formUtils = Mockito.mock(FormUtils.class); + + String subFormIdentifier = "tuberculosis_test"; + + JSONObject jsonObject = new JSONObject(); + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + NativeFormLibrary.getInstance().setClientFormDao(clientFormRepository); + ReflectionHelpers.setField(formConfigurationJsonFormActivity, "formUtils", formUtils); + + Mockito.doReturn(jsonObject).when(formUtils).getSubFormJsonFromRepository(RuntimeEnvironment.application, clientFormRepository, subFormIdentifier, null, false); + + formConfigurationJsonFormActivity.getSubForm(subFormIdentifier, null, RuntimeEnvironment.application, false); + Mockito.verify(formUtils).getSubFormJsonFromRepository(RuntimeEnvironment.application, clientFormRepository, subFormIdentifier, null, false); + } + + @Test + public void getSubFormShouldCallHandleFormErrorWhenFormReturnedIsCorrupted() throws Exception { + FormUtils formUtils = Mockito.mock(FormUtils.class); + FormConfigurationJsonFormActivity spiedActivity = Mockito.spy(formConfigurationJsonFormActivity); + + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + String subFormIdentifier = "tuberculosis_test.json"; + NativeFormLibrary.getInstance().setClientFormDao(clientFormRepository); + ReflectionHelpers.setField(spiedActivity, "formUtils", formUtils); + + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + throw new JSONException("Test exception"); + } + }).when(formUtils).getSubFormJsonFromRepository(RuntimeEnvironment.application, clientFormRepository, subFormIdentifier, null, false); + + + Assert.assertNull(spiedActivity.getSubForm(subFormIdentifier, null, RuntimeEnvironment.application, false)); + Mockito.verify(spiedActivity).handleFormError(false, subFormIdentifier); + } + + @Test + public void handleFormErrorShouldCallFormUtilsHandleError() { + String formIdentifier = "tuberculosis_test.json"; + FormUtils formUtils = Mockito.mock(FormUtils.class); + //formConfigurationJsonFormActivity.contex + FormConfigurationJsonFormActivity spiedActivity = Mockito.spy(formConfigurationJsonFormActivity); + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + NativeFormLibrary.getInstance().setClientFormDao(clientFormRepository); + ReflectionHelpers.setField(spiedActivity, "formUtils", formUtils); + + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + OnFormFetchedCallback onFormFetchedCallback = invocation.getArgument(4); + onFormFetchedCallback.onFormFetched(""); + return null; + } + }).when(formUtils).handleJsonFormOrRulesError(Mockito.eq(spiedActivity), Mockito.eq(clientFormRepository), Mockito.eq(false), Mockito.eq(formIdentifier), Mockito.any(OnFormFetchedCallback.class)); + + spiedActivity.handleFormError(false, formIdentifier); + Mockito.verify(formUtils).handleJsonFormOrRulesError(Mockito.eq(spiedActivity), Mockito.eq(clientFormRepository), Mockito.eq(false), Mockito.eq(formIdentifier), Mockito.any(OnFormFetchedCallback.class)); + Mockito.verify(spiedActivity).finish(); + } + + @Test + public void showFormVersionUpdateDialogShouldCreateAlertDialogWithTitleAndMessage() throws JSONException { + String title = "This is the title"; + String message = "This is the message"; + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JsonFormConstants.Properties.CLIENT_FORM_ID, 3); + + FormConfigurationJsonFormActivity spiedActivity = Mockito.spy(formConfigurationJsonFormActivity); + spiedActivity.requestWindowFeature(Window.FEATURE_NO_TITLE); + spiedActivity.setTheme(R.style.Theme_AppCompat_Light_Dialog_Alert); + + Mockito.doReturn(jsonObject).when(spiedActivity).getmJSONObject(); + + spiedActivity.showFormVersionUpdateDialog(jsonObject, title, message); + + AlertDialog alertDialog = (AlertDialog) ShadowDialog.getLatestDialog(); + Object alertDialogController = ReflectionHelpers.getField(alertDialog, "mAlert"); + Assert.assertNotNull(alertDialog); + Assert.assertEquals(title, ReflectionHelpers.getField(alertDialogController, "mTitle")); + Assert.assertEquals(message, ReflectionHelpers.getField(alertDialogController, "mMessage")); + } + + + @Test + public void formUpdateAlertDialogShouldCallNegateIsNewClientForm() throws JSONException { + String title = "This is the title"; + String message = "This is the message"; + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JsonFormConstants.Properties.CLIENT_FORM_ID, 3); + + FormConfigurationJsonFormActivity spiedActivity = Mockito.spy(formConfigurationJsonFormActivity); + spiedActivity.requestWindowFeature(Window.FEATURE_NO_TITLE); + spiedActivity.setTheme(R.style.Theme_AppCompat_Light_Dialog_Alert); + + Mockito.doReturn(jsonObject).when(spiedActivity).getmJSONObject(); + + spiedActivity.showFormVersionUpdateDialog(jsonObject, title, message); + AlertDialog alertDialog = (AlertDialog) ShadowDialog.getLatestDialog(); + alertDialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick(); + + Mockito.verify(spiedActivity).negateIsNewClientForm(3); + } + + @Test + public void onCreateShouldCallShowFormVersionUpdateDialog() throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JsonFormConstants.Properties.CLIENT_FORM_ID, 3); + jsonObject.put(JsonFormConstants.Properties.IS_NEW, true); + jsonObject.put(JsonFormConstants.Properties.FORM_VERSION, "0.0.1"); + + FormConfigurationJsonFormActivity spiedActivity = Mockito.spy(formConfigurationJsonFormActivity); + spiedActivity.requestWindowFeature(Window.FEATURE_NO_TITLE); + spiedActivity.setTheme(R.style.Theme_AppCompat_Light_Dialog_Alert); + + Mockito.doReturn(jsonObject).when(spiedActivity).getmJSONObject(); + Mockito.doNothing().when(spiedActivity).init(Mockito.anyString()); + Mockito.doNothing().when(spiedActivity).showFormVersionUpdateDialog(jsonObject, getString(R.string.form_update_title), getString(R.string.form_update_message)); + + Bundle bundle = new Bundle(); + bundle.putString(JsonFormBaseActivity.JSON_STATE, "{}"); + spiedActivity.onCreate(bundle); + + Mockito.verify(spiedActivity).showFormVersionUpdateDialog(jsonObject, getString(R.string.form_update_title), getString(R.string.form_update_message)); + } + + protected String getString(int stringId) { + return RuntimeEnvironment.application.getString(stringId); + } +} \ No newline at end of file diff --git a/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormRollbackDialogUtilTest.java b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormRollbackDialogUtilTest.java new file mode 100644 index 000000000..bff520ed5 --- /dev/null +++ b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormRollbackDialogUtilTest.java @@ -0,0 +1,102 @@ +package com.vijay.jsonwizard.utils; + + +import com.vijay.jsonwizard.BaseTest; +import com.vijay.jsonwizard.R; +import com.vijay.jsonwizard.constants.JsonFormConstants; +import com.vijay.jsonwizard.interfaces.RollbackDialogCallback; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.robolectric.RuntimeEnvironment; +import org.smartregister.client.utils.contract.ClientFormContract; + +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 29-06-2020. + */ +public class FormRollbackDialogUtilTest extends BaseTest { + + @Test + public void selectFormShouldReturnFalseWhenCurrentCorruptedFormIsChosen() { + ClientFormContract.Model clientForm = new TestClientForm(); + + assertFalse(FormRollbackDialogUtil.selectForm(Mockito.mock(ClientFormContract.Dao.class) , 0, "0.0.3" + RuntimeEnvironment.application.getString(R.string.current_corrupted_form) + , RuntimeEnvironment.application, new ArrayList(), clientForm, Mockito.mock(RollbackDialogCallback.class))); + } + + @Test + public void selectFormShouldReturnFalseWhenItemIndexDoesNotExist() { + ClientFormContract.Model clientForm = new TestClientForm(); + + assertFalse(FormRollbackDialogUtil.selectForm(Mockito.mock(ClientFormContract.Dao.class), 2, "0.0.3" + , RuntimeEnvironment.application, new ArrayList(), clientForm, Mockito.mock(RollbackDialogCallback.class))); + } + + @Test + public void selectFormShouldReturnTrueWhenAConfigurableFormIsSelected() { + ClientFormContract.Model highClientFormVersion = new TestClientForm(); + highClientFormVersion.setVersion("0.0.3"); + + ClientFormContract.Model clientForm = new TestClientForm(); + clientForm.setVersion("0.0.2"); + ArrayList clientFormsList = new ArrayList(); + clientFormsList.add(clientForm); + + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + + RollbackDialogCallback rollbackDialogCallback = Mockito.mock(RollbackDialogCallback.class); + assertTrue(FormRollbackDialogUtil.selectForm(clientFormRepository, 0, "0.0.2" + , RuntimeEnvironment.application, clientFormsList, highClientFormVersion, rollbackDialogCallback)); + Mockito.verify(rollbackDialogCallback).onFormSelected(clientForm); + + + ArgumentCaptor updateClientFormArgumentCaptor = ArgumentCaptor.forClass(ClientFormContract.Model.class); + Mockito.verify(clientFormRepository, Mockito.times(2)).addOrUpdate(updateClientFormArgumentCaptor.capture()); + ClientFormContract.Model updatedClientForm1 = updateClientFormArgumentCaptor.getAllValues().get(0); + assertEquals("0.0.2", updatedClientForm1.getVersion()); + assertTrue(updatedClientForm1.isActive()); + + ClientFormContract.Model updatedClientForm2 = updateClientFormArgumentCaptor.getAllValues().get(1); + assertEquals("0.0.3", updatedClientForm2.getVersion()); + assertFalse(updatedClientForm2.isActive()); + + + ArgumentCaptor clientFormArgumentCaptor = ArgumentCaptor.forClass(ClientFormContract.Model.class); + Mockito.verify(rollbackDialogCallback).onFormSelected(clientFormArgumentCaptor.capture()); + ClientFormContract.Model selectedClientForm = clientFormArgumentCaptor.getValue(); + assertEquals("0.0.2", selectedClientForm.getVersion()); + } + + + @Test + public void selectFormShouldReturnTrueWhenBaseFormIsSelected() { + ClientFormContract.Model highClientFormVersion = new TestClientForm(); + highClientFormVersion.setVersion("0.0.3"); + + ClientFormContract.Model clientForm = new TestClientForm(); + clientForm.setVersion("0.0.2"); + ArrayList clientFormsList = new ArrayList(); + clientFormsList.add(highClientFormVersion); + clientFormsList.add(clientForm); + + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + Mockito.doReturn(new TestClientForm()).when(clientFormRepository).createNewClientFormModel(); + + RollbackDialogCallback rollbackDialogCallback = Mockito.mock(RollbackDialogCallback.class); + assertTrue(FormRollbackDialogUtil.selectForm(clientFormRepository,0, "base version" + , RuntimeEnvironment.application, clientFormsList, highClientFormVersion, rollbackDialogCallback)); + + ArgumentCaptor clientFormArgumentCaptor = ArgumentCaptor.forClass(ClientFormContract.Model.class); + Mockito.verify(rollbackDialogCallback).onFormSelected(clientFormArgumentCaptor.capture()); + ClientFormContract.Model selectedClientForm = clientFormArgumentCaptor.getValue(); + assertEquals(JsonFormConstants.CLIENT_FORM_ASSET_VERSION, selectedClientForm.getVersion()); + } + +} \ No newline at end of file diff --git a/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormUtilsTest.java b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormUtilsTest.java index cbcc6ae61..cb60dc614 100644 --- a/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormUtilsTest.java +++ b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/FormUtilsTest.java @@ -1,5 +1,9 @@ package com.vijay.jsonwizard.utils; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; + import com.vijay.jsonwizard.BaseTest; import com.vijay.jsonwizard.constants.JsonFormConstants; import com.vijay.jsonwizard.domain.ExpansionPanelItemModel; @@ -12,12 +16,15 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import org.smartregister.client.utils.contract.ClientFormContract; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import static junit.framework.TestCase.assertEquals; @@ -323,4 +330,111 @@ public void testGetSpinnerValueOpenMRSAttributesShouldCorrectlyExtractOpenMRSAtt assertEquals(jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY_ID), "1107AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); assertEquals(jsonObject.getString(JsonFormConstants.KEY), "user_spinner"); } + + + @Test + public void getFormJsonFromRepositoryOrAssets() throws Exception { + formUtils = new FormUtils(); + + Resources resources = Mockito.mock(Resources.class); + Configuration configuration = Mockito.mock(Configuration.class); + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + ClientFormContract.Model clientForm = new TestClientForm(); + clientForm.setJson("{\"form\":\"Sick Child Referral\",\"count\":\"1\",\"encounter_type\":\" \",\"entity_id\":\"\",\"relational_id\":\"\",\"rules_file\":\"rule/general_neat_referral_form_rules.yml\",\"metadata\":{\"start\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_data_type\":\"start\",\"openmrs_entity_id\":\"163137AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"end\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_data_type\":\"end\",\"openmrs_entity_id\":\"163138AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"today\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"encounter\",\"openmrs_entity_id\":\"encounter_date\"},\"deviceid\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_data_type\":\"deviceid\",\"openmrs_entity_id\":\"163149AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"subscriberid\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_data_type\":\"subscriberid\",\"openmrs_entity_id\":\"163150AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"simserial\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_data_type\":\"simserial\",\"openmrs_entity_id\":\"163151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"phonenumber\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_data_type\":\"phonenumber\",\"openmrs_entity_id\":\"163152AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"encounter_location\":\"\",\"look_up\":{\"entity_id\":\"\",\"value\":\"\"}},\"steps\":[{\"title\":\"Sick child form\",\"fields\":[{\"name\":\"chw_referral_service\",\"type\":\"invisible\",\"properties\":{\"text\":\"Choose referral service\"},\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"09978\",\"openmrs_entity_parent\":\"\"},\"options\":[],\"required_status\":\"yes:Please specify referral service\"},{\"name\":\"problem\",\"type\":\"multi_choice_checkbox\",\"properties\":{\"text\":\"Pick condition/problem associated with the client.\"},\"meta_data\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"163182AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"options\":[{\"name\":\"Fast_breathing_and_difficulty_with_breathing\",\"text\":\"Fast breathing and difficulty with breathing\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"142373AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Umbilical_cord_navel_bleeding\",\"text\":\"Umbilical cord/navel bleeding\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"123844AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Excessive_crying\",\"text\":\"Excessive crying\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"140944AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Convulsions\",\"text\":\"Convulsions\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"113054AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Unable_to_breastfeed_or_swallow\",\"text\":\"Unable to breastfeed or swallow\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"159861AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Neck_stiffness\",\"text\":\"Neck stiffness\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"112721AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Fever\",\"text\":\"Fever\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"140238AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Bloating\",\"text\":\"Bloating\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"147132AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Redness_around_the_umbilical_cord_foul_smelling_discharge_from_the_umbilical_cord\",\"text\":\"Redness around the umbilical cord, foul-smelling discharge from the umbilical cord\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"132407AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Bacterial_conjunctivitis\",\"text\":\"Bacterial conjunctivitis\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"148026AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Severe_anaemia\",\"text\":\"Severe anaemia\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"162044AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Severe_abdominal_pain\",\"text\":\"Severe abdominal pain\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"165271AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Pale_or_jaundiced\",\"text\":\"Pale or jaundiced\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"136443AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Cyanosis_blueness_of_lips\",\"text\":\"Cyanosis (blueness of lips)\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"143050AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Skin_rash_pustules\",\"text\":\"Skin rash / pustules\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"512AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Diarrhea\",\"text\":\"Diarrhea\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"142412AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Vomiting\",\"text\":\"Vomiting\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"122983AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Disabilities\",\"text\":\"Disabilities\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"162558AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Premature_baby\",\"text\":\"Premature baby\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"159908AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Care_of_HIV_exposed_infant\",\"text\":\"Care of HIV-exposed infant\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"164818AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Immunisation\",\"text\":\"Immunisation\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"1914AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Other_symptom\",\"text\":\"Other symptom\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}}],\"required_status\":\"yes:Please specify client's problems\"},{\"name\":\"problem_other\",\"type\":\"text_input_edit_text\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"160632AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"163182AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"properties\":{\"hint\":\"Other symptoms\",\"type\":\"name\"},\"required_status\":\"true:Please specify other symptoms\",\"subjects\":\"problem:map\"},{\"name\":\"service_before_referral\",\"meta_data\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"164378AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"type\":\"multi_choice_checkbox\",\"properties\":{\"text\":\"Pre-referral management given.\"},\"options\":[{\"name\":\"ORS\",\"text\":\"ORS\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"351AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Panadol\",\"text\":\"Panadol\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"70116AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"Other_treatment\",\"text\":\"Other treatment\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}},{\"name\":\"None\",\"text\":\"None\",\"is_exclusive\":true,\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"164369AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"\"}}],\"required_status\":\"Pre-referral management field is required\"},{\"name\":\"service_before_referral_other\",\"type\":\"text_input_edit_text\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"160632AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"openmrs_entity_parent\":\"164378AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"properties\":{\"hint\":\"Other Treatment\",\"type\":\"name\"},\"required_status\":\"true:Please specify other treatment given\",\"subjects\":\"service_before_referral:map\"},{\"name\":\"chw_referral_hf\",\"type\":\"spinner\",\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"chw_referral_hf\",\"openmrs_entity_parent\":\"\"},\"properties\":{\"text\":\"Choose referral facility\",\"searchable\":\"Choose referral facility\"},\"options\":[],\"required_status\":\"yes:Please specify referral facility\"},{\"name\":\"referral_appointment_date\",\"type\":\"datetime_picker\",\"properties\":{\"hint\":\"Please select the appointment date\",\"type\":\"date_picker\",\"display_format\":\"dd/MM/yyyy\"},\"meta_data\":{\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"referral_appointment_date\",\"openmrs_entity_parent\":\"\"},\"required_status\":\"true:Please specify the appointment date\"},{\"name\":\"referral_date\",\"meta_data\":{\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"163181AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"type\":\"hidden\"},{\"name\":\"referral_time\",\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"referral_time\",\"type\":\"hidden\"},{\"name\":\"referral_type\",\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"referral_type\",\"type\":\"hidden\"},{\"name\":\"referral_status\",\"openmrs_entity_parent\":\"\",\"openmrs_entity\":\"concept\",\"openmrs_entity_id\":\"referral_status\",\"type\":\"hidden\"}]}]}"); + + configuration.locale = new Locale("en"); + + Context context = Mockito.spy(RuntimeEnvironment.application); + + Mockito.when(context.getResources()).thenReturn(resources); + Mockito.when(resources.getConfiguration()).thenReturn(configuration); + Mockito.when(clientFormRepository.getActiveClientFormByIdentifier("sick_child_referral_form")).thenReturn(clientForm); + + JSONObject form = formUtils.getFormJsonFromRepositoryOrAssets(context, clientFormRepository, "sick_child_referral_form"); + + Mockito.verify(clientFormRepository).getActiveClientFormByIdentifier("sick_child_referral_form"); + Assert.assertNotNull(form); + } + + @Test + public void extractFormNameWithoutExtensionShouldReturnNameWithoutExtension() { + String expectedAns = "registration_form"; + + Assert.assertEquals(expectedAns, formUtils.extractFormNameWithoutExtension("registration_form.json")); + } + + @Test + public void getRulesFromRepositoryShouldCallRepositoryQueryingClientForm() { + String rulesFileIdentifier = "registration_calculation.yml"; + Context context = Mockito.spy(RuntimeEnvironment.application); + ClientFormContract.Model clientForm = new TestClientForm(); + + clientForm.setJson(""); + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + Mockito.doReturn(clientForm).when(clientFormRepository).getActiveClientFormByIdentifier(Mockito.eq(rulesFileIdentifier)); + + Assert.assertNotNull(formUtils.getRulesFromRepository(context, clientFormRepository, rulesFileIdentifier)); + + Mockito.verify(clientFormRepository).getActiveClientFormByIdentifier(Mockito.eq(rulesFileIdentifier)); + } + + @Test + public void getSubFormFromRepository() throws JSONException { + Context context = Mockito.spy(RuntimeEnvironment.application); + String subFormIdentifier = "some_tests"; + ClientFormContract.Model clientForm = new TestClientForm(); + clientForm.setJson("{}"); + ClientFormContract.Dao clientFormRepository = Mockito.mock(ClientFormContract.Dao.class); + Mockito.doReturn(clientForm).when(clientFormRepository).getActiveClientFormByIdentifier(Mockito.eq("json.form/sub_form/" + subFormIdentifier)); + + JSONObject jsonObject = formUtils.getSubFormJsonFromRepository(context, clientFormRepository, subFormIdentifier, null, false); + + Mockito.verify(clientFormRepository).getActiveClientFormByIdentifier(Mockito.eq(subFormIdentifier)); + Mockito.verify(clientFormRepository).getActiveClientFormByIdentifier(Mockito.eq(subFormIdentifier + ".json")); + Mockito.verify(clientFormRepository).getActiveClientFormByIdentifier(Mockito.eq("json.form/sub_form/" + subFormIdentifier)); + + Assert.assertEquals(0, jsonObject.length()); + } + + @Test + public void injectFormStatusShouldAddClientFormDetailsToJsonObject() throws JSONException { + //TODO: Fix the below line + //ClientFormContract.Model clientForm = new TestClientForm(); + TestClientForm clientForm = new TestClientForm(); + clientForm.setId(3); + clientForm.setNew(true); + clientForm.setVersion("0.0.1"); + JSONObject jsonObject = new JSONObject(); + formUtils.injectFormStatus(jsonObject, clientForm); + + Assert.assertEquals(3, jsonObject.getInt(JsonFormConstants.Properties.CLIENT_FORM_ID)); + Assert.assertTrue(jsonObject.getBoolean(JsonFormConstants.Properties.IS_NEW)); + Assert.assertEquals("0.0.1", jsonObject.getString(JsonFormConstants.Properties.FORM_VERSION)); + } + + @Test + public void getClientFormIdShouldReturnClientFormIdPropertyOnJSONObject() throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JsonFormConstants.Properties.CLIENT_FORM_ID, 3); + Assert.assertEquals(3, FormUtils.getClientFormId(jsonObject)); + } + + @Test + public void getClientFormIdShouldReturn0WhenJSONObjectDoesNotHaveClientFormId() throws JSONException { + JSONObject jsonObject = new JSONObject(); + Assert.assertEquals(0, FormUtils.getClientFormId(jsonObject)); + } + + @Test + public void isFormNewShouldReturnFalseWhenJSONObjectDoesNotHaveIsNewProperty() throws JSONException { + JSONObject jsonObject = new JSONObject(); + Assert.assertFalse(FormUtils.isFormNew(jsonObject)); + } + + @Test + public void isFormNewShouldReturnTrueWhenJSONObjectIsNewPropertyIsTrue() throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JsonFormConstants.Properties.IS_NEW, true); + Assert.assertTrue(FormUtils.isFormNew(jsonObject)); + } } diff --git a/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/TestClientForm.java b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/TestClientForm.java new file mode 100644 index 000000000..ba786b7c2 --- /dev/null +++ b/android-json-form-wizard/src/test/java/com/vijay/jsonwizard/utils/TestClientForm.java @@ -0,0 +1,111 @@ +package com.vijay.jsonwizard.utils; + +import org.smartregister.client.utils.contract.ClientFormContract; + +import java.util.Date; + +/** + * Created by Ephraim Kigamba - nek.eam@gmail.com on 01-07-2020. + */ +public class TestClientForm implements ClientFormContract.Model { + private int id; + + private String version; + + private String identifier; + + private String module; + + private String json; + + private String jurisdiction; + + private String label; + + private boolean active; + + private boolean isNew; + + private Date createdAt; + + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getJson() { + return json; + } + + public void setJson(String json) { + this.json = json; + } + + public String getJurisdiction() { + return jurisdiction; + } + + public void setJurisdiction(String jurisdiction) { + this.jurisdiction = jurisdiction; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public boolean isNew() { + return isNew; + } + + public void setNew(boolean aNew) { + isNew = aNew; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } +} diff --git a/gradle.properties b/gradle.properties index 3ee32ecfd..236db2e40 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.11.0-SNAPSHOT +VERSION_NAME=1.12.0-SNAPSHOT VERSION_CODE=1 GROUP=org.smartregister POM_SETTING_DESCRIPTION=OpenSRP Client Native Form Json Wizard