diff --git a/Android/app/src/fda/java/com/harvard/studyappmodule/StudyActivity.java b/Android/app/src/fda/java/com/harvard/studyappmodule/StudyActivity.java index 8a3e9ff372..0561fba156 100644 --- a/Android/app/src/fda/java/com/harvard/studyappmodule/StudyActivity.java +++ b/Android/app/src/fda/java/com/harvard/studyappmodule/StudyActivity.java @@ -258,11 +258,6 @@ public void checkForNotification(Intent intent1) { StudyActivity.this, getString(R.string.enroll), "" + studyListArrayList.get(i).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - StudyActivity.this, - getString(R.string.rejoin), - "" + studyListArrayList.get(i).getSetting().getRejoin()); AppController.getHelperSharedPreference() .writePreference( StudyActivity.this, @@ -303,8 +298,6 @@ public void checkForNotification(Intent intent1) { intent.putExtra("position", "" + i); intent.putExtra( "enroll", "" + studyListArrayList.get(i).getSetting().isEnrolling()); - intent.putExtra( - "rejoin", "" + studyListArrayList.get(i).getSetting().getRejoin()); startActivity(intent); } isStudyAvailable = true; @@ -369,11 +362,6 @@ public void checkForNotification(Intent intent1) { StudyActivity.this, getString(R.string.enroll), "" + studyListArrayList.get(i).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - StudyActivity.this, - getString(R.string.rejoin), - "" + studyListArrayList.get(i).getSetting().getRejoin()); } catch (Exception e) { Logger.log(e); } diff --git a/Android/app/src/fda/res/drawable-560dpi/logo1.png b/Android/app/src/fda/res/drawable-560dpi/logo1.png index 2809f334e7..403b8fd2a1 100644 Binary files a/Android/app/src/fda/res/drawable-560dpi/logo1.png and b/Android/app/src/fda/res/drawable-560dpi/logo1.png differ diff --git a/Android/app/src/fda/res/drawable-560dpi/logo2.png b/Android/app/src/fda/res/drawable-560dpi/logo2.png index e4d06abcb1..e527c6cb97 100644 Binary files a/Android/app/src/fda/res/drawable-560dpi/logo2.png and b/Android/app/src/fda/res/drawable-560dpi/logo2.png differ diff --git a/Android/app/src/fda/res/drawable-xhdpi/logo1.png b/Android/app/src/fda/res/drawable-xhdpi/logo1.png index 4f44a36b99..6866d91561 100644 Binary files a/Android/app/src/fda/res/drawable-xhdpi/logo1.png and b/Android/app/src/fda/res/drawable-xhdpi/logo1.png differ diff --git a/Android/app/src/fda/res/drawable-xhdpi/logo2.png b/Android/app/src/fda/res/drawable-xhdpi/logo2.png index 7e7f139bfa..85fd2f86bf 100644 Binary files a/Android/app/src/fda/res/drawable-xhdpi/logo2.png and b/Android/app/src/fda/res/drawable-xhdpi/logo2.png differ diff --git a/Android/app/src/fda/res/drawable-xxhdpi/logo1.png b/Android/app/src/fda/res/drawable-xxhdpi/logo1.png index 434b3cab39..4ee483cfd3 100644 Binary files a/Android/app/src/fda/res/drawable-xxhdpi/logo1.png and b/Android/app/src/fda/res/drawable-xxhdpi/logo1.png differ diff --git a/Android/app/src/fda/res/drawable-xxhdpi/logo2.png b/Android/app/src/fda/res/drawable-xxhdpi/logo2.png index 58a21ca311..66b1554e39 100644 Binary files a/Android/app/src/fda/res/drawable-xxhdpi/logo2.png and b/Android/app/src/fda/res/drawable-xxhdpi/logo2.png differ diff --git a/Android/app/src/fda/res/drawable-xxxhdpi/logo1.png b/Android/app/src/fda/res/drawable-xxxhdpi/logo1.png index d71a551866..59fdb72d22 100644 Binary files a/Android/app/src/fda/res/drawable-xxxhdpi/logo1.png and b/Android/app/src/fda/res/drawable-xxxhdpi/logo1.png differ diff --git a/Android/app/src/fda/res/drawable-xxxhdpi/logo2.png b/Android/app/src/fda/res/drawable-xxxhdpi/logo2.png index 336c7e3a9f..1c7ffac371 100644 Binary files a/Android/app/src/fda/res/drawable-xxxhdpi/logo2.png and b/Android/app/src/fda/res/drawable-xxxhdpi/logo2.png differ diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/CustomActivitiesDailyDialogClass.java b/Android/app/src/main/java/com/harvard/studyappmodule/CustomActivitiesDailyDialogClass.java index 383c940d35..4f557a0162 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/CustomActivitiesDailyDialogClass.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/CustomActivitiesDailyDialogClass.java @@ -1,6 +1,6 @@ /* * Copyright © 2017-2019 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -29,6 +29,7 @@ import android.widget.RelativeLayout; import android.widget.TextView; import com.harvard.R; +import com.harvard.studyappmodule.surveyscheduler.model.ActivityStatus; import java.util.ArrayList; public class CustomActivitiesDailyDialogClass extends Dialog implements View.OnClickListener { @@ -40,19 +41,23 @@ public class CustomActivitiesDailyDialogClass extends Dialog implements View.OnC private ArrayList scheduledTime; private boolean isClickableItem; private DialogClick dialogClick; + private String status; + private ActivityStatus activityStatus; CustomActivitiesDailyDialogClass( Context context, ArrayList scheduledTime, int selectedTime, boolean isClickableItem, - DialogClick dialogClick) { + DialogClick dialogClick, String status, ActivityStatus activityStatus) { super(context); this.context = context; this.scheduledTime = scheduledTime; this.selectedTime = selectedTime; this.isClickableItem = isClickableItem; this.dialogClick = dialogClick; + this.status = status; + this.activityStatus = activityStatus; } @Override @@ -67,6 +72,13 @@ protected void onCreate(Bundle savedInstanceState) { RelativeLayout closeBtnLayout = (RelativeLayout) findViewById(R.id.mCloseBtnLayout); closeBtnLayout.setOnClickListener(this); LinearLayout l = (LinearLayout) findViewById(R.id.lin_layout_hours); + if (status + .equalsIgnoreCase(SurveyActivitiesFragment.STATUS_CURRENT) + && (activityStatus.getStatus().equalsIgnoreCase(SurveyActivitiesFragment.COMPLETED) + || activityStatus.getStatus().equalsIgnoreCase(SurveyActivitiesFragment.INCOMPLETE) + || activityStatus.getCurrentRunId() == 0)) { + selectedTime++; + } for (int i = 0; i < scheduledTime.size(); i++) { TextView textDynamic = new TextView(getContext()); textDynamic.setLayoutParams( diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/DeleteAccountActivity.java b/Android/app/src/main/java/com/harvard/studyappmodule/DeleteAccountActivity.java index 5ae42c87c0..ba3b9a364e 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/DeleteAccountActivity.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/DeleteAccountActivity.java @@ -63,14 +63,7 @@ public class DeleteAccountActivity extends AppCompatActivity private AppCompatTextView idisagree; private LinearLayout middleLayaout; private static final int DELETE_ACCOUNT_REPSONSECODE = 101; - private ArrayList storeWithdrawalTypeDeleteFlag = new ArrayList<>(); private ArrayList studyIdList = new ArrayList<>(); - private ArrayList studyTitleList = new ArrayList<>(); - private ArrayList withdrawalTypeList = new ArrayList<>(); - private String noData = "nodata"; - private boolean noDataFlag = false; - private int tempPos; - private static final int STUDY_INFO = 10; private DbServiceSubscriber dbServiceSubscriber; private Realm realm; @@ -133,29 +126,9 @@ public void onClick(View view) { new View.OnClickListener() { @Override public void onClick(View view) { - boolean noDataAvailable = false; - if (storeWithdrawalTypeDeleteFlag.size() > 0) { - for (int i = 0; i < storeWithdrawalTypeDeleteFlag.size(); i++) { - if (storeWithdrawalTypeDeleteFlag.get(i).equalsIgnoreCase(noData)) { - Toast.makeText( - DeleteAccountActivity.this, - getResources().getString(R.string.select_option), - Toast.LENGTH_LONG) - .show(); - break; - } - if (i == (storeWithdrawalTypeDeleteFlag.size() - 1)) { - noDataAvailable = true; - } - } - } else { - noDataAvailable = true; - } - if (noDataAvailable) { AppController.getHelperProgressDialog() .showProgress(DeleteAccountActivity.this, "", "", false); deactivateAccount(); - } } }); } @@ -168,174 +141,14 @@ private void checkAndCreateDataList() { for (int i = 0; i < realmStudies.size(); i++) { studyIdList.add(realmStudies.get(i).getStudyId()); } - - for (int i = 0; i < studyIdList.size(); i++) { - // get all study title, studyTitleList - StudyList studyList = dbServiceSubscriber.getStudyTitle(studyIdList.get(i), realm); - String title = null; - if (studyList != null) { - title = studyList.getTitle(); - } - if (title == null || title.equalsIgnoreCase("")) { - studyTitleList.add(noData); - } else { - studyTitleList.add(title); - } - // get all withdawalType, withdrawalTypeList[] - try { - StudyHome studyHome = dbServiceSubscriber.getWithdrawalType(studyIdList.get(i), realm); - if (studyHome == null) { - withdrawalTypeList.add(noData); - } else { - String type = studyHome.getWithdrawalConfig().getType(); - if (type == null || type.equalsIgnoreCase("")) { - withdrawalTypeList.add(noData); - } else { - withdrawalTypeList.add(type); - } - } - } catch (Exception e) { - Logger.log(e); - } - } - checkWithdrawalTypeListContainsNoData(); - if (noDataFlag) { - setListLeaveStudy(); - } } catch (Exception e) { Logger.log(e); } } - private void checkWithdrawalTypeListContainsNoData() { - noDataFlag = false; - for (int i = 0; i < withdrawalTypeList.size(); i++) { - if (withdrawalTypeList.get(i).equalsIgnoreCase(noData)) { - tempPos = i; - // missing details to get eg: withdrawal type - callGetStudyInfoWebservice(); - break; - } - if (i == (withdrawalTypeList.size() - 1)) { - noDataFlag = true; - } - } - } - - private void callGetStudyInfoWebservice() { - AppController.getHelperProgressDialog().showProgress(DeleteAccountActivity.this, "", "", false); - HashMap header = new HashMap<>(); - String url = Urls.STUDY_INFO + "?studyId=" + studyIdList.get(tempPos); - GetUserStudyInfoEvent getUserStudyInfoEvent = new GetUserStudyInfoEvent(); - StudyDatastoreConfigEvent studyDatastoreConfigEvent = - new StudyDatastoreConfigEvent( - "get", - url, - STUDY_INFO, - DeleteAccountActivity.this, - StudyHome.class, - null, - header, - null, - false, - this); - - getUserStudyInfoEvent.setStudyDatastoreConfigEvent(studyDatastoreConfigEvent); - StudyModulePresenter studyModulePresenter = new StudyModulePresenter(); - studyModulePresenter.performGetGateWayStudyInfo(getUserStudyInfoEvent); - } - - private void setListLeaveStudy() { - try { - for (int i = 0; i < withdrawalTypeList.size(); i++) { - final int pos = i; - if (withdrawalTypeList.get(i).equalsIgnoreCase("ask_user")) { - final View child2 = getLayoutInflater().inflate(R.layout.content_delete_account2, null); - final AppCompatTextView mentalHealthSurveyTitle = - (AppCompatTextView) child2.findViewById(R.id.mentalHealthSurveyTitle); - final AppCompatCheckBox mDeleteButton1 = - (AppCompatCheckBox) child2.findViewById(R.id.mDeleteButton1); - final AppCompatCheckBox mRetainButton1 = - (AppCompatCheckBox) child2.findViewById(R.id.mRetainButton1); - // font setting - mentalHealthSurveyTitle.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - mDeleteButton1.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - mRetainButton1.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - // value setting - mentalHealthSurveyTitle.setText(studyTitleList.get(i)); - middleLayaout.addView(child2); - storeWithdrawalTypeDeleteFlag.add(pos, noData); - // click listner - mDeleteButton1.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - mDeleteButton1.setChecked(true); - mRetainButton1.setChecked(false); - storeWithdrawalTypeDeleteFlag.set(pos, "true"); - } - }); - mRetainButton1.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - mRetainButton1.setChecked(true); - mDeleteButton1.setChecked(false); - storeWithdrawalTypeDeleteFlag.set(pos, "false"); - } - }); - } - if (withdrawalTypeList.get(i).equalsIgnoreCase("delete_data")) { - final View child1 = getLayoutInflater().inflate(R.layout.content_delete_account1, null); - final AppCompatTextView mMedicationSurveyTitle = - (AppCompatTextView) child1.findViewById(R.id.mMedicationSurveyTitle); - final AppCompatTextView mMedicationSurveyValue = - (AppCompatTextView) child1.findViewById(R.id.mMedicationSurveyValue); - // font setting - mMedicationSurveyTitle.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - mMedicationSurveyValue.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - // value settings - mMedicationSurveyTitle.setText(studyTitleList.get(i)); - mMedicationSurveyValue.setText(getResources().getString(R.string.response_deleted)); - middleLayaout.addView(child1); - storeWithdrawalTypeDeleteFlag.add(pos, "true"); - } else if (withdrawalTypeList.get(i).equalsIgnoreCase("no_action")) { - final View child1 = getLayoutInflater().inflate(R.layout.content_delete_account1, null); - - final AppCompatTextView mMedicationSurveyTitle = - (AppCompatTextView) child1.findViewById(R.id.mMedicationSurveyTitle); - final AppCompatTextView mMedicationSurveyValue = - (AppCompatTextView) child1.findViewById(R.id.mMedicationSurveyValue); - final RelativeLayout r1 = - (RelativeLayout) child1.findViewById(R.id.activity_delete_account1); - // font setting - mMedicationSurveyTitle.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - mMedicationSurveyValue.setTypeface( - AppController.getTypeface(DeleteAccountActivity.this, "regular")); - // value setting - mMedicationSurveyTitle.setText(studyTitleList.get(i)); - mMedicationSurveyValue.setText(getResources().getString(R.string.response_retained)); - middleLayaout.addView(child1); - storeWithdrawalTypeDeleteFlag.add(pos, "false"); - } - } - } catch (Resources.NotFoundException e) { - Logger.log(e); - } - hrLine.setVisibility(View.GONE); - } - @Override public void asyncResponse(T response, int responseCode) { - if (responseCode != STUDY_INFO) { - AppController.getHelperProgressDialog().dismissDialog(); - } + AppController.getHelperProgressDialog().dismissDialog(); if (responseCode == DELETE_ACCOUNT_REPSONSECODE) { LoginData loginData = (LoginData) response; if (loginData != null) { @@ -366,21 +179,6 @@ public void asyncResponse(T response, int responseCode) { Toast.makeText(DeleteAccountActivity.this, R.string.unable_to_parse, Toast.LENGTH_SHORT) .show(); } - } else if (responseCode == STUDY_INFO) { - if (response != null) { - StudyHome studyHome = (StudyHome) response; - // adding withdrawal type - withdrawalTypeList.set(tempPos, studyHome.getWithdrawalConfig().getType()); - // saving to db - studyHome.setStudyId(studyIdList.get(tempPos)); - - dbServiceSubscriber.saveStudyInfoToDB(this, studyHome); - } - checkWithdrawalTypeListContainsNoData(); - if (noDataFlag) { - AppController.getHelperProgressDialog().dismissDialog(); - setListLeaveStudy(); - } } } @@ -410,12 +208,11 @@ public void deactivateAccount() { for (int i = 0; i < studyIdList.size(); i++) { jsonObject = new JSONObject(); jsonObject.put("studyId", studyIdList.get(i)); - jsonObject.put("delete", storeWithdrawalTypeDeleteFlag.get(i)); jsonArray1.put(jsonObject); } } - obj.put("deleteData", jsonArray1); + obj.put("studyData", jsonArray1); } catch (JSONException e) { Logger.log(e); } @@ -446,9 +243,6 @@ public void asyncResponseFailure(int responseCode, String errormsg, String statu if (statusCode.equalsIgnoreCase("401")) { Toast.makeText(DeleteAccountActivity.this, errormsg, Toast.LENGTH_SHORT).show(); AppController.getHelperSessionExpired(DeleteAccountActivity.this, errormsg); - } else if (responseCode == STUDY_INFO) { - Toast.makeText(DeleteAccountActivity.this, errormsg, Toast.LENGTH_SHORT).show(); - finish(); } else { Toast.makeText(DeleteAccountActivity.this, errormsg, Toast.LENGTH_SHORT).show(); } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/NotificationListAdapter.java b/Android/app/src/main/java/com/harvard/studyappmodule/NotificationListAdapter.java index 2ffa6683fe..6ba6e3f1d8 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/NotificationListAdapter.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/NotificationListAdapter.java @@ -148,11 +148,6 @@ public void onClick(View view) { context, context.getString(R.string.enroll), "" + studyListArrayList.get(i).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - context, - context.getString(R.string.rejoin), - "" + studyListArrayList.get(i).getSetting().getRejoin()); AppController.getHelperSharedPreference() .writePreference( context, @@ -198,8 +193,6 @@ public void onClick(View view) { intent.putExtra( "enroll", "" + studyListArrayList.get(i).getSetting().isEnrolling()); - intent.putExtra( - "rejoin", "" + studyListArrayList.get(i).getSetting().getRejoin()); context.startActivity(intent); } isStudyAvailable = true; @@ -272,11 +265,6 @@ public void onClick(View view) { context, context.getString(R.string.enroll), "" + studyListArrayList.get(i).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - context, - context.getString(R.string.rejoin), - "" + studyListArrayList.get(i).getSetting().getRejoin()); } catch (Exception e) { Logger.log(e); } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/ResourcesListAdapter.java b/Android/app/src/main/java/com/harvard/studyappmodule/ResourcesListAdapter.java index eb2ceb5693..67863390a5 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/ResourcesListAdapter.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/ResourcesListAdapter.java @@ -1,6 +1,6 @@ /* * Copyright © 2017-2019 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -136,7 +136,6 @@ public void onClick(View view) { intent.putExtra("studyStatus", ((SurveyActivity) context).getStudyStatus()); intent.putExtra("position", "" + ((SurveyActivity) context).getPosition()); intent.putExtra("enroll", "" + ((SurveyActivity) context).getTitle1()); - intent.putExtra("rejoin", "" + ((SurveyActivity) context).getTitle1()); intent.putExtra("about_this_study", true); (context).startActivity(intent); @@ -157,22 +156,31 @@ public void onClick(View view) { .getTitle() .equalsIgnoreCase(view.getResources().getString(R.string.leave_study))) { - String message = ((SurveyResourcesFragment) fragment).getLeaveStudyMessage(); + String message; + if (items + .get(i) + .getTitle() + .equalsIgnoreCase(context.getResources().getString(R.string.leave_study)) + && AppConfig.AppType.equalsIgnoreCase(context.getString(R.string.app_standalone))) { + message = context.getString(R.string.leaveStudyDeleteAccount); + } else { + message = context.getString(R.string.leaveStudy); + } AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.MyAlertDialogStyle); builder.setTitle(context.getResources().getString(R.string.leave_study) + "?"); builder.setMessage(message); builder.setPositiveButton( - context.getResources().getString(R.string.proceed_caps), + context.getResources().getString(R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - typeUserShowDialog(); + ((SurveyResourcesFragment) fragment).responseServerWithdrawFromStudy(); } }); builder.setNegativeButton( - context.getResources().getString(R.string.cancel_caps), + context.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -188,70 +196,4 @@ public void onClick(DialogInterface dialog, int which) { Logger.log(e); } } - - private void typeUserShowDialog() { - try { - String withdrawalType = ((SurveyResourcesFragment) fragment).getType(); - switch (withdrawalType) { - case "ask_user": - showDialog(3); - break; - case "delete_data": - showDialog(2); - - break; - case "no_action": - showDialog(1); - break; - } - } catch (Exception e) { - Logger.log(e); - } - } - - private void showDialog(int count) { - AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.MyAlertDialogStyle); - // withdrawalType ask_user - if (count == 3) { - builder.setMessage( - context.getResources().getString(R.string.leave_study_retained_or_deleted_message)); - - builder.setPositiveButton( - context.getResources().getString(R.string.retain_my_data_caps), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ((SurveyResourcesFragment) fragment).responseServerWithdrawFromStudy("false"); - } - }); - - builder.setNeutralButton( - context.getResources().getString(R.string.cancel_caps), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); - - builder.setNegativeButton( - context.getResources().getString(R.string.delete_my_data_caps), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ((SurveyResourcesFragment) fragment).responseServerWithdrawFromStudy("true"); - dialog.cancel(); - } - }); - - AlertDialog diag = builder.create(); - diag.show(); - } else if (count == 2) { - // withdrawalType delete_data - ((SurveyResourcesFragment) fragment).responseServerWithdrawFromStudy("true"); - } else if (count == 1) { - // withdrawalType no_action - ((SurveyResourcesFragment) fragment).responseServerWithdrawFromStudy("false"); - } - } } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneActivity.java b/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneActivity.java index 42066b468e..80aa05fef7 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneActivity.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneActivity.java @@ -291,11 +291,6 @@ public void asyncResponse(T response, int responseCode) { StandaloneActivity.this, getString(R.string.enroll), "" + studyListArrayList.get(0).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - StandaloneActivity.this, - getString(R.string.rejoin), - "" + studyListArrayList.get(0).getSetting().getRejoin()); AppController.getHelperSharedPreference() .writePreference( StandaloneActivity.this, @@ -333,8 +328,6 @@ public void asyncResponse(T response, int responseCode) { intent.putExtra("position", "0"); intent.putExtra( "enroll", "" + studyListArrayList.get(j).getSetting().isEnrolling()); - intent.putExtra( - "rejoin", "" + studyListArrayList.get(j).getSetting().getRejoin()); startActivity(intent); finish(); } @@ -354,7 +347,6 @@ public void asyncResponse(T response, int responseCode) { intent.putExtra("studyStatus", studyListArrayList.get(0).getStudyStatus()); intent.putExtra("position", "0"); intent.putExtra("enroll", "" + studyListArrayList.get(0).getSetting().isEnrolling()); - intent.putExtra("rejoin", "" + studyListArrayList.get(0).getSetting().getRejoin()); startActivity(intent); finish(); } @@ -489,7 +481,6 @@ public void asyncResponseFailure(int responseCode, String errormsg, String statu intent.putExtra("studyStatus", studyListArrayList.get(0).getStudyStatus()); intent.putExtra("position", "0"); intent.putExtra("enroll", "" + studyListArrayList.get(0).getSetting().isEnrolling()); - intent.putExtra("rejoin", "" + studyListArrayList.get(0).getSetting().getRejoin()); startActivity(intent); finish(); } @@ -555,11 +546,6 @@ public void checkForNotification(Intent intent1) { StandaloneActivity.this, getString(R.string.enroll), "" + studyListArrayList.get(i).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - StandaloneActivity.this, - getString(R.string.rejoin), - "" + studyListArrayList.get(i).getSetting().getRejoin()); AppController.getHelperSharedPreference() .writePreference( StandaloneActivity.this, @@ -608,8 +594,6 @@ public void checkForNotification(Intent intent1) { intent.putExtra("position", "" + i); intent.putExtra( "enroll", "" + studyListArrayList.get(i).getSetting().isEnrolling()); - intent.putExtra( - "rejoin", "" + studyListArrayList.get(i).getSetting().getRejoin()); startActivity(intent); finish(); } @@ -666,11 +650,6 @@ public void checkForNotification(Intent intent1) { StandaloneActivity.this, getString(R.string.enroll), "" + studyListArrayList.get(i).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - StandaloneActivity.this, - getString(R.string.rejoin), - "" + studyListArrayList.get(i).getSetting().getRejoin()); } catch (Exception e) { Logger.log(e); } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneStudyInfoActivity.java b/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneStudyInfoActivity.java index ca9489f582..6bdbd64819 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneStudyInfoActivity.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/StandaloneStudyInfoActivity.java @@ -355,11 +355,6 @@ public void asyncResponse(T response, int responseCode) { StandaloneStudyInfoActivity.this, getString(R.string.enroll), "" + study.getStudies().get(0).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - StandaloneStudyInfoActivity.this, - getString(R.string.rejoin), - "" + study.getStudies().get(0).getSetting().getRejoin()); AppController.getHelperSharedPreference() .writePreference( StandaloneStudyInfoActivity.this, @@ -373,10 +368,6 @@ public void asyncResponse(T response, int responseCode) { Toast.makeText(getApplication(), R.string.study_no_enroll, Toast.LENGTH_SHORT).show(); } else if (studyList.getStatus().equalsIgnoreCase(StudyFragment.PAUSED)) { Toast.makeText(getApplication(), R.string.study_paused, Toast.LENGTH_SHORT).show(); - } else if (!studyList.getSetting().getRejoin() - && studyList.getStudyStatus().equalsIgnoreCase(StudyFragment.WITHDRAWN)) { - Toast.makeText(getApplication(), R.string.cannot_rejoin_study, Toast.LENGTH_SHORT) - .show(); } else { new CallConsentMetaData(false).execute(); } @@ -582,9 +573,6 @@ private void joinStudy() { Toast.makeText(getApplication(), R.string.study_no_enroll, Toast.LENGTH_SHORT).show(); } else if (study.getStudies().get(0).getStatus().equalsIgnoreCase(StudyFragment.PAUSED)) { Toast.makeText(getApplication(), R.string.study_paused, Toast.LENGTH_SHORT).show(); - } else if (!study.getStudies().get(0).getSetting().getRejoin() - && study.getStudies().get(0).getStudyStatus().equalsIgnoreCase(StudyFragment.WITHDRAWN)) { - Toast.makeText(getApplication(), R.string.cannot_rejoin_study, Toast.LENGTH_SHORT).show(); } else { if (eligibilityConsent.getEligibility().getType().equalsIgnoreCase("token")) { Intent intent = diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/StudyInfoActivity.java b/Android/app/src/main/java/com/harvard/studyappmodule/StudyInfoActivity.java index 41952a5284..d79b947bfb 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/StudyInfoActivity.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/StudyInfoActivity.java @@ -95,7 +95,6 @@ public class StudyInfoActivity extends AppCompatActivity implements ApiCall.OnAs private String position = ""; private String title = ""; private String enroll = ""; - private String rejoin = ""; private AppCompatTextView joinButton; private StudyHome studyHome; private ConsentDocumentData consentDocumentData; @@ -130,7 +129,6 @@ protected void onCreate(Bundle savedInstanceState) { position = getIntent().getStringExtra("position"); title = getIntent().getStringExtra("title"); enroll = getIntent().getStringExtra("enroll"); - rejoin = getIntent().getStringExtra("rejoin"); aboutThisStudy = getIntent().getBooleanExtra("about_this_study", false); } catch (Exception e) { Logger.log(e); @@ -217,10 +215,6 @@ StudyInfoActivity.this, getResources().getString(R.string.userid), "") StudyInfoActivity.this, "login_studyinfo_enroll", getIntent().getStringExtra("enroll")); - SharedPreferenceHelper.writePreference( - StudyInfoActivity.this, - "login_studyinfo_rejoin", - getIntent().getStringExtra("rejoin")); SharedPreferenceHelper.writePreference( StudyInfoActivity.this, "login_studyinfo_about_this_study", @@ -305,9 +299,6 @@ private void joinStudy() { Toast.makeText(getApplication(), R.string.study_no_enroll, Toast.LENGTH_SHORT).show(); } else if (status.equalsIgnoreCase(StudyFragment.PAUSED)) { Toast.makeText(getApplication(), R.string.study_paused, Toast.LENGTH_SHORT).show(); - } else if (rejoin.equalsIgnoreCase("false") - && studyStatus.equalsIgnoreCase(StudyFragment.WITHDRAWN)) { - Toast.makeText(getApplication(), R.string.cannot_rejoin_study, Toast.LENGTH_SHORT).show(); } else { if (eligibilityConsent.getEligibility().getType().equalsIgnoreCase("token")) { Intent intent = new Intent(StudyInfoActivity.this, EligibilityEnrollmentActivity.class); @@ -666,9 +657,6 @@ public void asyncResponse(T response, int responseCode) { Toast.makeText(getApplication(), R.string.study_no_enroll, Toast.LENGTH_SHORT).show(); } else if (studyList.getStatus().equalsIgnoreCase(StudyFragment.PAUSED)) { Toast.makeText(getApplication(), R.string.study_paused, Toast.LENGTH_SHORT).show(); - } else if (!studyList.getSetting().getRejoin() - && studyList.getStudyStatus().equalsIgnoreCase(StudyFragment.WITHDRAWN)) { - Toast.makeText(getApplication(), R.string.cannot_rejoin_study, Toast.LENGTH_SHORT).show(); } else { new CallConsentMetaData(false).execute(); } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/StudyListAdapter.java b/Android/app/src/main/java/com/harvard/studyappmodule/StudyListAdapter.java index e6390a7d09..9453408962 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/StudyListAdapter.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/StudyListAdapter.java @@ -315,11 +315,6 @@ public void run() { context, context.getString(R.string.enroll), "" + items.get(holder.getAdapterPosition()).getSetting().isEnrolling()); - AppController.getHelperSharedPreference() - .writePreference( - context, - context.getString(R.string.rejoin), - "" + items.get(holder.getAdapterPosition()).getSetting().getRejoin()); AppController.getHelperSharedPreference() .writePreference( context, @@ -356,8 +351,6 @@ public void run() { intent.putExtra( "enroll", "" + items.get(holder.getAdapterPosition()).getSetting().isEnrolling()); - intent.putExtra( - "rejoin", "" + items.get(holder.getAdapterPosition()).getSetting().getRejoin()); ((StudyActivity) context).startActivityForResult(intent, 100); } } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesFragment.java b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesFragment.java index 9af2c2c827..c6f7cc9fe4 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesFragment.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesFragment.java @@ -286,7 +286,7 @@ public void onClick(View view) { mScheduledTime.add(context.getResources().getString(R.string.tasks1)); CustomActivitiesDailyDialogClass c = new CustomActivitiesDailyDialogClass( - context, mScheduledTime, filterPos, true, SurveyActivitiesFragment.this); + context, mScheduledTime, filterPos, true, SurveyActivitiesFragment.this, status.get(filterPos), currentRunStatusForActivities.get(filterPos)); c.show(); } }); diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesListAdapter.java b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesListAdapter.java index 4203fefee0..2352a4f8e3 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesListAdapter.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivitiesListAdapter.java @@ -89,7 +89,8 @@ public int getItemCount() { } @Override - public void clicked(int positon) {} + public void clicked(int positon) { + } class Holder extends RecyclerView.ViewHolder { final RelativeLayout stateLayout; @@ -472,23 +473,39 @@ public void onBindViewHolder(final Holder holder, final int position) { .toString() .split("\\.")[0]); } - pos = - checkCurrentTimeInBetweenDates( - items - .get(position) - .getFrequency() - .getRuns() - .get(i) - .getStartTime() - .split("\\.")[0], - items - .get(position) - .getFrequency() - .getRuns() - .get(i) - .getEndTime() - .split("\\.")[0], - i); + if (i < items + .get(position) + .getFrequency() + .getRuns().size() - 1) { + pos = checkCurrentTimeInBetweenDates(items + .get(position) + .getFrequency() + .getRuns() + .get(i) + .getStartTime() + .split("\\.")[0], items + .get(position) + .getFrequency() + .getRuns() + .get(i + 1) + .getStartTime() + .split("\\.")[0], i); + } else { + pos = checkCurrentTimeInBetweenDates(items + .get(position) + .getFrequency() + .getRuns() + .get(i) + .getStartTime() + .split("\\.")[0], items + .get(position) + .getFrequency() + .getRuns() + .get(i) + .getEndTime() + .split("\\.")[0], i); + } + finalTime = startTime + " to " + endTime; mScheduledTime.add(finalTime); @@ -547,6 +564,45 @@ public void onBindViewHolder(final Holder holder, final int position) { .getEndTime() .toString() .split("\\.")[0]))); + + if ((currentRunStatusForActivities + .get(position) + .getStatus() + .equalsIgnoreCase(SurveyActivitiesFragment.COMPLETED) || currentRunStatusForActivities + .get(position) + .getStatus() + .equalsIgnoreCase(SurveyActivitiesFragment.INCOMPLETE)) && pos < items + .get(position) + .getFrequency() + .getRuns() + .size() && status + .get(position) + .equalsIgnoreCase(SurveyActivitiesFragment.STATUS_CURRENT) || currentRunStatusForActivities + .get(holder.getAdapterPosition()).getCurrentRunId() == 0) { + holder.date.setText( + simpleDateFormatForOtherFreq.format( + simpleDateFormat5.parse( + items + .get(position) + .getFrequency() + .getRuns() + .get(pos + 1) + .getStartTime() + .toString() + .split("\\.")[0])) + + " to " + + simpleDateFormatForOtherFreq.format( + simpleDateFormat5.parse( + items + .get(position) + .getFrequency() + .getRuns() + .get(pos + 1) + .getEndTime() + .toString() + .split("\\.")[0]))); + } + } if (pos > 0) { @@ -576,6 +632,44 @@ public void onBindViewHolder(final Holder holder, final int position) { simpleDateFormatForOtherFreq.format(d1) + " to " + simpleDateFormatForOtherFreq.format(d2)); + + if ((currentRunStatusForActivities + .get(position) + .getStatus() + .equalsIgnoreCase(SurveyActivitiesFragment.COMPLETED) || currentRunStatusForActivities + .get(position) + .getStatus() + .equalsIgnoreCase(SurveyActivitiesFragment.INCOMPLETE)) && pos < items + .get(position) + .getFrequency() + .getRuns() + .size() && status + .get(position) + .equalsIgnoreCase(SurveyActivitiesFragment.STATUS_CURRENT) || currentRunStatusForActivities + .get(holder.getAdapterPosition()).getCurrentRunId() == 0) { + holder.date.setText( + simpleDateFormatForOtherFreq.format( + simpleDateFormat5.parse( + items + .get(position) + .getFrequency() + .getRuns() + .get(pos + 1) + .getStartTime() + .toString() + .split("\\.")[0])) + + " to " + + simpleDateFormatForOtherFreq.format( + simpleDateFormat5.parse( + items + .get(position) + .getFrequency() + .getRuns() + .get(pos + 1) + .getEndTime() + .toString() + .split("\\.")[0]))); + } } catch (Exception e) { Logger.log(e); } @@ -617,15 +711,12 @@ public void onClick(View view) { int totalRunVal = currentRunStatusForActivities.get(position).getTotalRun(); if (click) { click = false; - new Handler() - .postDelayed( - new Runnable() { - @Override - public void run() { - click = true; - } - }, - 1500); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + click = true; + } + }, 1500); if (paused) { Toast.makeText(context, R.string.study_Joined_paused, Toast.LENGTH_SHORT).show(); } else { @@ -693,7 +784,9 @@ public void onClick(View view) { } CustomActivitiesDailyDialogClass c = new CustomActivitiesDailyDialogClass( - context, mScheduledTime, p, false, SurveyActivitiesListAdapter.this); + context, mScheduledTime, p, false, SurveyActivitiesListAdapter.this, status + .get(holder.getAdapterPosition()), currentRunStatusForActivities + .get(holder.getAdapterPosition())); c.show(); } }); @@ -714,6 +807,25 @@ public void onClick(View view) { holder.itemlayout.setBackgroundColor(Color.parseColor("#c6ccd0")); holder.box1.setAlpha(.5f); holder.box2.setAlpha(.5f); + + if (currentRunStatusForActivities.get(position).getCurrentRunId() <= currentRunStatusForActivities.get(position).getTotalRun()) { + holder.run.setText( + context.getResources().getString(R.string.run) + + ": " + + (currentRunStatusForActivities.get(position).getCurrentRunId() + 1) + + "/" + + currentRunStatusForActivities.get(position).getTotalRun() + + ", " + + currentRunStatusForActivities.get(position).getCompletedRun() + + " " + + context.getResources().getString(R.string.done2) + + ", " + + currentRunStatusForActivities.get(position).getMissedRun() + + " " + + context.getResources().getString(R.string.missed)); + holder.process.setText(R.string.start); + bgShape.setColor(context.getResources().getColor(R.color.colorPrimary)); + } } else { holder.container.setBackgroundColor(context.getResources().getColor(R.color.white)); holder.itemlayout.setBackgroundColor(context.getResources().getColor(R.color.white)); diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivity.java b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivity.java index a363794b6b..963cfe25dd 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivity.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyActivity.java @@ -86,7 +86,6 @@ public class SurveyActivity extends AppCompatActivity private String studyStatus; private String position; private String enroll; - private String rejoin; public String activityId = ""; public String localNotification = ""; private LinearLayout menulayout; @@ -167,9 +166,6 @@ && getIntent().getStringExtra("to").equalsIgnoreCase("Resource")) { enroll = AppController.getHelperSharedPreference() .readPreference(SurveyActivity.this, getResources().getString(R.string.enroll), ""); - rejoin = - AppController.getHelperSharedPreference() - .readPreference(SurveyActivity.this, getResources().getString(R.string.rejoin), ""); } catch (Exception e) { Logger.log(e); } @@ -617,10 +613,6 @@ public String getEnroll() { return enroll; } - public String getRejoin() { - return rejoin; - } - @Override public void asyncResponse(T response, int responseCode) { if (responseCode == LOGOUT_REPSONSECODE) { diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyResourcesFragment.java b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyResourcesFragment.java index 922f040631..39302db5d0 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/SurveyResourcesFragment.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/SurveyResourcesFragment.java @@ -1,6 +1,6 @@ /* * Copyright © 2017-2019 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -95,7 +95,6 @@ public class SurveyResourcesFragment extends Fragment implements ApiCall.OnAs private DbServiceSubscriber dbServiceSubscriber; private static String RESOURCES = "resources"; private Realm realm; - private String participantDatastoreServer = "false"; private ArrayList arrayList; @Override @@ -881,14 +880,6 @@ public void asyncResponseFailure(int responseCode, String errormsg, String statu } } - public String getType() { - return studyHome.getWithdrawalConfig().getType(); - } - - public String getLeaveStudyMessage() { - return studyHome.getWithdrawalConfig().getMessage(); - } - public void updateuserpreference() { HashMap header = new HashMap(); header.put( @@ -909,7 +900,6 @@ public void updateuserpreference() { try { jsonObject.put("participantId", studies.getParticipantId()); jsonObject.put("studyId", ((SurveyActivity) context).getStudyId()); - jsonObject.put("delete", participantDatastoreServer); } catch (JSONException e) { Logger.log(e); } @@ -933,8 +923,7 @@ public void updateuserpreference() { userModulePresenter.performUpdateUserPreference(updatePreferenceEvent); } - public void responseServerWithdrawFromStudy(String flag) { - participantDatastoreServer = flag; + public void responseServerWithdrawFromStudy() { AppController.getHelperProgressDialog().showProgress(getActivity(), "", "", false); dbServiceSubscriber.deleteActivityRunsFromDbByStudyID( context, ((SurveyActivity) context).getStudyId()); @@ -1005,9 +994,8 @@ public void deactivateAccount() { JSONArray jsonArray1 = new JSONArray(); JSONObject jsonObject = new JSONObject(); jsonObject.put("studyId", AppConfig.StudyId); - jsonObject.put("delete", participantDatastoreServer); jsonArray1.put(jsonObject); - obj.put("deleteData", jsonArray1); + obj.put("studyData", jsonArray1); } catch (JSONException e) { Logger.log(e); } diff --git a/Android/app/src/main/java/com/harvard/studyappmodule/consent/ConsentBuilder.java b/Android/app/src/main/java/com/harvard/studyappmodule/consent/ConsentBuilder.java index 4682cc5bcd..2648870142 100644 --- a/Android/app/src/main/java/com/harvard/studyappmodule/consent/ConsentBuilder.java +++ b/Android/app/src/main/java/com/harvard/studyappmodule/consent/ConsentBuilder.java @@ -1,6 +1,6 @@ /* * Copyright © 2017-2019 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -57,7 +57,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -70,7 +70,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -83,7 +83,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -96,7 +96,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -109,7 +109,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -122,7 +122,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -135,7 +135,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -148,7 +148,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -161,7 +161,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection.setSummary(consent.getVisualScreens().get(i).getText()); consentSection.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getType()); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -179,7 +179,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St consentSection1.setHtmlContent(consent.getVisualScreens().get(i).getHtml()); consentSection1.setCustomImageName("task_img2"); - visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle()); + visualStep = new ConsentVisualStep(consent.getVisualScreens().get(i).getTitle() + "_" + i); visualStep.setStepTitle(R.string.notxt); visualStep.setSection(consentSection1); visualStep.setNextButtonString(context.getResources().getString(R.string.next1)); @@ -255,7 +255,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St ConsentDocumentStep documentStep = new ConsentDocumentStepCustom("review"); documentStep.setConsentHTML(docBuilder.toString()); documentStep.setStepTitle(R.string.notxt); - documentStep.setConfirmMessage(consent.getReview().getReasonForConsent()); + documentStep.setConfirmMessage(context.getString(R.string.consentConfirmation)); visualSteps.add(documentStep); } else { if (consent.getVisualScreens().size() > 0) { @@ -288,7 +288,7 @@ public ArrayList createsurveyquestion(Context context, Consent consent, St documentStep.setConsentHTML(docBuilder.toString()); documentStep.setStepTitle(R.string.notxt); documentStep.setOptional(false); - documentStep.setConfirmMessage(consent.getReview().getReasonForConsent()); + documentStep.setConfirmMessage(context.getString(R.string.consentConfirmation)); visualSteps.add(documentStep); } } diff --git a/Android/app/src/main/java/com/harvard/usermodule/LoginCallbackActivity.java b/Android/app/src/main/java/com/harvard/usermodule/LoginCallbackActivity.java index 789b7c41fb..3e17b3d9b9 100644 --- a/Android/app/src/main/java/com/harvard/usermodule/LoginCallbackActivity.java +++ b/Android/app/src/main/java/com/harvard/usermodule/LoginCallbackActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -255,10 +255,6 @@ LoginCallbackActivity.this, getString(R.string.loginflow), "") "enroll", SharedPreferenceHelper.readPreference( LoginCallbackActivity.this, getString(R.string.login_studyinfo_enroll), "")); - intent.putExtra( - "rejoin", - SharedPreferenceHelper.readPreference( - LoginCallbackActivity.this, getString(R.string.login_studyinfo_rejoin), "")); startActivity(intent); } else { if (AppConfig.AppType.equalsIgnoreCase(getString(R.string.app_gateway))) { diff --git a/Android/app/src/main/res/layout/gateway_item1.xml b/Android/app/src/main/res/layout/gateway_item1.xml index 43c835f8ac..0de4a0cd3a 100644 --- a/Android/app/src/main/res/layout/gateway_item1.xml +++ b/Android/app/src/main/res/layout/gateway_item1.xml @@ -68,7 +68,7 @@ style="@style/TextType1" android:layout_below="@id/logo" android:layout_centerHorizontal="true" - android:layout_marginTop="30dp" + android:layout_marginTop="20dp" android:text="@string/gateway_welcome" android:textColor="@android:color/white" /> diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml index 6c5c4df737..84cfd069e9 100644 --- a/Android/app/src/main/res/values/strings.xml +++ b/Android/app/src/main/res/values/strings.xml @@ -218,7 +218,6 @@ Need help? Contact us. Press the \'back\' button to exit Sorry, enrollment for this study has been closed for now. Please check back later or explore other studies you could join. - Sorry, this study currently does not allow previously enrolled participants to rejoin after they have withdrawn. Please check back later or explore other studies. This study has been temporarily paused. Please check back later. The study has been temporarily paused. You can participate in activities once it is resumed. Please check back later. Sorry, enrollment for this study has been closed for now. Please check back later or explore other studies you could join. @@ -286,7 +285,6 @@ studyStatus position enroll - rejoin studyVersion This study is not available. Please sign in to view details. @@ -479,6 +477,9 @@ refreshToken Reminder: The fetal kick recording activity is in progress. Please update the app with the count of the kicks or movements you experience in this period.. Stay up-to-date! Turn ON notifications and reminders in app and phone settings to get notified about study activities in a timely manner. + By tapping on Agree, you confirm that you have reviewed the consent document and agree to participate in the study. + Are you sure you want to leave the study? + Are you sure you want to leave the study?\nThis will also delete your app account. Standalone Gateway Please fill out the text field too diff --git a/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/OAuthController.java b/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/OAuthController.java index b85304651c..a305e55387 100644 --- a/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/OAuthController.java +++ b/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/OAuthController.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -26,6 +26,8 @@ import com.google.cloud.healthcare.fdamystudies.mapper.AuditEventMapper; import com.google.cloud.healthcare.fdamystudies.oauthscim.common.AuthScimAuditHelper; import com.google.cloud.healthcare.fdamystudies.oauthscim.service.OAuthService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import java.util.Collections; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -44,6 +46,10 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Api( + tags = "Get Token", + value = "Get access token and refresh token based on grant type", + description = "Get access token and refresh token based on grant type") @CrossOrigin @RestController public class OAuthController { @@ -60,6 +66,13 @@ public class OAuthController { @Autowired private AuthScimAuditHelper auditHelper; + @ApiOperation( + notes = + "Refer to the [The OAuth 2.0 Token Endpoint](https://www.ory.sh/hydra/docs/reference/api/#the-oauth-20-token-endpoint)" + + " for request and response details ", + value = + "Get access token and refresh token based on grant type. " + + "Refer to the ORY Hydra REST API documentation for request and response details") @PostMapping( value = "/oauth2/token", produces = MediaType.APPLICATION_JSON_VALUE, diff --git a/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/UserController.java b/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/UserController.java index caa8a403ab..91e20b2bc9 100644 --- a/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/UserController.java +++ b/auth-server/oauth-scim-service/src/main/java/com/google/cloud/healthcare/fdamystudies/oauthscim/controller/UserController.java @@ -28,6 +28,8 @@ import com.google.cloud.healthcare.fdamystudies.mapper.AuditEventMapper; import com.google.cloud.healthcare.fdamystudies.oauthscim.common.AuthScimAuditHelper; import com.google.cloud.healthcare.fdamystudies.oauthscim.service.UserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import org.slf4j.ext.XLogger; @@ -42,8 +44,13 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +@Api( + tags = "Users", + value = "user management in auth server", + description = "Operations pertaining to users in auth server") @RestController public class UserController { @@ -57,10 +64,12 @@ public class UserController { @Autowired private AuthScimAuditHelper auditHelper; + @ApiOperation(value = "create a new user") @PostMapping( value = "/users", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseStatus(HttpStatus.CREATED) public ResponseEntity createUser( @Valid @RequestBody UserRequest userRequest, HttpServletRequest request) { logger.entry(String.format(BEGIN_S_REQUEST_LOG, request.getRequestURI())); @@ -70,6 +79,7 @@ public ResponseEntity createUser( return ResponseEntity.status(HttpStatus.CREATED.value()).body(userResponse); } + @ApiOperation(value = "reset the password and send an email with temporary password") @PostMapping( value = "/user/reset_password", produces = MediaType.APPLICATION_JSON_VALUE, @@ -90,6 +100,7 @@ public ResponseEntity resetPassword( .body(resetPasswordResponse); } + @ApiOperation(value = "replace old password with new password") @PutMapping( value = "/users/{userId}/change_password", produces = MediaType.APPLICATION_JSON_VALUE, @@ -110,6 +121,7 @@ public ResponseEntity changePassword( return ResponseEntity.status(userResponse.getHttpStatusCode()).body(userResponse); } + @ApiOperation(value = "update user account status") @PutMapping( value = "/users/{userId}", produces = MediaType.APPLICATION_JSON_VALUE, @@ -132,6 +144,7 @@ public ResponseEntity updateEmailStatus( return ResponseEntity.status(userResponse.getHttpStatusCode()).body(userResponse); } + @ApiOperation(value = "revoke access token and refresh token when user gets logged out") @PostMapping(value = "/users/{userId}/logout", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity logout( @PathVariable String userId, @@ -153,6 +166,7 @@ public ResponseEntity logout( return ResponseEntity.status(userResponse.getHttpStatusCode()).body(userResponse); } + @ApiOperation(value = "delete a user") @DeleteMapping(value = "/users/{userId}") public void deleteUserAccount(@PathVariable String userId, HttpServletRequest request) { logger.entry(String.format(BEGIN_S_REQUEST_LOG, request.getRequestURI())); diff --git a/auth-server/oauth-scim-service/src/main/resources/application.properties b/auth-server/oauth-scim-service/src/main/resources/application.properties index 32fac33f17..9f6dcb77c7 100644 --- a/auth-server/oauth-scim-service/src/main/resources/application.properties +++ b/auth-server/oauth-scim-service/src/main/resources/application.properties @@ -122,14 +122,14 @@ cors.allowed.origins=${CORS_ALLOWED_ORIGINS} component.name=SCIM AUTH SERVER mail.subject.reset-password-mobile=Password help for {{appName}} app -mail.body.reset-password-mobile=
Hi

Thank you for reaching out for password help.

Here is a temporary password which you can use to sign in to the {{appName}} app. You will be required to set up a new password after signing in.

Temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of ${oauth.scim.service.reset.password.expiry.hours} hours only.

For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The {{appName}} support team
----------------------------------------------------
Note: This is an auto-generated email. Please do not reply. In case you did not request password help, please visit the app and change your password as a security measure.
+mail.body.reset-password-mobile=
Hi

Thanks for reaching out for password help.

Here is a temporary password which you can use to sign in to the {{appName}} app. You will be required to set up a new password after signing in.

Your temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of ${oauth.scim.service.reset.password.expiry.hours} hours only.

For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The {{appName}} support team
----------------------------------------------------
Note: This is an auto-generated email. Please do not reply. In case you did not request password help, please visit the app and change your password as a security measure.
mail.account.locked.subject-mobile=Your account has been locked -mail.account.locked.content-mobile=
Hi

This is to inform you that, as a security measure, your user account for {{appName}} app has been temporarily locked for a period of ${oauth.scim.service.account.lockout.period.minutes} minutes, due to multiple consecutive failed sign-in attempts with incorrect password.

Please try signing in again after this period.

Alternatively, use the temporary password given below to sign in to the {{appName}} app and then set up a new password.

Temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of
${oauth.scim.service.account.lockout.period.minutes} minutes only.


For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The {{appName}} support team
-------------------------------------------------------------------------------------------------
Note: This is an auto-generated email. Please do not reply.
+mail.account.locked.content-mobile=
Hi

This is to inform you that, as a security measure, your user account for {{appName}} app has been temporarily locked for a period of ${oauth.scim.service.account.lockout.period.minutes} minutes, due to consecutive failed sign-in attempts.

Please try signing in again after this period.

Alternatively, use the temporary password given below to sign in to the {{appName}} app and then set up a new password.

Your temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of
${oauth.scim.service.account.lockout.period.minutes} minutes only.


For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The {{appName}} support team
-------------------------------------------------------------------------------------------------
Note: This is an auto-generated email. Please do not reply.
mail.subject.reset-password=Password help for your Participant Manager account -mail.body.reset-password=
Hi

Thank you for reaching out for password help.

Here is a temporary password which you can use to sign in to ${org.name} Participant Manager portal. You will be required to set up a new password after signing in.

Temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of ${oauth.scim.service.reset.password.expiry.hours} hours only.

For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The ${org.name} MyStudies support team
----------------------------------------------------
Note: This is an auto-generated email. Please do not reply. In case you did not request for password help, please visit the portal and change your password as a security measure.
+mail.body.reset-password=
Hi

Thanks for reaching out for password help.

Here is a temporary password which you can use to sign in to ${org.name}’s Participant Manager portal. You will be required to set up a new password after signing in.

Your temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of ${oauth.scim.service.reset.password.expiry.hours} hours only.

For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The ${org.name} MyStudies support team
----------------------------------------------------
Note: This is an auto-generated email. Please do not reply. In case you did not request for password help, please visit the portal and change your password as a security measure.
mail.account.locked.subject=Your account has been locked -mail.account.locked.content=
Hi

This is to inform you that, as a security measure, your admin user account for the ${org.name} Participant Manager portal has been temporarily locked for a period of ${oauth.scim.service.account.lockout.period.minutes} minutes, due to multiple consecutive failed sign-in attempts with incorrect password.

Please try signing in again after this period.

Alternatively, use the temporary password given below to sign in to the Participant Manager and then set up a new password.

Temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of
${oauth.scim.service.account.lockout.period.minutes} minutes only.


For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The ${org.name} MyStudies support team
-------------------------------------------------------------------------------------------------
Note: This is an auto-generated email. Please do not reply.
+mail.account.locked.content=
Hi

This is to inform you that, as a security measure, your admin account for ${org.name}'s Participant Manager portal has been temporarily locked for a period of ${oauth.scim.service.account.lockout.period.minutes} minutes, due to consecutive failed sign-in attempts.

Please try signing in again after this period.

Alternatively, use the temporary password given below to sign in to the Participant Manager and then set up a new password.

Your temporary password: {{tempPassword}}

Please note that this temporary password can be used only once and is valid for a period of
${oauth.scim.service.account.lockout.period.minutes} minutes only.


For any questions or assistance, please write to ${mail.contact-email}

Thanks,
The ${org.name} MyStudies support team
-------------------------------------------------------------------------------------------------
Note: This is an auto-generated email. Please do not reply.
diff --git a/auth-server/oauth-scim-service/src/main/resources/static/css/style.css b/auth-server/oauth-scim-service/src/main/resources/static/css/style.css index daefb4ab1c..f0977e989d 100644 --- a/auth-server/oauth-scim-service/src/main/resources/static/css/style.css +++ b/auth-server/oauth-scim-service/src/main/resources/static/css/style.css @@ -518,6 +518,14 @@ body#body-container { margin-top: 80%; } +.loading_text_error { + color: var(--space-gray); + font-size: 14px; + font-family: Muli, sans-serif !important; + font-weight: 600; + text-align: center; +} + @-webkit-keyframes loading-spinner { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } @@ -560,3 +568,4 @@ body#body-container { @media only screen and (max-width: 500px) and (min-width: 320px) { .mob_mt { margin-top:10px; } } + diff --git a/auth-server/oauth-scim-service/src/main/resources/static/images/landing-logo.png b/auth-server/oauth-scim-service/src/main/resources/static/images/landing-logo.png new file mode 100644 index 0000000000..e14676cd6a Binary files /dev/null and b/auth-server/oauth-scim-service/src/main/resources/static/images/landing-logo.png differ diff --git a/auth-server/oauth-scim-service/src/main/resources/static/images/landingpage-logo.png b/auth-server/oauth-scim-service/src/main/resources/static/images/landingpage-logo.png deleted file mode 100644 index 141d9a0df1..0000000000 Binary files a/auth-server/oauth-scim-service/src/main/resources/static/images/landingpage-logo.png and /dev/null differ diff --git a/auth-server/oauth-scim-service/src/main/resources/static/js/autoLogin.js b/auth-server/oauth-scim-service/src/main/resources/static/js/autoLogin.js index 8b58838dbe..4aeb9d4780 100644 --- a/auth-server/oauth-scim-service/src/main/resources/static/js/autoLogin.js +++ b/auth-server/oauth-scim-service/src/main/resources/static/js/autoLogin.js @@ -10,4 +10,14 @@ window.onload = function() { window.setTimeout(function() { document.autoSignInForm.submit(); }, 500); -}; \ No newline at end of file + + window.setTimeout(function() { + hideLoader(); + }, 10000); +}; + +var hideLoader = function () { + $(".loading_div").fadeOut("slow"); + $("#errorMsg").show(); + $("#errorImg").show(); +}; diff --git a/auth-server/oauth-scim-service/src/main/resources/static/js/consent.js b/auth-server/oauth-scim-service/src/main/resources/static/js/consent.js index 35b563ba4a..95c0e65441 100644 --- a/auth-server/oauth-scim-service/src/main/resources/static/js/consent.js +++ b/auth-server/oauth-scim-service/src/main/resources/static/js/consent.js @@ -10,4 +10,14 @@ window.onload = function() { window.setTimeout(function() { document.consentForm.submit(); }, 500); -}; \ No newline at end of file + + window.setTimeout(function() { + hideLoader(); + }, 10000); +}; + +var hideLoader = function () { + $(".loading_div").fadeOut("slow"); + $("#errorMsg").show(); + $("#errorImg").show(); +}; diff --git a/auth-server/oauth-scim-service/src/main/resources/templates/autoLogin.html b/auth-server/oauth-scim-service/src/main/resources/templates/autoLogin.html index ac2a432e07..359de488ed 100644 --- a/auth-server/oauth-scim-service/src/main/resources/templates/autoLogin.html +++ b/auth-server/oauth-scim-service/src/main/resources/templates/autoLogin.html @@ -24,6 +24,7 @@ href="https://getbootstrap.com/docs/4.0/examples/sticky-footer/sticky-footer.css" rel="stylesheet" /> + @@ -54,6 +63,14 @@ style="display: none" /> +
+ + +
diff --git a/auth-server/oauth-scim-service/src/main/resources/templates/consent.html b/auth-server/oauth-scim-service/src/main/resources/templates/consent.html index 01c3ae3ea4..92214cbdf7 100644 --- a/auth-server/oauth-scim-service/src/main/resources/templates/consent.html +++ b/auth-server/oauth-scim-service/src/main/resources/templates/consent.html @@ -24,6 +24,7 @@ href="https://getbootstrap.com/docs/4.0/examples/sticky-footer/sticky-footer.css" rel="stylesheet" /> + @@ -54,6 +63,14 @@ style="display: none" />
+
+ + +
diff --git a/auth-server/oauth-scim-service/src/main/resources/templates/login.html b/auth-server/oauth-scim-service/src/main/resources/templates/login.html index 011540a73d..7fd44a044f 100644 --- a/auth-server/oauth-scim-service/src/main/resources/templates/login.html +++ b/auth-server/oauth-scim-service/src/main/resources/templates/login.html @@ -66,7 +66,7 @@
- 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.2.6.RELEASE - - - com.google.cloud.healthcare.fdamystudies - common-service - 0.0.1-SNAPSHOT - jar - common-service - Common service module for FDA MyStudies + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + com.google.cloud.healthcare.fdamystudies + common-service + 0.0.1-SNAPSHOT + jar + common-service + Common service module for FDA MyStudies - - 1.8 - 1.8 - 1.4 - - - - - com.google.cloud - libraries-bom - 15.1.0 - pom - import - - - com.google.api - gax - 1.57.0 - - - com.google.api - gax-grpc - 1.57.0 - - - + + 1.8 + 1.8 + 1.4 + + + + + com.google.cloud + libraries-bom + 15.1.0 + pom + import + + + com.google.api + gax + 1.57.0 + + + com.google.api + gax-grpc + 1.57.0 + + + + + + + com.google.cloud + google-cloud-logging + 1.101.2 + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + ch.qos.logback + logback-classic + + + org.apache.logging.log4j + log4j-to-slf4j + + + + + org.projectlombok + lombok + true + + + org.slf4j + slf4j-ext + + + org.apache.commons + commons-text + ${common.text.version} + + + org.springframework.boot + spring-boot-starter-actuator + + + commons-codec + commons-codec + + + javax.mail + mail + ${mail.version} + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-collections4 + 4.1 + + + org.jasypt + jasypt + 1.9.3 + + + commons-io + commons-io + 2.7 + + + com.jayway.jsonpath + json-path + + + org.apache.syncope.common + syncope-common-lib + 2.0.16 + + + io.springfox + springfox-swagger2 + 2.9.2 + compile + + + io.springfox + springfox-swagger-ui + 2.9.2 + compile + + + io.springfox + springfox-bean-validators + 2.9.2 + + + org.bitbucket.tek-nik + spring-swagger-simplified + 1.0.9 + + + org.jsoup + jsoup + 1.13.1 + + - - - com.google.cloud - google-cloud-logging - 1.101.2 - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-web - - - ch.qos.logback - logback-classic - - - org.apache.logging.log4j - log4j-to-slf4j - - - - - org.projectlombok - lombok - true - - - org.slf4j - slf4j-ext - - - org.apache.commons - commons-text - ${common.text.version} - - - org.springframework.boot - spring-boot-starter-actuator - - - commons-codec - commons-codec - - - javax.mail - mail - ${mail.version} - - - org.springframework.boot - spring-boot-starter-mail - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.apache.commons - commons-lang3 - - - org.apache.commons - commons-collections4 - 4.1 - - - org.jasypt - jasypt - 1.9.3 - - - commons-io - commons-io - 2.7 - - - com.jayway.jsonpath - json-path - - - org.apache.syncope.common - syncope-common-lib - 2.0.16 - - - - - common-service - - - com.google.cloud.tools - jib-maven-plugin - 2.5.2 - - - true - - - - + + common-service + + + com.google.cloud.tools + jib-maven-plugin + 2.5.2 + + + true + + + + diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/CommonConstants.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/CommonConstants.java index 4b74f704ca..357336799b 100644 --- a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/CommonConstants.java +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/CommonConstants.java @@ -94,4 +94,12 @@ private CommonConstants() {} public static final String ACTIVITY_STATE = "activity_state"; public static final String PAGE_NO = "0"; + + public static final String UNAUTHORIZED_MESSAGE = "Unauthorized or Invalid token"; + + public static final String APPLICATION_ERROR_MESSAGE = + "Sorry, an error has occurred and your request could not be processed. Please try again later."; + + public static final String BAD_REQUEST_MESSAGE = + "Invalid entries found in the submitted form. Please try again."; } diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/SwaggerConfig.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/SwaggerConfig.java new file mode 100644 index 0000000000..2ce94ba946 --- /dev/null +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/SwaggerConfig.java @@ -0,0 +1,133 @@ +/* + * Copyright 2020-2021 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +package com.google.cloud.healthcare.fdamystudies.config; + +import com.google.cloud.healthcare.fdamystudies.beans.BaseResponse; +import com.google.cloud.healthcare.fdamystudies.beans.ValidationErrorResponse; +import com.google.cloud.healthcare.fdamystudies.common.CommonConstants; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.format.support.FormattingConversionService; +import org.springframework.validation.Validator; +import org.springframework.web.accept.ContentNegotiationManager; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.builders.ResponseMessageBuilder; +import springfox.documentation.schema.ModelRef; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ResponseMessage; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger.web.UiConfiguration; +import springfox.documentation.swagger.web.UiConfigurationBuilder; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +@Import(BeanValidatorPluginsConfiguration.class) +public class SwaggerConfig extends WebMvcConfigurationSupport { + + @Bean + public Docket api() { + Docket docket = + new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.google.cloud.healthcare.fdamystudies")) + .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class)) + .paths(PathSelectors.any()) + .build(); + + ModelRef errorModel = new ModelRef(BaseResponse.class.getSimpleName()); + List responseMessages = + Arrays.asList( + new ResponseMessageBuilder() + .code(400) + .message(CommonConstants.BAD_REQUEST_MESSAGE) + .responseModel(new ModelRef(ValidationErrorResponse.class.getSimpleName())) + .build(), + new ResponseMessageBuilder() + .code(401) + .message(CommonConstants.UNAUTHORIZED_MESSAGE) + .responseModel(errorModel) + .build(), + new ResponseMessageBuilder() + .code(500) + .message(CommonConstants.APPLICATION_ERROR_MESSAGE) + .responseModel(errorModel) + .build()); + + docket + .useDefaultResponseMessages(false) + .globalResponseMessage(RequestMethod.GET, responseMessages) + .globalResponseMessage(RequestMethod.POST, responseMessages) + .globalResponseMessage(RequestMethod.PUT, responseMessages) + .globalResponseMessage(RequestMethod.DELETE, responseMessages); + return docket; + } + + private ApiInfo apiInfo() { + return new ApiInfo( + ApiInfo.DEFAULT.getTitle(), + null, + ApiInfo.DEFAULT.getVersion(), + null, + ApiInfo.DEFAULT_CONTACT, + "View License", + "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt", + Collections.emptyList()); + } + + @Bean + public UiConfiguration tryItOutConfig() { + final String[] methodsWithTryItOutButton = {}; + return UiConfigurationBuilder.builder() + .supportedSubmitMethods(methodsWithTryItOutButton) + .build(); + } + + @Override + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + registry + .addResourceHandler("swagger-ui.html") + .addResourceLocations("classpath:/META-INF/resources/"); + registry + .addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); + + registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/"); + registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/"); + registry.addResourceHandler("/images/**").addResourceLocations("classpath:/static/images/"); + } + + @Override + @Bean + public RequestMappingHandlerAdapter requestMappingHandlerAdapter( + @Qualifier("mvcContentNegotiationManager") + ContentNegotiationManager contentNegotiationManager, + @Qualifier("mvcConversionService") FormattingConversionService conversionService, + @Qualifier("mvcValidator") Validator validator) { + RequestMappingHandlerAdapter adapter = + super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator); + // prevent Spring MVC @ModelAttribute variables from appearing in URL + adapter.setIgnoreDefaultModelOnRedirect(true); + return adapter; + } +} diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/SwaggerParameterAnnotationPlugin.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/SwaggerParameterAnnotationPlugin.java new file mode 100644 index 0000000000..15a7946bd2 --- /dev/null +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/SwaggerParameterAnnotationPlugin.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +package com.google.cloud.healthcare.fdamystudies.config; + +import static springfox.bean.validators.plugins.Validators.annotationFromBean; +import static springfox.bean.validators.plugins.Validators.annotationFromField; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import springfox.bean.validators.plugins.Validators; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin; +import springfox.documentation.spi.schema.contexts.ModelPropertyContext; + +@Component +@Order(Validators.BEAN_VALIDATOR_PLUGIN_ORDER) +public class SwaggerParameterAnnotationPlugin implements ModelPropertyBuilderPlugin { + + @Override + public boolean supports(DocumentationType delimiter) { + // we simply support all documentationTypes! + return true; + } + + @Override + public void apply(ModelPropertyContext context) { + Optional notBlank = extractAnnotation(context, NotBlank.class); + if (notBlank.isPresent()) { + context.getBuilder().required(true); + } + + Optional notEmpty = extractAnnotation(context, NotEmpty.class); + if (notEmpty.isPresent()) { + context.getBuilder().required(true); + } + } + + @VisibleForTesting + Optional extractAnnotation(ModelPropertyContext context, Class claz) { + return annotationFromBean(context, claz).or(annotationFromField(context, claz)); + } +} diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/CleanPropertyAdvice.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/CleanPropertyAdvice.java new file mode 100644 index 0000000000..9fa0c948da --- /dev/null +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/CleanPropertyAdvice.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020-2021 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +package com.google.cloud.healthcare.fdamystudies.controller; + +import java.beans.PropertyEditorSupport; +import org.jsoup.Jsoup; +import org.jsoup.safety.Whitelist; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.InitBinder; + +@ControllerAdvice +public class CleanPropertyAdvice { + public static class CustomPropertyEditor extends PropertyEditorSupport { + + @Override + public void setAsText(String text) throws IllegalArgumentException { + String safe = Jsoup.clean(text, Whitelist.simpleText()); + setValue(safe); + } + } + + @InitBinder + public void bindPropertyCleaner(WebDataBinder webDataBinder) { + CustomPropertyEditor propertyCleaner = new CustomPropertyEditor(); + webDataBinder.registerCustomEditor(String.class, propertyCleaner); + } +} diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/HealthController.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/HealthController.java index f8686f3649..3924cfc955 100644 --- a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/HealthController.java +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/HealthController.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -8,17 +8,50 @@ package com.google.cloud.healthcare.fdamystudies.controller; +import com.google.cloud.healthcare.fdamystudies.beans.BaseResponse; +import com.google.cloud.healthcare.fdamystudies.beans.ValidationErrorResponse; +import com.google.cloud.healthcare.fdamystudies.common.CommonConstants; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; import java.util.Collections; import java.util.Map; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +@Api(tags = "Get Health", description = "Provides an indication about the health of the service") +// Swagger Issue - Default response message models are not recognized directly but works fine if the +// response models are referred from the @ApiResponse at least once. Refer SwaggerConfig class for +// default response messages configuration. +@ApiResponses( + value = { + @ApiResponse( + code = 400, + message = CommonConstants.BAD_REQUEST_MESSAGE, + response = ValidationErrorResponse.class), + @ApiResponse( + code = 401, + message = CommonConstants.UNAUTHORIZED_MESSAGE, + response = BaseResponse.class), + @ApiResponse( + code = 500, + message = CommonConstants.APPLICATION_ERROR_MESSAGE, + response = BaseResponse.class) + }) @RestController public class HealthController { private static Map alwaysUp = Collections.singletonMap("status", "UP"); + @ApiOperation( + value = "Provides an indication about the health of the service", + notes = "Default response codes 400 and 401 are not applicable for this operation") + @ApiResponses( + value = { + @ApiResponse(code = 200, message = "Service is Up and Running"), + }) @GetMapping( value = "/healthCheck", produces = {MediaType.APPLICATION_JSON_VALUE}) diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/model/StudyEntity.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/model/StudyEntity.java index cb7da08b1b..fc78208f31 100644 --- a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/model/StudyEntity.java +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/model/StudyEntity.java @@ -13,6 +13,7 @@ import static com.google.cloud.healthcare.fdamystudies.common.ColumnConstraints.SMALL_LENGTH; import static com.google.cloud.healthcare.fdamystudies.common.ColumnConstraints.TINY_LENGTH; import static com.google.cloud.healthcare.fdamystudies.common.ColumnConstraints.XS_LENGTH; +import static com.google.cloud.healthcare.fdamystudies.common.CommonConstants.EMAIL_LENGTH; import java.beans.Transient; import java.io.Serializable; @@ -123,6 +124,10 @@ public class StudyEntity implements Serializable { @Column(name = "logo_image_url", length = LARGE_LENGTH) private String logoImageUrl; + @ToString.Exclude + @Column(name = "contact_email", length = EMAIL_LENGTH) + private String contactEmail; + @OneToMany( cascade = CascadeType.ALL, fetch = FetchType.LAZY, diff --git a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/AuditEventServiceImpl.java b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/AuditEventServiceImpl.java index c894d18446..1ac49ec225 100644 --- a/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/AuditEventServiceImpl.java +++ b/common-modules/common-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/AuditEventServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -40,21 +40,24 @@ public void postAuditLogEvent(AuditLogEventRequest auditRequest) { logger.entry( String.format("begin postAuditLogEvent() for %s event", auditRequest.getEventCode())); - Logging logging = LoggingOptions.getDefaultInstance().getService(); + try (Logging logging = LoggingOptions.getDefaultInstance().getService()) { - // The data to write to the log - Map jsonPayloadMap = getObjectMapper().convertValue(auditRequest, Map.class); + // The data to write to the log + Map jsonPayloadMap = getObjectMapper().convertValue(auditRequest, Map.class); - LogEntry entry = - LogEntry.newBuilder(Payload.JsonPayload.of(jsonPayloadMap)) - .setTimestamp(auditRequest.getOccurred().getTime()) - .setSeverity(Severity.INFO) - .setLogName(AUDIT_LOG_NAME) - .setResource(MonitoredResource.newBuilder("global").build()) - .build(); + LogEntry entry = + LogEntry.newBuilder(Payload.JsonPayload.of(jsonPayloadMap)) + .setTimestamp(auditRequest.getOccurred().getTime()) + .setSeverity(Severity.INFO) + .setLogName(AUDIT_LOG_NAME) + .setResource(MonitoredResource.newBuilder("global").build()) + .build(); - // Writes the log entry asynchronously - logging.write(Collections.singleton(entry)); + // Writes the log entry asynchronously + logging.write(Collections.singleton(entry)); + } catch (Exception e) { + logger.error(String.format("%s failed with an exception", auditRequest.getEventCode()), e); + } logger.exit( String.format("postAuditLogEvent() for %s event finished", auditRequest.getEventCode())); } diff --git a/common-modules/common-tests/src/main/java/com/google/cloud/healthcare/fdamystudies/common/BaseMockIT.java b/common-modules/common-tests/src/main/java/com/google/cloud/healthcare/fdamystudies/common/BaseMockIT.java index 2d89efebbb..90b1182bd4 100644 --- a/common-modules/common-tests/src/main/java/com/google/cloud/healthcare/fdamystudies/common/BaseMockIT.java +++ b/common-modules/common-tests/src/main/java/com/google/cloud/healthcare/fdamystudies/common/BaseMockIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -25,6 +25,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.matching.ContainsPattern; @@ -32,7 +35,10 @@ import com.google.cloud.healthcare.fdamystudies.config.CommonModuleConfiguration; import com.google.cloud.healthcare.fdamystudies.config.WireMockInitializer; import com.google.cloud.healthcare.fdamystudies.service.AuditEventService; +import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Base64; import java.util.List; @@ -46,6 +52,7 @@ import javax.mail.internet.MimeMessage; import javax.servlet.ServletContext; import javax.servlet.http.Cookie; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.AfterEach; @@ -60,6 +67,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; @@ -116,6 +124,8 @@ public class BaseMockIT { @Autowired protected JavaMailSender emailSender; + @Autowired private TestRestTemplate restTemplate; + protected List auditRequests = new ArrayList<>(); @LocalServerPort int randomServerPort; @@ -330,4 +340,31 @@ protected MimeMessage verifyMimeMessage( .isEqualToIgnoringCase("text/html; charset=utf-8"); return mail; } + + protected String generateApiDocs() throws IOException { + // get swagger json + String apiDocs = this.restTemplate.getForObject("/v2/api-docs", String.class); + + // format the json + ObjectMapper mapper = new ObjectMapper(); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + + ObjectNode jsonObjNode = mapper.readValue(apiDocs, ObjectNode.class); + jsonObjNode.put("host", "localhost:8080"); + + // prepare the filepath + String documentPath = Paths.get("").toAbsolutePath().toString(); + if (StringUtils.contains(documentPath, "fda-mystudies")) { + documentPath = + documentPath.substring(0, documentPath.indexOf("fda-mystudies")) + + "fda-mystudies/documentation/API"; + } + documentPath += servletContext.getContextPath() + "/openapi.json"; + documentPath = documentPath.replace(" ", "_"); + + // write api-docs json to a file + FileUtils.write(new File(documentPath), jsonObjNode.toPrettyString(), Charset.defaultCharset()); + logger.info(String.format("Open API documentation created at %s", documentPath)); + return documentPath; + } } diff --git a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyParticipantId_failure.json b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyParticipantId_failure.json index a3dda8b8bf..3eb119bb59 100644 --- a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyParticipantId_failure.json +++ b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyParticipantId_failure.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/response-datastore/participant/withdraw?studyId=OpenStudy02&participantId=&deleteResponses=true" + "url": "/response-datastore/participant/withdraw?studyId=OpenStudy02&participantId=" }, "response": { "status": 400, diff --git a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyStudyId_failure.json b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyStudyId_failure.json index da37620a07..4c532536a1 100644 --- a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyStudyId_failure.json +++ b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_emptyStudyId_failure.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/response-datastore/participant/withdraw?studyId=&studyVersion=3.2&participantId=4&deleteResponses=true" + "url": "/response-datastore/participant/withdraw?studyId=&studyVersion=3.2&participantId=4" }, "response": { "status": 200, diff --git a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_failureCase.json b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_failureCase.json index 4afb9e826d..a720221778 100644 --- a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_failureCase.json +++ b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_failureCase.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/response-datastore/participant/withdraw?studyId=StudyofHealthNew&studyVersion=3.2&participantId=4&deleteResponses=true" + "url": "/response-datastore/participant/withdraw?studyId=StudyofHealthNew&studyVersion=3.2&participantId=4" }, "response": { diff --git a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_success.json b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_success.json index 363a42469c..f457a9823d 100644 --- a/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_success.json +++ b/common-modules/common-tests/src/main/resources/mappings/enroll-service/withdraw_success.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/response-datastore/participant/withdraw?studyId=OpenStudy02&studyVersion=3.5&participantId=4&deleteResponses=true" + "url": "/response-datastore/participant/withdraw?studyId=OpenStudy02&studyVersion=3.5&participantId=4" }, "response": { "status": 200, diff --git a/common-modules/common-tests/src/main/resources/mappings/response-service/response-server-deactivate.json b/common-modules/common-tests/src/main/resources/mappings/response-service/response-server-deactivate.json index b9f76c1419..1dc9adcc74 100644 --- a/common-modules/common-tests/src/main/resources/mappings/response-service/response-server-deactivate.json +++ b/common-modules/common-tests/src/main/resources/mappings/response-service/response-server-deactivate.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/response-datastore/participant/withdraw?studyId=studyId1&studyVersion=3.6&participantId=4&deleteResponses=true" + "url": "/response-datastore/participant/withdraw?studyId=studyId1&studyVersion=3.6&participantId=4" }, "response": { "status": 200, diff --git a/db-migration/README.md b/db-migration/README.md new file mode 100644 index 0000000000..88926a8346 --- /dev/null +++ b/db-migration/README.md @@ -0,0 +1,182 @@ + + +## Migrating Databases + +### Introduction +This guide provides instructions for migrating databases between versions of +FDA MyStudies using the [Flyway Maven plugin](https://flywaydb.org/documentation/usage/maven/). +This can be used to migrate between compatible versions of FDA MyStudies. + +### Before you begin +Confirm the user account you will use has the `Project Owner` role in the +following projects: + +* `{PREFIX}-{ENV}-data` +* `{PREFIX}-{ENV}-networks` +* `{PREFIX}-{ENV}-secrets` + +If you followed the [Deployment Guide](https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/deployment/README.md), +you can use a user which you added to the group `{PREFIX}-{ENV}-project-owners@{DOMAIN}` + +### Connect to virtual machine + +This process utilizes connecting to the `bastion-vm` in the `{PREFIX}-{ENV}-networks` +to execute the commands. + +1. Within Cloud Console, go to the [Compute Engine Instances](http://console.cloud.google.com/compute/instances) + page and select the project `{PREFIX}-{ENV}-networks` at the top of the page. +1. Connect to the instance with SSH using the SSH button next to the instance + named `bastion-vm`. + +### Install or confirm prior installation of dependencies + +1. Install OpenJDK + + ``` + wget https://download.java.net/java/GA/jdk13.0.1/cec27d702aa74d5a8630c65ae61e4305/9/GPL/openjdk-13.0.1_linux-x64_bin.tar.gz + tar -xvf openjdk-13.0.1_linux-x64_bin.tar.gz + mv jdk-13.0.1 /opt/ + ``` + +1. Install Maven + + ``` + wget https://mirrors.estointernet.in/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz + tar -xvf apache-maven-3.6.3-bin.tar.gz + mv apache-maven-3.6.3 /opt/ + ``` + +1. Add Java and Maven to `PATH` + + Add the following lines at the end of the user profile file `~/.profile` using + a text editor + + ``` + M2_HOME='/opt/apache-maven-3.6.3' + JAVA_HOME='/opt/jdk-13.0.1' + PATH="$M2_HOME/bin:$JAVA_HOME/bin:$PATH" + export PATH + ``` + +1. Relaunch terminal + + After editing the `~/.profile` file and saving, exit and relaunch the SSH + connection to apply the configuration changes. + +### Set up Cloud SQL Proxy + +Set up [Cloud SQL Proxy](https://cloud.google.com/sql/docs/mysql/sql-proxy) to +be able to connect to the Cloud SQL instance using the private IP within the +VPC without the need for Authorized networks or for configuring SSL. + +1. Install Cloud SQL Proxy + + ``` + wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy + chmod +x cloud_sql_proxy + ``` + +1. Authenticate user + + Login as a user with the `Project Owner` role as described above and update + your application default credentials. (When using a Google Compute Engine VM + such as `bastion-vm` you must update the application default credentials, + otherwise requests will continue to be made with its default service account). + Remember to log your user account out once your deployment is complete. + + ``` + gcloud auth login --update-adc + ``` + +1. Authenticate the Cloud SQL Proxy + + Authenticate the Cloud SQL Proxy, replacing the variables below with the + specifics for your deployment. If you are unsure of the region, you can + check by going to the [Cloud SQL Instances](https://console.cloud.google.com/sql/instances) + page in Cloud Console and selecting the project `{PREFIX}-{ENV}-data` at the + top of the page. You will see the full path in the column: + `Instance connection name` + + ``` + ./cloud_sql_proxy -instances=<{PREFIX}-{ENV}-data:{REGION}:mystudies>=tcp:3306 + ``` + +### Data Migration Script Execution Setup: + +1. Open another SSH connection terminal to `bastion-vm` through SSH and perform + the user authentication and application default credentials as above, for + example: + + ``` + gcloud auth login --update-adc + ``` + +1. Clone your repository + + Clone the repository and branch with the new version of FDA MyStudies. You + will need to authenticate your Git user if you are accessing a private + repository. In the following command, replace the variables with the + specifics for your deployment, for example: + + ``` + git clone -b <> https://github.com// + ``` + +1. Set environment variables + + Edit `/deployment/scripts/set_env_var.sh` to set the variables for + your deployment using a text editor. Then run this script to set your + environment variables, for example: + + ``` + source set_env_var.sh # executed from your /deployment/scripts directory + ``` + +1. Set database credentials + + Save the database credentials to environment variables for the migration. + You can either find these values in Cloud Console [Secret Manager](https://console.cloud.google.com/security/secret-manager) + in the `{PREFIX}-{ENV}-secrets` project, or retrieve them with `gcloud`, for + example: + + ``` + gcloud config set project $PREFIX-$ENV-secrets + export DB_USER=$( \ + gcloud secrets versions access latest --secret="auto-auth-server-db-user") + export DB_PASS=$( \ + gcloud secrets versions access latest --secret="auto-auth-server-db-password") + ``` + +### Run Data Migration Scripts + +To migrate the data, run each of the following for each of the databases +requiring migration. + +#### Auth Server Database +``` +cd $GIT_ROOT/db-migration/auth-server-db-migration/ +mvn clean flyway:migrate -Dflyway.user=${DB_USER} -Dflyway.configFiles=flyway.properties -Dflyway.password=${DB_PASS} -Dflyway.url=jdbc:mysql://127.0.0.1:3306/ +``` + +#### Participant Datastore Database +``` +cd $GIT_ROOT/db-migration/participant-datastore-db-migration/ +mvn clean flyway:migrate -Dflyway.user=${DB_USER} -Dflyway.configFiles=flyway.properties -Dflyway.password=${DB_PASS} -Dflyway.url=jdbc:mysql://127.0.0.1:3306/ +``` + +#### Response Datastore database +``` +cd $GIT_ROOT/db-migration/response-datastore-db-migration/ +mvn clean flyway:migrate -Dflyway.user=${DB_USER} -Dflyway.configFiles=flyway.properties -Dflyway.password=${DB_PASS} -Dflyway.url=jdbc:mysql://127.0.0.1:3306/ +``` + +#### Study Builderdatabase +``` +cd $GIT_ROOT/db-migration/study-builder-db-migration/ +mvn clean flyway:migrate -Dflyway.user=${DB_USER} -Dflyway.configFiles=flyway.properties -Dflyway.password=${DB_PASS} -Dflyway.url=jdbc:mysql://127.0.0.1:3306/ +``` diff --git a/db-migration/auth-server-db-migration/.gitignore b/db-migration/auth-server-db-migration/.gitignore new file mode 100644 index 0000000000..3a568a5028 --- /dev/null +++ b/db-migration/auth-server-db-migration/.gitignore @@ -0,0 +1,30 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/db-migration/auth-server-db-migration/db/migration/V2_0_3__release.sql b/db-migration/auth-server-db-migration/db/migration/V2_0_3__release.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/db-migration/auth-server-db-migration/flyway.properties b/db-migration/auth-server-db-migration/flyway.properties new file mode 100644 index 0000000000..4e5f203f71 --- /dev/null +++ b/db-migration/auth-server-db-migration/flyway.properties @@ -0,0 +1,11 @@ +# Data Migration using Flyway +# mvn clean flyway:migrate -Dflyway.configFiles=flyway.properties -Dflyway.user=DB_USER -Dflyway.password=DB_PASSWORD -Dflyway.url=DB_URL +flyway.user=XXXX +flyway.password=XXXX +flyway.url=jdbc:mysql://localhost:3306/DB_NAME +flyway.locations=filesystem:db/migration +flyway.driver=com.mysql.cj.jdbc.Driver + +# Found non-empty schema(s) without schema history table! Use baseline() or set baselineOnMigrate to true to initialize the schema history table. +# Version should be greater than V1_0_0 to run the migration scripts. +flyway.baselineOnMigrate=true diff --git a/db-migration/auth-server-db-migration/pom.xml b/db-migration/auth-server-db-migration/pom.xml new file mode 100644 index 0000000000..e3d89146ec --- /dev/null +++ b/db-migration/auth-server-db-migration/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + com.google.cloud.healthcare.fdamystudies + auth-server-db-migration + 1.0-SNAPSHOT + jar + + auth-server-db-migration + http://maven.apache.org + Auth Server Database Migration with Flyway + + + UTF-8 + 1.8 + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + mysql + mysql-connector-java + 8.0.23 + + + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + + diff --git a/db-migration/participant-datastore-db-migration/.gitignore b/db-migration/participant-datastore-db-migration/.gitignore new file mode 100644 index 0000000000..3a568a5028 --- /dev/null +++ b/db-migration/participant-datastore-db-migration/.gitignore @@ -0,0 +1,30 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/db-migration/participant-datastore-db-migration/db/migration/V2_0_3__release.sql b/db-migration/participant-datastore-db-migration/db/migration/V2_0_3__release.sql new file mode 100644 index 0000000000..4719aa00de --- /dev/null +++ b/db-migration/participant-datastore-db-migration/db/migration/V2_0_3__release.sql @@ -0,0 +1,3 @@ +/* ISSUE #3115 Emails sent to participants of a specific study (for study invitations) must contain + the support contact email address specific to the study, and as configured in the Study Builder */ +alter table study_info add contact_email varchar(320) DEFAULT NULL; \ No newline at end of file diff --git a/db-migration/participant-datastore-db-migration/flyway.properties b/db-migration/participant-datastore-db-migration/flyway.properties new file mode 100644 index 0000000000..4e5f203f71 --- /dev/null +++ b/db-migration/participant-datastore-db-migration/flyway.properties @@ -0,0 +1,11 @@ +# Data Migration using Flyway +# mvn clean flyway:migrate -Dflyway.configFiles=flyway.properties -Dflyway.user=DB_USER -Dflyway.password=DB_PASSWORD -Dflyway.url=DB_URL +flyway.user=XXXX +flyway.password=XXXX +flyway.url=jdbc:mysql://localhost:3306/DB_NAME +flyway.locations=filesystem:db/migration +flyway.driver=com.mysql.cj.jdbc.Driver + +# Found non-empty schema(s) without schema history table! Use baseline() or set baselineOnMigrate to true to initialize the schema history table. +# Version should be greater than V1_0_0 to run the migration scripts. +flyway.baselineOnMigrate=true diff --git a/db-migration/participant-datastore-db-migration/pom.xml b/db-migration/participant-datastore-db-migration/pom.xml new file mode 100644 index 0000000000..bd1a7c2560 --- /dev/null +++ b/db-migration/participant-datastore-db-migration/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + com.google.cloud.healthcare.fdamystudies + participant-datastore-db-migration + 1.0-SNAPSHOT + jar + + participant-datastore-db-migration + http://maven.apache.org + Participant Datastore Database Migration with Flyway + + + UTF-8 + 1.8 + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + mysql + mysql-connector-java + 8.0.23 + + + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + + diff --git a/db-migration/response-datastore-db-migration/.gitignore b/db-migration/response-datastore-db-migration/.gitignore new file mode 100644 index 0000000000..3a568a5028 --- /dev/null +++ b/db-migration/response-datastore-db-migration/.gitignore @@ -0,0 +1,30 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/db-migration/response-datastore-db-migration/db/migration/V2_0_3__release.sql b/db-migration/response-datastore-db-migration/db/migration/V2_0_3__release.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/db-migration/response-datastore-db-migration/flyway.properties b/db-migration/response-datastore-db-migration/flyway.properties new file mode 100644 index 0000000000..4e5f203f71 --- /dev/null +++ b/db-migration/response-datastore-db-migration/flyway.properties @@ -0,0 +1,11 @@ +# Data Migration using Flyway +# mvn clean flyway:migrate -Dflyway.configFiles=flyway.properties -Dflyway.user=DB_USER -Dflyway.password=DB_PASSWORD -Dflyway.url=DB_URL +flyway.user=XXXX +flyway.password=XXXX +flyway.url=jdbc:mysql://localhost:3306/DB_NAME +flyway.locations=filesystem:db/migration +flyway.driver=com.mysql.cj.jdbc.Driver + +# Found non-empty schema(s) without schema history table! Use baseline() or set baselineOnMigrate to true to initialize the schema history table. +# Version should be greater than V1_0_0 to run the migration scripts. +flyway.baselineOnMigrate=true diff --git a/db-migration/response-datastore-db-migration/pom.xml b/db-migration/response-datastore-db-migration/pom.xml new file mode 100644 index 0000000000..9b2d368488 --- /dev/null +++ b/db-migration/response-datastore-db-migration/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + com.google.cloud.healthcare.fdamystudies + response-datastore-db-migration + 1.0-SNAPSHOT + jar + + response-datastore-db-migration + http://maven.apache.org + Response Datastore Database Migration with Flyway + + + UTF-8 + 1.8 + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + mysql + mysql-connector-java + 8.0.23 + + + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + + diff --git a/db-migration/study-builder-db-migration/.gitignore b/db-migration/study-builder-db-migration/.gitignore new file mode 100644 index 0000000000..3a568a5028 --- /dev/null +++ b/db-migration/study-builder-db-migration/.gitignore @@ -0,0 +1,30 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/db-migration/study-builder-db-migration/db/migration/V2_0_3__release.sql b/db-migration/study-builder-db-migration/db/migration/V2_0_3__release.sql new file mode 100644 index 0000000000..50aa6f161d --- /dev/null +++ b/db-migration/study-builder-db-migration/db/migration/V2_0_3__release.sql @@ -0,0 +1,48 @@ +/* Issue #2929 Standardize use of title case and lower case across screens, + and update text accordingly. */ +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Number of disks' +WHERE display_name = 'Number of Disks'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Puzzle solved/unsolved' +WHERE display_name = 'Puzzle Solved/Unsolved'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Number of moves' +WHERE display_name = 'Number of Moves'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Initial span' +WHERE display_name = 'Initial Span'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Minimum span' +WHERE display_name = 'Minimum Span'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Maximum span' +WHERE display_name = 'Maximum Span'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Play speed' +WHERE display_name = 'Play Speed'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Maximum tests' +WHERE display_name = 'Maximum Tests'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Maximum consecutive failures' +WHERE display_name = 'Maximum Consecutive Failures'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Require reversal' +WHERE display_name = 'Require Reversal'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Number of games' +WHERE display_name = 'Number of Games'; + +UPDATE fda_hphc.active_task_master_attribute SET display_name = 'Number of failures' +WHERE display_name = 'Number of Failures'; + +UPDATE fda_hphc.roles SET role_name = 'Superadmin' +WHERE role_name = 'Project Lead'; + +UPDATE fda_hphc.roles SET role_name = 'Study admin' +WHERE role_name = 'Coordinator'; + +DELETE FROM fda_hphc.user_permission_mapping WHERE permission_id IN ('5','7') AND user_id not IN + (SELECT user_id FROM fda_hphc.users WHERE access_level ='SUPERADMIN'); + +UPDATE fda_hphc.users SET role_id='2', access_level='STUDY ADMIN' WHERE access_level !='SUPERADMIN'; diff --git a/db-migration/study-builder-db-migration/flyway.properties b/db-migration/study-builder-db-migration/flyway.properties new file mode 100644 index 0000000000..4e5f203f71 --- /dev/null +++ b/db-migration/study-builder-db-migration/flyway.properties @@ -0,0 +1,11 @@ +# Data Migration using Flyway +# mvn clean flyway:migrate -Dflyway.configFiles=flyway.properties -Dflyway.user=DB_USER -Dflyway.password=DB_PASSWORD -Dflyway.url=DB_URL +flyway.user=XXXX +flyway.password=XXXX +flyway.url=jdbc:mysql://localhost:3306/DB_NAME +flyway.locations=filesystem:db/migration +flyway.driver=com.mysql.cj.jdbc.Driver + +# Found non-empty schema(s) without schema history table! Use baseline() or set baselineOnMigrate to true to initialize the schema history table. +# Version should be greater than V1_0_0 to run the migration scripts. +flyway.baselineOnMigrate=true diff --git a/db-migration/study-builder-db-migration/pom.xml b/db-migration/study-builder-db-migration/pom.xml new file mode 100644 index 0000000000..bd40b09e30 --- /dev/null +++ b/db-migration/study-builder-db-migration/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + com.google.cloud.healthcare.fdamystudies + study-builder-db-migration + 1.0-SNAPSHOT + jar + + study-builder-db-migration + http://maven.apache.org + Study Builder Database Migration with Flyway + + + UTF-8 + 1.8 + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + mysql + mysql-connector-java + 8.0.23 + + + + + + + org.flywaydb + flyway-maven-plugin + 7.5.0 + + + + diff --git a/deployment/README.md b/deployment/README.md index 0d2d8e0be9..42a6ce1a96 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -484,6 +484,11 @@ app record will appear in the [`Participant manager`](/participant-manager/) use ```bash gcloud auth list ``` +## Maintaining & Updating FDA MyStudies + +### Database Migration + +When updating FDA MyStudies it may be necessary to migrate the databases to support the new version. Detailed instructions can be found in the [Database Migration README](/db-migration/README.md). ***

Copyright 2020 Google LLC

diff --git a/documentation/API/README.md b/documentation/API/README.md new file mode 100644 index 0000000000..18e3f15dcf --- /dev/null +++ b/documentation/API/README.md @@ -0,0 +1,56 @@ + + +## API Documentation + +### Introduction +This guide provides instructions for generating API docs using the Springfox implementation of the Open API (Swagger) specification and using the Springfox Swagger UI or [Swagger Editor](https://editor.swagger.io/) to visually render documentation for an API defined with Open API (Swagger) specification. + +### Springfox Configuration +As mentioned above, we'll use Springfox implementation of the Open API (Swagger) specification. We've added below maven artifacts to the `pom.xml` in `common-service` module. + +* `springfox-swagger2` +* `springfox-swagger-ui` +* `springfox-bean-validators` +* `spring-swagger-simplified` + +The `SwaggerConfig` class is added in `common-service` module to override the default response messages, provide custom information about the API and to add static resources to the ResourceHandlerRegistry object. Please refer to the [Configuration explained](https://springfox.github.io/springfox/docs/current/#configuration-explained) for more details. + +### Springfox Annotations + +Springfox provides following annotations to use on Bean and Controller classes: `@ApiModelProperty`, `@ApiParam`, `@ApiImplicitParam`, `@ApiOperation`, `@RequestParam`, `@RequestHeader`. Please refer to the [Overriding descriptions via properties](https://springfox.github.io/springfox/docs/current/#overriding-descriptions-via-properties) for more details on Springfox annotations and attributes. + +### API Docs + +To verify that Springfox is working, we can visit following URL's in our browser: + +* http[s]://[DOMAIN]/auth-server/api/v2/api-docs +* http[s]://[DOMAIN]/participant-manager-datastore/api/v2/api-docs +* http[s]://[DOMAIN]/participant-consent-datastore/api/v2/api-docs +* http[s]://[DOMAIN]/participant-enroll-datastore/api/v2/api-docs +* http[s]://[DOMAIN]/participant-user-datastore/api/v2/api-docs +* http[s]://[DOMAIN]/response-datastore/api/v2/api-docs + +The result is a JSON response which is not very human readable. Swagger provides Swagger UI to visually render documentation for an API defined with Open API (Swagger) specification. + +The `SwaggerGeneratorTest` class is added in auth-server, response-datastore, participant-manager-datastore, participant-consent-datastore, participant-enroll-datastore and participant-user-datastore modules to generate the `openapi.docs` under `/fda-mystudies/documentation/API/[service-name]/` + +### Springfox Swagger UI + +The `springfox-swagger-ui` web jar ships with Swagger UI. To include it in a standard Spring Boot application we've added this dependency to `pom.xml` in `common-service` module. The Swagger UI page should then be available at + +* http[s]://[DOMAIN]/auth-server/swagger-ui.html +* http[s]://[DOMAIN]/participant-manager-datastore/swagger-ui.html +* http[s]://[DOMAIN]/participant-consent-datastore/swagger-ui.html +* http[s]://[DOMAIN]/participant-enroll-datastore/swagger-ui.html +* http[s]://[DOMAIN]/participant-user-datastore/swagger-ui.html +* http[s]://[DOMAIN]/response-datastore/swagger-ui.html + +### Swagger Editor + +The API docs are stored under `/fda-mystudies/documentation/API/[service-name]/` for offline reference. Please refer to the [Importing OpenAPI documents](https://github.com/swagger-api/swagger-editor/blob/master/docs/import.md#importing-openapi-documents) to import the `openapi.json` file into the [Swagger Editor](https://editor.swagger.io/). + diff --git a/documentation/API/auth-server/openapi.json b/documentation/API/auth-server/openapi.json new file mode 100644 index 0000000000..af15ac2ff9 --- /dev/null +++ b/documentation/API/auth-server/openapi.json @@ -0,0 +1,575 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "1.0", + "title" : "Api Documentation", + "contact" : { }, + "license" : { + "name" : "View License", + "url" : "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt" + } + }, + "host" : "localhost:8080", + "basePath" : "/auth-server", + "tags" : [ { + "name" : "Get Health", + "description" : "Provides an indication about the health of the service" + }, { + "name" : "Get Token", + "description" : "Get access token and refresh token based on grant type" + }, { + "name" : "Users", + "description" : "Operations pertaining to users in auth server" + } ], + "paths" : { + "/healthCheck" : { + "get" : { + "tags" : [ "Get Health" ], + "summary" : "Provides an indication about the health of the service", + "description" : "Default response codes 400 and 401 are not applicable for this operation", + "operationId" : "healthUsingGET", + "produces" : [ "application/json" ], + "responses" : { + "200" : { + "description" : "Service is Up and Running", + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/oauth2/token" : { + "post" : { + "tags" : [ "Get Token" ], + "summary" : "Get access token and refresh token based on grant type. Refer ORY Hydra REST API documentation for request and response details", + "description" : "Refer [The OAuth 2.0 Token Endpoint](https://www.ory.sh/hydra/docs/reference/api/#the-oauth-20-token-endpoint) for request and response details ", + "operationId" : "getTokenUsingPOST", + "consumes" : [ "application/x-www-form-urlencoded" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "paramMap", + "in" : "formData", + "description" : "paramMap", + "required" : true, + "items" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/user/reset_password" : { + "post" : { + "tags" : [ "Users" ], + "summary" : "reset the password and send an email with temporary password", + "operationId" : "resetPasswordUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "appName", + "in" : "header", + "description" : "appName", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "resetPasswordRequest", + "description" : "resetPasswordRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ResetPasswordRequest" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users" : { + "post" : { + "tags" : [ "Users" ], + "summary" : "create a new user", + "operationId" : "createUserUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "userRequest", + "description" : "userRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UserRequest" + } + } ], + "responses" : { + "201" : { + "description" : "Created", + "schema" : { + "$ref" : "#/definitions/UserResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{userId}" : { + "put" : { + "tags" : [ "Users" ], + "summary" : "update user account status", + "operationId" : "updateEmailStatusUsingPUT", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "userRequest", + "description" : "userRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UpdateEmailStatusRequest" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UpdateEmailStatusResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "delete" : { + "tags" : [ "Users" ], + "summary" : "delete a user", + "operationId" : "deleteUserAccountUsingDELETE", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK" + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{userId}/change_password" : { + "put" : { + "tags" : [ "Users" ], + "summary" : "replace old password with new password", + "operationId" : "changePasswordUsingPUT", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "userRequest", + "description" : "userRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ChangePasswordRequest" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{userId}/logout" : { + "post" : { + "tags" : [ "Users" ], + "summary" : "revoke access token and refresh token when user gets logged out", + "operationId" : "logoutUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "Authorization", + "in" : "header", + "description" : "Authorization", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UserResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + } + }, + "definitions" : { + "BaseResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "BaseResponse" + }, + "ChangePasswordRequest" : { + "type" : "object", + "properties" : { + "currentPassword" : { + "type" : "string", + "minLength" : 8, + "maxLength" : 64 + }, + "newPassword" : { + "type" : "string", + "pattern" : "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])(?=\\S+$).{8,64}$" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "ChangePasswordRequest" + }, + "ResetPasswordRequest" : { + "type" : "object", + "required" : [ "appId", "email" ], + "properties" : { + "appId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 100 + }, + "email" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + } + }, + "title" : "ResetPasswordRequest" + }, + "UpdateEmailStatusRequest" : { + "type" : "object", + "properties" : { + "email" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "status" : { + "type" : "integer", + "format" : "int32", + "minimum" : 0.0, + "maximum" : 4.0, + "exclusiveMinimum" : false, + "exclusiveMaximum" : false + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UpdateEmailStatusRequest" + }, + "UpdateEmailStatusResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "tempRegId" : { + "type" : "string" + } + }, + "title" : "UpdateEmailStatusResponse" + }, + "UserRequest" : { + "type" : "object", + "required" : [ "appId", "email", "password", "status" ], + "properties" : { + "appId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 100 + }, + "appName" : { + "type" : "string" + }, + "email" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "password" : { + "type" : "string", + "pattern" : "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])(?=\\S+$).{8,64}$" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "UserRequest" + }, + "UserResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "tempRegId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UserResponse" + }, + "ValidationErrorResponse" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string" + }, + "error_description" : { + "type" : "string" + }, + "error_type" : { + "type" : "string" + }, + "violations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Violation" + } + } + }, + "title" : "ValidationErrorResponse" + }, + "Violation" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + }, + "path" : { + "type" : "string" + } + }, + "title" : "Violation" + } + } +} \ No newline at end of file diff --git a/documentation/API/participant-consent-datastore/openapi.json b/documentation/API/participant-consent-datastore/openapi.json new file mode 100644 index 0000000000..b904efa543 --- /dev/null +++ b/documentation/API/participant-consent-datastore/openapi.json @@ -0,0 +1,354 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "1.0", + "title" : "Api Documentation", + "contact" : { }, + "license" : { + "name" : "View License", + "url" : "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt" + } + }, + "host" : "localhost:8080", + "basePath" : "/participant-consent-datastore", + "tags" : [ { + "name" : "Consent", + "description" : "Operations pertaining to consent document" + }, { + "name" : "Get Health", + "description" : "Provides an indication about the health of the service" + }, { + "name" : "test-pdf-upload", + "description" : "Test PDF Upload" + } ], + "paths" : { + "/consentDocument" : { + "get" : { + "tags" : [ "Consent" ], + "summary" : "Returns a response related to consent document", + "operationId" : "getStudyConsentPdfUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "consentVersion", + "description" : "consentVersion", + "required" : false, + "schema" : { + "type" : "string" + } + }, { + "in" : "body", + "name" : "studyId", + "description" : "studyId", + "required" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/healthCheck" : { + "get" : { + "tags" : [ "Get Health" ], + "summary" : "Provides an indication about the health of the service", + "description" : "Default response codes 400 and 401 are not applicable for this operation", + "operationId" : "healthUsingGET", + "produces" : [ "application/json" ], + "responses" : { + "200" : { + "description" : "Service is Up and Running", + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/testPDFDownload" : { + "get" : { + "tags" : [ "test-pdf-upload" ], + "summary" : "testPDFDownload", + "operationId" : "testPDFDownloadUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "fileName", + "in" : "header", + "description" : "fileName", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/testPDFUpload" : { + "post" : { + "tags" : [ "test-pdf-upload" ], + "summary" : "testPDFUpload", + "operationId" : "testPDFUploadUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "fileName", + "in" : "header", + "description" : "fileName", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/updateEligibilityConsentStatus" : { + "post" : { + "tags" : [ "Consent" ], + "summary" : "Update consent status and store the consent document in Google Cloud Storage (GCS)", + "operationId" : "updateEligibilityConsentStatusUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "consentStatusBean", + "description" : "consentStatusBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ConsentStatusBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + } + }, + "definitions" : { + "BaseResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "BaseResponse" + }, + "ConsentReqBean" : { + "type" : "object", + "required" : [ "pdf", "status", "version" ], + "properties" : { + "pdf" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "version" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + } + }, + "title" : "ConsentReqBean" + }, + "ConsentStatusBean" : { + "type" : "object", + "required" : [ "consent", "siteId", "studyId" ], + "properties" : { + "consent" : { + "$ref" : "#/definitions/ConsentReqBean" + }, + "eligibility" : { + "type" : "boolean" + }, + "sharing" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "siteId" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + } + }, + "title" : "ConsentStatusBean" + }, + "ValidationErrorResponse" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string" + }, + "error_description" : { + "type" : "string" + }, + "error_type" : { + "type" : "string" + }, + "violations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Violation" + } + } + }, + "title" : "ValidationErrorResponse" + }, + "Violation" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + }, + "path" : { + "type" : "string" + } + }, + "title" : "Violation" + } + } +} \ No newline at end of file diff --git a/documentation/API/participant-enroll-datastore/openapi.json b/documentation/API/participant-enroll-datastore/openapi.json new file mode 100644 index 0000000000..f97c13f0a6 --- /dev/null +++ b/documentation/API/participant-enroll-datastore/openapi.json @@ -0,0 +1,482 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "1.0", + "title" : "Api Documentation", + "contact" : { }, + "license" : { + "name" : "View License", + "url" : "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt" + } + }, + "host" : "localhost:8080", + "basePath" : "/participant-enroll-datastore", + "tags" : [ { + "name" : "Enrollment", + "description" : "Operations pertaining to enroll flow in enrollment service" + }, { + "name" : "Get Health", + "description" : "Provides an indication about the health of the service" + }, { + "name" : "Participant Information", + "description" : "Operations pertaining to get participant details" + }, { + "name" : "Participant enrollment details", + "description" : "Operations related to Participant enrollment details " + } ], + "paths" : { + "/enroll" : { + "post" : { + "tags" : [ "Enrollment" ], + "summary" : "Enrolls into a study", + "operationId" : "enrollParticipantUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "enrollmentBean", + "description" : "enrollmentBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/EnrollmentBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/healthCheck" : { + "get" : { + "tags" : [ "Get Health" ], + "summary" : "Provides an indication about the health of the service", + "description" : "Default response codes 400 and 401 are not applicable for this operation", + "operationId" : "healthUsingGET", + "produces" : [ "application/json" ], + "responses" : { + "200" : { + "description" : "Service is Up and Running", + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participantInfo" : { + "get" : { + "tags" : [ "Participant Information" ], + "summary" : "Returns a response containing participant's enrollment details", + "operationId" : "getParticipantDetailsUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "participantId", + "in" : "query", + "description" : "participantId", + "required" : true, + "type" : "string" + }, { + "name" : "studyId", + "in" : "query", + "description" : "studyId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/studyState" : { + "get" : { + "tags" : [ "Participant enrollment details" ], + "summary" : "Returns a response containing participant study information.", + "operationId" : "getStudyStateUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/updateStudyState" : { + "post" : { + "tags" : [ "Participant enrollment details" ], + "summary" : "Updates enrollment status of a participant associated to particular study", + "operationId" : "updateStudyStateUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "studyStateReqBean", + "description" : "studyStateReqBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/StudyStateReqBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/validateEnrollmentToken" : { + "post" : { + "tags" : [ "Enrollment" ], + "summary" : " Validates enrollment token of the participant ", + "operationId" : "validateEnrollmentTokenUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "enrollmentBean", + "description" : "enrollmentBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/EnrollmentBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/withdrawfromstudy" : { + "post" : { + "tags" : [ "Participant enrollment details" ], + "summary" : " Updates participant's enrollment status to withdrawn.", + "operationId" : "withdrawFromStudyUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "withdrawFromStudyBean", + "description" : "withdrawFromStudyBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/WithdrawFromStudyBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + } + }, + "definitions" : { + "BaseResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "BaseResponse" + }, + "EnrollmentBean" : { + "type" : "object", + "required" : [ "studyId" ], + "properties" : { + "studyId" : { + "type" : "string" + }, + "token" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 8 + } + }, + "title" : "EnrollmentBean" + }, + "StudiesBean" : { + "type" : "object", + "properties" : { + "adherence" : { + "type" : "integer", + "format" : "int32" + }, + "bookmarked" : { + "type" : "boolean" + }, + "completion" : { + "type" : "integer", + "format" : "int32" + }, + "enrolledDate" : { + "type" : "string" + }, + "participantId" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "studyId" : { + "type" : "string" + } + }, + "title" : "StudiesBean" + }, + "StudyStateReqBean" : { + "type" : "object", + "required" : [ "studies" ], + "properties" : { + "studies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/StudiesBean" + } + } + }, + "title" : "StudyStateReqBean" + }, + "ValidationErrorResponse" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string" + }, + "error_description" : { + "type" : "string" + }, + "error_type" : { + "type" : "string" + }, + "violations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Violation" + } + } + }, + "title" : "ValidationErrorResponse" + }, + "Violation" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + }, + "path" : { + "type" : "string" + } + }, + "title" : "Violation" + }, + "WithdrawFromStudyBean" : { + "type" : "object", + "required" : [ "participantId", "studyId" ], + "properties" : { + "participantId" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + } + }, + "title" : "WithdrawFromStudyBean" + } + } +} \ No newline at end of file diff --git a/documentation/API/participant-manager-datastore/openapi.json b/documentation/API/participant-manager-datastore/openapi.json new file mode 100644 index 0000000000..0c59a1d421 --- /dev/null +++ b/documentation/API/participant-manager-datastore/openapi.json @@ -0,0 +1,3405 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "1.0", + "title" : "Api Documentation", + "contact" : { }, + "license" : { + "name" : "View License", + "url" : "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt" + } + }, + "host" : "localhost:8080", + "basePath" : "/participant-manager-datastore", + "tags" : [ { + "name" : "Apps", + "description" : "Operations pertaining to apps in participant manager" + }, { + "name" : "Get Consent Document", + "description" : "Operations pertaining to download consent document in participant manager" + }, { + "name" : "Get Health", + "description" : "Provides an indication about the health of the service" + }, { + "name" : "Locations", + "description" : "Operations pertaining to Locations in participant manager" + }, { + "name" : "Sites", + "description" : "Operations pertaining to Sites in participant manager" + }, { + "name" : "Studies", + "description" : "Operations pertaining to Studies in participant manager" + }, { + "name" : "User Profile", + "description" : "Operations pertaining to user profile in participant manager" + }, { + "name" : "Users", + "description" : "Operations pertaining to Users in participant manager" + } ], + "paths" : { + "/apps" : { + "get" : { + "tags" : [ "Apps" ], + "summary" : "Returns a list of apps based on permissions", + "operationId" : "getAppsUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "fields", + "in" : "query", + "description" : "fields", + "required" : false, + "type" : "array", + "items" : { + "type" : "string" + }, + "collectionFormat" : "multi" + }, { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/AppResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/apps/{appId}/participants" : { + "get" : { + "tags" : [ "Apps" ], + "summary" : "Returns a response of app registrants with enrolled studies and sites", + "operationId" : "getAppParticipantsUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "appId", + "in" : "path", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "sortBy", + "in" : "query", + "description" : "sortBy", + "required" : false, + "type" : "string", + "default" : "email" + }, { + "name" : "sortDirection", + "in" : "query", + "description" : "sortDirection", + "required" : false, + "type" : "string", + "default" : "asc" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/AppParticipantsResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/consents/{consentId}/consentDocument" : { + "get" : { + "tags" : [ "Get Consent Document" ], + "summary" : "Returns a response of encoded base 64 consent document", + "operationId" : "getConsentDocumentUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "consentId", + "in" : "path", + "description" : "consentId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ConsentDocumentResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/healthCheck" : { + "get" : { + "tags" : [ "Get Health" ], + "summary" : "Provides an indication about the health of the service", + "description" : "Default response codes 400 and 401 are not applicable for this operation", + "operationId" : "healthUsingGET", + "produces" : [ "application/json" ], + "responses" : { + "200" : { + "description" : "Service is Up and Running", + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/locations" : { + "get" : { + "tags" : [ "Locations" ], + "summary" : "Returns a list of location details", + "operationId" : "getLocationsUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "excludeStudyId", + "in" : "query", + "description" : "excludeStudyId", + "required" : false, + "type" : "string" + }, { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "sortBy", + "in" : "query", + "description" : "sortBy", + "required" : false, + "type" : "string", + "default" : "locationId" + }, { + "name" : "sortDirection", + "in" : "query", + "description" : "sortDirection", + "required" : false, + "type" : "string", + "default" : "asc" + }, { + "name" : "status", + "in" : "query", + "description" : "status", + "required" : false, + "type" : "integer", + "format" : "int32" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/LocationResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "post" : { + "tags" : [ "Locations" ], + "summary" : "Valid data submitted to this resource will create a new location", + "operationId" : "addNewLocationUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "locationRequest", + "description" : "locationRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/LocationRequest" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "201" : { + "description" : "Created", + "schema" : { + "$ref" : "#/definitions/LocationDetailsResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/locations/{locationId}" : { + "get" : { + "tags" : [ "Locations" ], + "summary" : "Returns a response containing location based on location id", + "operationId" : "getLocationByIdUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "locationId", + "in" : "path", + "description" : "locationId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/LocationDetailsResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "put" : { + "tags" : [ "Locations" ], + "summary" : "Updates existing location", + "operationId" : "updateLocationUsingPUT", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "locationId", + "in" : "path", + "description" : "locationId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "locationRequest", + "description" : "locationRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UpdateLocationRequest" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/LocationDetailsResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites" : { + "get" : { + "tags" : [ "Sites" ], + "summary" : "Returns a response containing list of sites based on permission", + "operationId" : "getSitesUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/SiteDetailsResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "post" : { + "tags" : [ "Sites" ], + "summary" : "Creates a new site for a study based on permission", + "operationId" : "addNewSiteUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "siteRequest", + "description" : "siteRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/SiteRequest" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/SiteResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites/{participantRegistrySiteId}/participant" : { + "get" : { + "tags" : [ "Sites" ], + "summary" : "Returns participant details with enrollment history", + "operationId" : "getParticipantDetailsUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "format" : "int32" + }, { + "name" : "page", + "in" : "query", + "description" : "page", + "required" : false, + "type" : "integer", + "format" : "int32" + }, { + "name" : "participantRegistrySiteId", + "in" : "path", + "description" : "participantRegistrySiteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ParticipantDetailResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites/{siteId}/decommission" : { + "put" : { + "tags" : [ "Sites" ], + "summary" : "Activate/Deactivate's site for a study", + "operationId" : "decomissionSiteUsingPUT", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "siteId", + "in" : "path", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/SiteStatusResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites/{siteId}/participants" : { + "get" : { + "tags" : [ "Sites" ], + "summary" : "Returns a list of site participants with onboarding status", + "operationId" : "getSiteParticipantUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "format" : "int32" + }, { + "name" : "onboardingStatus", + "in" : "query", + "description" : "onboardingStatus", + "required" : false, + "type" : "string" + }, { + "name" : "page", + "in" : "query", + "description" : "page", + "required" : false, + "type" : "integer", + "format" : "int32" + }, { + "name" : "siteId", + "in" : "path", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ParticipantRegistryResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "post" : { + "tags" : [ "Sites" ], + "summary" : "Add a new participant for a site", + "operationId" : "addNewParticipantUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "participant", + "description" : "participant", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ParticipantDetailRequest" + } + }, { + "name" : "siteId", + "in" : "path", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ParticipantResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites/{siteId}/participants/import" : { + "post" : { + "tags" : [ "Sites" ], + "summary" : "Imports participants from a file", + "operationId" : "importParticipantsUsingPOST", + "consumes" : [ "multipart/form-data" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "file", + "in" : "formData", + "description" : "file", + "required" : true, + "type" : "file" + }, { + "name" : "siteId", + "in" : "path", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ImportParticipantResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites/{siteId}/participants/invite" : { + "post" : { + "tags" : [ "Sites" ], + "summary" : "Send/Resend's invitation to participants", + "operationId" : "inviteParticipantsUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "inviteParticipantRequest", + "description" : "inviteParticipantRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/InviteParticipantRequest" + } + }, { + "name" : "siteId", + "in" : "path", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/InviteParticipantResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/sites/{siteId}/participants/status" : { + "patch" : { + "tags" : [ "Sites" ], + "summary" : "Updates onbording status for a participant", + "operationId" : "updateOnboardingStatusUsingPATCH", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "participantStatusRequest", + "description" : "participantStatusRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ParticipantStatusRequest" + } + }, { + "name" : "siteId", + "in" : "path", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ParticipantStatusResponse" + } + } + }, + "deprecated" : false + } + }, + "/studies" : { + "get" : { + "tags" : [ "Studies" ], + "summary" : "Return a list study details", + "operationId" : "getStudiesUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/StudyResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/studies/{studyId}/participants" : { + "get" : { + "tags" : [ "Studies" ], + "summary" : "Returns a response containing study participant details", + "operationId" : "getStudyParticipantsUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "excludeParticipantStudyStatus", + "in" : "query", + "description" : "excludeParticipantStudyStatus", + "required" : false, + "type" : "array", + "items" : { + "type" : "string" + }, + "collectionFormat" : "multi" + }, { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "sortBy", + "in" : "query", + "description" : "sortBy", + "required" : false, + "type" : "string", + "default" : "email" + }, { + "name" : "sortDirection", + "in" : "query", + "description" : "sortDirection", + "required" : false, + "type" : "string", + "default" : "asc" + }, { + "name" : "studyId", + "in" : "path", + "description" : "studyId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/ParticipantRegistryResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/studies/{studyId}/targetEnrollment" : { + "patch" : { + "tags" : [ "Studies" ], + "summary" : "Updates target enrollment value for the study", + "operationId" : "updateTargetEnrollmentUsingPATCH", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "studyId", + "in" : "path", + "description" : "studyId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "targetEnrollmentRequest", + "description" : "targetEnrollmentRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UpdateTargetEnrollmentRequest" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UpdateTargetEnrollmentResponse" + } + } + }, + "deprecated" : false + } + }, + "/users" : { + "get" : { + "tags" : [ "Users" ], + "summary" : "Returns list of all admin details", + "operationId" : "getUsersUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "limit", + "in" : "query", + "description" : "limit", + "required" : false, + "type" : "integer", + "default" : 10, + "format" : "int32" + }, { + "name" : "offset", + "in" : "query", + "description" : "offset", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + }, { + "name" : "searchTerm", + "in" : "query", + "description" : "searchTerm", + "required" : false, + "type" : "string" + }, { + "name" : "sortBy", + "in" : "query", + "description" : "sortBy", + "required" : false, + "type" : "string", + "default" : "firstName" + }, { + "name" : "sortDirection", + "in" : "query", + "description" : "sortDirection", + "required" : false, + "type" : "string", + "default" : "asc" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/GetUsersResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "post" : { + "tags" : [ "Users" ], + "summary" : "Add new admin with permissions by sending an email", + "operationId" : "addNewUserSiteCoordinatorUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "user", + "description" : "user", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UserRequest" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/AdminUserResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/admin/{adminId}" : { + "get" : { + "tags" : [ "Users" ], + "summary" : "Returns a response containing particular admin detail", + "operationId" : "getAdminDetailsAndAppsUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "adminId", + "in" : "path", + "description" : "adminId", + "required" : true, + "type" : "string" + }, { + "name" : "includeUnselected", + "in" : "query", + "description" : "includeUnselected", + "required" : false, + "type" : "boolean" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/GetAdminDetailsResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/securitycodes/{securityCode}" : { + "get" : { + "tags" : [ "User Profile" ], + "summary" : "Returns a response containing user profile by security code", + "operationId" : "getUserDetailsUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "securityCode", + "in" : "path", + "description" : "securityCode", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UserProfileResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/setUpAccount" : { + "post" : { + "tags" : [ "User Profile" ], + "summary" : "Set up an account by activating user after registration", + "operationId" : "setUpAccountUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "setUpAccountRequest", + "description" : "setUpAccountRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/SetUpAccountRequest" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/SetUpAccountResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{adminUserId}/" : { + "put" : { + "tags" : [ "Users" ], + "summary" : "Updates admin with permissions by sending an email", + "operationId" : "updateUserSiteCoordinatorUsingPUT", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "adminUserId", + "in" : "path", + "description" : "adminUserId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "user", + "description" : "user", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UserRequest" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/AdminUserResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{userId}" : { + "get" : { + "tags" : [ "User Profile" ], + "summary" : "Returns user profile information", + "operationId" : "getUserProfileUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UserProfileResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "delete" : { + "tags" : [ "User Profile" ], + "summary" : "Delete invitation of the user", + "operationId" : "deleteInvitationUsingDELETE", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + }, + "patch" : { + "tags" : [ "User Profile" ], + "summary" : "Updates user account status", + "operationId" : "updateUserAccountStatusUsingPATCH", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "userRequest", + "description" : "userRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/PatchUserRequest" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/PatchUserResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{userId}/invite" : { + "post" : { + "tags" : [ "Users" ], + "summary" : "Resends invitation email to the admin", + "operationId" : "sendInvitationUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/AdminUserResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/users/{userId}/profile" : { + "put" : { + "tags" : [ "User Profile" ], + "summary" : "Updates user account details", + "operationId" : "updateUserProfileUsingPUT", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "userId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "userProfileRequest", + "description" : "userProfileRequest", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UserProfileRequest" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UserProfileResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + } + }, + "definitions" : { + "AdminUserResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "AdminUserResponse" + }, + "AppDetails" : { + "type" : "object", + "properties" : { + "appUsersCount" : { + "type" : "integer", + "format" : "int64" + }, + "customId" : { + "type" : "string" + }, + "enrolledCount" : { + "type" : "integer", + "format" : "int64" + }, + "enrollmentPercentage" : { + "type" : "number", + "format" : "double" + }, + "id" : { + "type" : "string" + }, + "invitedCount" : { + "type" : "integer", + "format" : "int64" + }, + "name" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "studies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AppStudyResponse" + } + }, + "studiesCount" : { + "type" : "integer", + "format" : "int64" + }, + "totalSitesCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "AppDetails" + }, + "AppParticipantsResponse" : { + "type" : "object", + "properties" : { + "appId" : { + "type" : "string" + }, + "code" : { + "type" : "string" + }, + "customId" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "participants" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/ParticipantDetail" + } + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "totalParticipantCount" : { + "type" : "integer", + "format" : "int64" + } + }, + "title" : "AppParticipantsResponse" + }, + "AppResponse" : { + "type" : "object", + "properties" : { + "apps" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AppDetails" + } + }, + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "studyPermissionCount" : { + "type" : "integer", + "format" : "int64" + }, + "superAdmin" : { + "type" : "boolean" + } + }, + "title" : "AppResponse" + }, + "AppSiteDetails" : { + "type" : "object", + "properties" : { + "customLocationId" : { + "type" : "string" + }, + "enrollmentDate" : { + "type" : "string" + }, + "locationName" : { + "type" : "string" + }, + "participantStudyStatus" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "withdrawlDate" : { + "type" : "string" + } + }, + "title" : "AppSiteDetails" + }, + "AppSiteResponse" : { + "type" : "object", + "properties" : { + "customLocationId" : { + "type" : "string" + }, + "locationDescription" : { + "type" : "string" + }, + "locationId" : { + "type" : "string" + }, + "locationName" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + } + }, + "title" : "AppSiteResponse" + }, + "AppStudyDetails" : { + "type" : "object", + "properties" : { + "customStudyId" : { + "type" : "string" + }, + "sites" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AppSiteDetails" + } + }, + "studyId" : { + "type" : "string" + }, + "studyName" : { + "type" : "string" + }, + "studyType" : { + "type" : "string" + } + }, + "title" : "AppStudyDetails" + }, + "AppStudyResponse" : { + "type" : "object", + "properties" : { + "customStudyId" : { + "type" : "string" + }, + "sites" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AppSiteResponse" + } + }, + "studyId" : { + "type" : "string" + }, + "studyName" : { + "type" : "string" + }, + "totalSitesCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "AppStudyResponse" + }, + "BaseResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "BaseResponse" + }, + "ConsentDocumentResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "content" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "type" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + }, + "title" : "ConsentDocumentResponse" + }, + "ConsentHistory" : { + "type" : "object", + "properties" : { + "consentDocumentPath" : { + "type" : "string" + }, + "consentVersion" : { + "type" : "string" + }, + "consentedDate" : { + "type" : "string" + }, + "dataSharingPermissions" : { + "type" : "string" + }, + "id" : { + "type" : "string" + } + }, + "title" : "ConsentHistory" + }, + "Enrollment" : { + "type" : "object", + "properties" : { + "enrollmentDate" : { + "type" : "string" + }, + "enrollmentStatus" : { + "type" : "string" + }, + "participantId" : { + "type" : "string" + }, + "withdrawalDate" : { + "type" : "string" + } + }, + "title" : "Enrollment" + }, + "GetAdminDetailsResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "user" : { + "$ref" : "#/definitions/User" + } + }, + "title" : "GetAdminDetailsResponse" + }, + "GetUsersResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "totalUsersCount" : { + "type" : "integer", + "format" : "int64" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/User" + } + } + }, + "title" : "GetUsersResponse" + }, + "ImportParticipantResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "duplicateEmails" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "invalidEmails" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "message" : { + "type" : "string" + }, + "participants" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/ParticipantDetail" + } + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "ImportParticipantResponse" + }, + "InviteParticipantRequest" : { + "type" : "object", + "required" : [ "ids" ], + "properties" : { + "ids" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "siteId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "InviteParticipantRequest" + }, + "InviteParticipantResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "failedParticipantIds" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "invitedParticipantIds" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "InviteParticipantResponse" + }, + "LocationDetails" : { + "type" : "object", + "properties" : { + "customId" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "locationId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "studiesCount" : { + "type" : "integer", + "format" : "int32" + }, + "studyNames" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "userId" : { + "type" : "string" + } + }, + "title" : "LocationDetails" + }, + "LocationDetailsResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "customId" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "locationId" : { + "type" : "string" + }, + "locationPermission" : { + "type" : "integer", + "format" : "int32" + }, + "message" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "studyNames" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "title" : "LocationDetailsResponse" + }, + "LocationRequest" : { + "type" : "object", + "required" : [ "customId", "description", "name" ], + "properties" : { + "customId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 15 + }, + "description" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + }, + "name" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + } + }, + "title" : "LocationRequest" + }, + "LocationResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "locationPermission" : { + "type" : "integer", + "format" : "int32" + }, + "locations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/LocationDetails" + } + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "totalLocationsCount" : { + "type" : "integer", + "format" : "int64" + } + }, + "title" : "LocationResponse" + }, + "ParticipantDetail" : { + "type" : "object", + "properties" : { + "appName" : { + "type" : "string" + }, + "consentHistory" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/ConsentHistory" + } + }, + "customAppId" : { + "type" : "string" + }, + "customLocationId" : { + "type" : "string" + }, + "customStudyId" : { + "type" : "string" + }, + "disabledDate" : { + "type" : "string" + }, + "email" : { + "type" : "string" + }, + "enrolledStudies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AppStudyDetails" + } + }, + "enrollmentDate" : { + "type" : "string" + }, + "enrollmentStatus" : { + "type" : "string" + }, + "enrollments" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Enrollment" + } + }, + "id" : { + "type" : "string" + }, + "invitationDate" : { + "type" : "string" + }, + "invitedDate" : { + "type" : "string" + }, + "locationName" : { + "type" : "string" + }, + "newlyCreatedUser" : { + "type" : "boolean" + }, + "onboardingStatus" : { + "type" : "string" + }, + "participantRegistrySiteid" : { + "type" : "string" + }, + "registrationDate" : { + "type" : "string" + }, + "registrationStatus" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "sitePermission" : { + "type" : "integer", + "format" : "int32" + }, + "siteStatus" : { + "type" : "integer", + "format" : "int32" + }, + "studiesEnrolled" : { + "type" : "string" + }, + "studyName" : { + "type" : "string" + }, + "studyStatus" : { + "type" : "string" + }, + "studyType" : { + "type" : "string" + }, + "userDetailsId" : { + "type" : "string" + }, + "withdrawalDate" : { + "type" : "string" + } + }, + "title" : "ParticipantDetail" + }, + "ParticipantDetailRequest" : { + "type" : "object", + "required" : [ "email" ], + "properties" : { + "email" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "invitedDate" : { + "type" : "string" + }, + "onboardingStatus" : { + "type" : "string" + }, + "participantId" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + } + }, + "title" : "ParticipantDetailRequest" + }, + "ParticipantDetailResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "participantDetails" : { + "$ref" : "#/definitions/ParticipantDetail" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "totalConsentHistoryCount" : { + "type" : "integer", + "format" : "int64" + } + }, + "title" : "ParticipantDetailResponse" + }, + "ParticipantRegistryDetail" : { + "type" : "object", + "properties" : { + "appId" : { + "type" : "string" + }, + "appName" : { + "type" : "string" + }, + "code" : { + "type" : "string" + }, + "countByStatus" : { + "type" : "object", + "additionalProperties" : { + "type" : "integer", + "format" : "int64" + } + }, + "customAppId" : { + "type" : "string" + }, + "customLocationId" : { + "type" : "string" + }, + "customStudyId" : { + "type" : "string" + }, + "locationName" : { + "type" : "string" + }, + "locationStatus" : { + "type" : "integer", + "format" : "int32" + }, + "message" : { + "type" : "string" + }, + "openStudySitePermission" : { + "type" : "integer", + "format" : "int32" + }, + "registryParticipants" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/ParticipantDetail" + } + }, + "siteId" : { + "type" : "string" + }, + "sitePermission" : { + "type" : "integer", + "format" : "int32" + }, + "siteStatus" : { + "type" : "integer", + "format" : "int32" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "studyId" : { + "type" : "string" + }, + "studyName" : { + "type" : "string" + }, + "studyPermission" : { + "type" : "integer", + "format" : "int32" + }, + "studyStatus" : { + "type" : "string" + }, + "studyType" : { + "type" : "string" + }, + "targetEnrollment" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "ParticipantRegistryDetail" + }, + "ParticipantRegistryResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "participantRegistryDetail" : { + "$ref" : "#/definitions/ParticipantRegistryDetail" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "totalParticipantCount" : { + "type" : "integer", + "format" : "int64" + } + }, + "title" : "ParticipantRegistryResponse" + }, + "ParticipantResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "participantId" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "ParticipantResponse" + }, + "ParticipantStatusRequest" : { + "type" : "object", + "required" : [ "ids", "status" ], + "properties" : { + "ids" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "siteId" : { + "type" : "string" + }, + "status" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "ParticipantStatusRequest" + }, + "ParticipantStatusResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "ParticipantStatusResponse" + }, + "PatchUserRequest" : { + "type" : "object", + "required" : [ "status" ], + "properties" : { + "signedInUserId" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "PatchUserRequest" + }, + "PatchUserResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "PatchUserResponse" + }, + "SetUpAccountRequest" : { + "type" : "object", + "required" : [ "email", "firstName", "lastName", "password" ], + "properties" : { + "email" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "firstName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "lastName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "password" : { + "type" : "string", + "minLength" : 8, + "maxLength" : 64, + "pattern" : "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!\\\"#$%&'()*+,-.:;<=>?@\\[\\]^_`{|}~]).{8,64}$" + } + }, + "title" : "SetUpAccountRequest" + }, + "SetUpAccountResponse" : { + "type" : "object", + "properties" : { + "authUserId" : { + "type" : "string" + }, + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "tempRegId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "SetUpAccountResponse" + }, + "SiteDetails" : { + "type" : "object", + "properties" : { + "edit" : { + "type" : "integer", + "format" : "int32" + }, + "enrolled" : { + "type" : "integer", + "format" : "int64" + }, + "enrollmentPercentage" : { + "type" : "number", + "format" : "double" + }, + "id" : { + "type" : "string" + }, + "invited" : { + "type" : "integer", + "format" : "int64" + }, + "name" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "SiteDetails" + }, + "SiteDetailsResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "studies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/StudyDetails" + } + } + }, + "title" : "SiteDetailsResponse" + }, + "SiteRequest" : { + "type" : "object", + "required" : [ "locationId", "studyId" ], + "properties" : { + "locationId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "studyId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "userId" : { + "type" : "string" + } + }, + "title" : "SiteRequest" + }, + "SiteResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "siteName" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "SiteResponse" + }, + "SiteStatusResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "siteStatus" : { + "type" : "integer", + "format" : "int32" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "SiteStatusResponse" + }, + "StudyDetails" : { + "type" : "object", + "properties" : { + "appId" : { + "type" : "string" + }, + "appInfoId" : { + "type" : "string" + }, + "appName" : { + "type" : "string" + }, + "customId" : { + "type" : "string" + }, + "enrolled" : { + "type" : "integer", + "format" : "int64" + }, + "enrollmentPercentage" : { + "type" : "number", + "format" : "double" + }, + "id" : { + "type" : "string" + }, + "invited" : { + "type" : "integer", + "format" : "int64" + }, + "logoImageUrl" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "sites" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/SiteDetails" + } + }, + "sitesCount" : { + "type" : "integer", + "format" : "int64" + }, + "studyPermission" : { + "type" : "integer", + "format" : "int32" + }, + "studyStatus" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "title" : "StudyDetails" + }, + "StudyResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "sitePermissionCount" : { + "type" : "integer", + "format" : "int64" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "studies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/StudyDetails" + } + }, + "superAdmin" : { + "type" : "boolean" + } + }, + "title" : "StudyResponse" + }, + "UpdateLocationRequest" : { + "type" : "object", + "properties" : { + "description" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + }, + "locationId" : { + "type" : "string" + }, + "name" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UpdateLocationRequest" + }, + "UpdateTargetEnrollmentRequest" : { + "type" : "object", + "required" : [ "targetEnrollment" ], + "properties" : { + "studyId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "targetEnrollment" : { + "type" : "integer", + "format" : "int32" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UpdateTargetEnrollmentRequest" + }, + "UpdateTargetEnrollmentResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "UpdateTargetEnrollmentResponse" + }, + "User" : { + "type" : "object", + "properties" : { + "apps" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/UserAppDetails" + } + }, + "email" : { + "type" : "string" + }, + "firstName" : { + "type" : "string" + }, + "id" : { + "type" : "string" + }, + "lastName" : { + "type" : "string" + }, + "manageLocations" : { + "type" : "integer", + "format" : "int32" + }, + "status" : { + "type" : "string" + }, + "superAdmin" : { + "type" : "boolean" + } + }, + "title" : "User" + }, + "UserAppDetails" : { + "type" : "object", + "properties" : { + "customId" : { + "type" : "string" + }, + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "selected" : { + "type" : "boolean" + }, + "selectedSitesCount" : { + "type" : "integer", + "format" : "int32" + }, + "selectedStudiesCount" : { + "type" : "integer", + "format" : "int32" + }, + "studies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/UserStudyDetails" + } + }, + "totalSitesCount" : { + "type" : "integer", + "format" : "int32" + }, + "totalStudiesCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "UserAppDetails" + }, + "UserAppPermissionRequest" : { + "type" : "object", + "properties" : { + "appUsersCount" : { + "type" : "integer", + "format" : "int32" + }, + "customId" : { + "type" : "string" + }, + "enrolledCount" : { + "type" : "integer", + "format" : "int32" + }, + "enrollmentPercentage" : { + "type" : "integer", + "format" : "int32" + }, + "id" : { + "type" : "string" + }, + "invitedCount" : { + "type" : "integer", + "format" : "int32" + }, + "name" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "selected" : { + "type" : "boolean" + }, + "selectedSitesCount" : { + "type" : "integer", + "format" : "int32" + }, + "studies" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/UserStudyPermissionRequest" + } + }, + "studiesCount" : { + "type" : "integer", + "format" : "int32" + }, + "totalSitesCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "UserAppPermissionRequest" + }, + "UserProfileRequest" : { + "type" : "object", + "required" : [ "firstName", "lastName" ], + "properties" : { + "firstName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + }, + "lastName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UserProfileRequest" + }, + "UserProfileResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "email" : { + "type" : "string" + }, + "firstName" : { + "type" : "string" + }, + "lastName" : { + "type" : "string" + }, + "manageLocations" : { + "type" : "integer", + "format" : "int32" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "superAdmin" : { + "type" : "boolean" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UserProfileResponse" + }, + "UserRequest" : { + "type" : "object", + "required" : [ "email", "firstName", "lastName", "superAdmin" ], + "properties" : { + "apps" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/UserAppPermissionRequest" + } + }, + "email" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "firstName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 200 + }, + "id" : { + "type" : "string" + }, + "lastName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 200 + }, + "manageLocations" : { + "type" : "integer", + "format" : "int32", + "minimum" : 0.0, + "maximum" : 1.0, + "exclusiveMinimum" : false, + "exclusiveMaximum" : false + }, + "signedInUserId" : { + "type" : "string" + }, + "superAdmin" : { + "type" : "boolean" + }, + "superAdminUserId" : { + "type" : "string" + } + }, + "title" : "UserRequest" + }, + "UserSiteDetails" : { + "type" : "object", + "properties" : { + "customLocationId" : { + "type" : "string" + }, + "locationDescription" : { + "type" : "string" + }, + "locationId" : { + "type" : "string" + }, + "locationName" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "selected" : { + "type" : "boolean" + }, + "siteId" : { + "type" : "string" + } + }, + "title" : "UserSiteDetails" + }, + "UserSitePermissionRequest" : { + "type" : "object", + "properties" : { + "customLocationId" : { + "type" : "string" + }, + "locationDescription" : { + "type" : "string" + }, + "locationId" : { + "type" : "string" + }, + "locationName" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "selected" : { + "type" : "boolean" + }, + "siteId" : { + "type" : "string" + } + }, + "title" : "UserSitePermissionRequest" + }, + "UserStudyDetails" : { + "type" : "object", + "properties" : { + "customStudyId" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "selected" : { + "type" : "boolean" + }, + "selectedSitesCount" : { + "type" : "integer", + "format" : "int32" + }, + "sites" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/UserSiteDetails" + } + }, + "studyId" : { + "type" : "string" + }, + "studyName" : { + "type" : "string" + }, + "totalSitesCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "UserStudyDetails" + }, + "UserStudyPermissionRequest" : { + "type" : "object", + "properties" : { + "customStudyId" : { + "type" : "string" + }, + "permission" : { + "type" : "integer", + "format" : "int32" + }, + "selected" : { + "type" : "boolean" + }, + "selectedSitesCount" : { + "type" : "integer", + "format" : "int32" + }, + "sites" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/UserSitePermissionRequest" + } + }, + "studyId" : { + "type" : "string" + }, + "studyName" : { + "type" : "string" + }, + "totalSitesCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "UserStudyPermissionRequest" + }, + "ValidationErrorResponse" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string" + }, + "error_description" : { + "type" : "string" + }, + "error_type" : { + "type" : "string" + }, + "violations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Violation" + } + } + }, + "title" : "ValidationErrorResponse" + }, + "Violation" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + }, + "path" : { + "type" : "string" + } + }, + "title" : "Violation" + } + } +} \ No newline at end of file diff --git a/documentation/API/participant-user-datastore/openapi.json b/documentation/API/participant-user-datastore/openapi.json new file mode 100644 index 0000000000..eb011d1c41 --- /dev/null +++ b/documentation/API/participant-user-datastore/openapi.json @@ -0,0 +1,996 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "1.0", + "title" : "Api Documentation", + "contact" : { }, + "license" : { + "name" : "View License", + "url" : "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt" + } + }, + "host" : "localhost:8080", + "basePath" : "/participant-user-datastore", + "tags" : [ { + "name" : "Get Health", + "description" : "Provides an indication about the health of the service" + }, { + "name" : "Studies", + "description" : "Operations pertaining to Studies in user management service" + }, { + "name" : "Support", + "description" : "Operations pertaining to support in user management service" + }, { + "name" : "User Profile", + "description" : "Operations pertaining to user profile in user management service" + }, { + "name" : "User Registration", + "description" : "Operations pertaining to register the user in user management service" + }, { + "name" : "Verify Email", + "description" : "Operation pertaining to verify email in user management service" + } ], + "paths" : { + "/contactUs" : { + "post" : { + "tags" : [ "Support" ], + "summary" : "Triggers sending of 'Contact Us' e-mail with data submitted with the request", + "operationId" : "contactUsDetailsUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "appName", + "in" : "header", + "description" : "appName", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "reqBean", + "description" : "reqBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ContactUsReqBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/deactivate" : { + "delete" : { + "tags" : [ "User Profile" ], + "summary" : "Deactivate the user", + "operationId" : "deactivateAccountUsingDELETE", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "deactivateAcctBean", + "description" : "deactivateAcctBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/DeactivateAcctBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/feedback" : { + "post" : { + "tags" : [ "Support" ], + "summary" : "Triggers sending of 'Feedback' e-mail with data submitted with the request", + "operationId" : "feedbackDetailsUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "appName", + "in" : "header", + "description" : "appName", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "reqBean", + "description" : "reqBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/FeedbackReqBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/healthCheck" : { + "get" : { + "tags" : [ "Get Health" ], + "summary" : "Provides an indication about the health of the service", + "description" : "Default response codes 400 and 401 are not applicable for this operation", + "operationId" : "healthUsingGET", + "produces" : [ "application/json" ], + "responses" : { + "200" : { + "description" : "Service is Up and Running", + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/register" : { + "post" : { + "tags" : [ "User Registration" ], + "summary" : "Register the new user", + "operationId" : "registerUserUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "appId", + "in" : "header", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "name" : "appName", + "in" : "header", + "description" : "appName", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "user", + "description" : "user", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UserRegistrationForm" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "$ref" : "#/definitions/UserRegistrationResponse" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/resendConfirmation" : { + "post" : { + "tags" : [ "User Profile" ], + "summary" : "Resend confirmation to the user via email", + "operationId" : "resendConfirmationUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "appId", + "in" : "header", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "name" : "appName", + "in" : "header", + "description" : "appName", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "resetPasswordBean", + "description" : "resetPasswordBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ResetPasswordBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/studies/sendNotification" : { + "post" : { + "tags" : [ "Studies" ], + "summary" : "Sends notifications to users", + "operationId" : "SendNotificationUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "notificationForm", + "description" : "notificationForm", + "required" : true, + "schema" : { + "$ref" : "#/definitions/NotificationForm" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/studies/studymetadata" : { + "post" : { + "tags" : [ "Studies" ], + "summary" : "Add or update metadata details of the study", + "operationId" : "addUpdateStudyMetadataUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "studyMetadataBean", + "description" : "studyMetadataBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/StudyMetadataBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/updateUserProfile" : { + "post" : { + "tags" : [ "User Profile" ], + "summary" : "Updates the profile of the currently logged in user.", + "operationId" : "updateUserProfileUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "user", + "description" : "user", + "required" : true, + "schema" : { + "$ref" : "#/definitions/UserRequestBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/userProfile" : { + "get" : { + "tags" : [ "User Profile" ], + "summary" : "Returns a response containing user profile information.", + "operationId" : "getUserProfileUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/verifyEmailId" : { + "post" : { + "tags" : [ "Verify Email" ], + "summary" : "Email verification based on the code", + "operationId" : "verifyEmailIdUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "appId", + "in" : "header", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "verificationForm", + "description" : "verificationForm", + "required" : true, + "schema" : { + "$ref" : "#/definitions/EmailIdVerificationForm" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + } + }, + "definitions" : { + "BaseResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "BaseResponse" + }, + "ContactUsReqBean" : { + "type" : "object", + "required" : [ "body", "email", "firstName", "subject" ], + "properties" : { + "appName" : { + "type" : "string" + }, + "body" : { + "type" : "string" + }, + "email" : { + "type" : "string" + }, + "firstName" : { + "type" : "string" + }, + "subject" : { + "type" : "string" + } + }, + "title" : "ContactUsReqBean" + }, + "DeactivateAcctBean" : { + "type" : "object", + "properties" : { + "studyData" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/StudyReqBean" + } + } + }, + "title" : "DeactivateAcctBean" + }, + "EmailIdVerificationForm" : { + "type" : "object", + "required" : [ "code", "emailId" ], + "properties" : { + "code" : { + "type" : "string" + }, + "emailId" : { + "type" : "string" + } + }, + "title" : "EmailIdVerificationForm" + }, + "FeedbackReqBean" : { + "type" : "object", + "required" : [ "body", "subject" ], + "properties" : { + "appName" : { + "type" : "string" + }, + "body" : { + "type" : "string" + }, + "subject" : { + "type" : "string" + } + }, + "title" : "FeedbackReqBean" + }, + "InfoBean" : { + "type" : "object", + "properties" : { + "appVersion" : { + "type" : "string" + }, + "deviceToken" : { + "type" : "string" + }, + "os" : { + "type" : "string" + } + }, + "title" : "InfoBean" + }, + "JSONArray" : { + "type" : "object", + "title" : "JSONArray" + }, + "NotificationBean" : { + "type" : "object", + "properties" : { + "appId" : { + "type" : "string" + }, + "customStudyId" : { + "type" : "string" + }, + "deviceToken" : { + "$ref" : "#/definitions/JSONArray" + }, + "deviceType" : { + "type" : "string" + }, + "notificationId" : { + "type" : "integer", + "format" : "int32" + }, + "notificationSubType" : { + "type" : "string" + }, + "notificationText" : { + "type" : "string" + }, + "notificationTitle" : { + "type" : "string" + }, + "notificationType" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + } + }, + "title" : "NotificationBean" + }, + "NotificationForm" : { + "type" : "object", + "required" : [ "notifications" ], + "properties" : { + "notifications" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/NotificationBean" + } + } + }, + "title" : "NotificationForm" + }, + "ParticipantInfoBean" : { + "type" : "object", + "properties" : { + "enrolledDate" : { + "type" : "string" + }, + "participantId" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + } + }, + "title" : "ParticipantInfoBean" + }, + "ProfileRespBean" : { + "type" : "object", + "properties" : { + "emailId" : { + "type" : "string" + } + }, + "title" : "ProfileRespBean" + }, + "ResetPasswordBean" : { + "type" : "object", + "required" : [ "emailId" ], + "properties" : { + "emailId" : { + "type" : "string" + } + }, + "title" : "ResetPasswordBean" + }, + "SettingsRespBean" : { + "type" : "object", + "properties" : { + "localNotifications" : { + "type" : "boolean" + }, + "locale" : { + "type" : "string" + }, + "passcode" : { + "type" : "boolean" + }, + "reminderLeadTime" : { + "type" : "string" + }, + "remoteNotifications" : { + "type" : "boolean" + }, + "touchId" : { + "type" : "boolean" + } + }, + "title" : "SettingsRespBean" + }, + "StudyMetadataBean" : { + "type" : "object", + "required" : [ "appId", "appName", "contactEmail", "studyId", "studyVersion" ], + "properties" : { + "appDescription" : { + "type" : "string" + }, + "appId" : { + "type" : "string" + }, + "appName" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "contactEmail" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "logoImageUrl" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 255 + }, + "studyCategory" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "studyEnrolling" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 3 + }, + "studyId" : { + "type" : "string" + }, + "studySponsor" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 128 + }, + "studyStatus" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 32 + }, + "studyTagline" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 128 + }, + "studyTitle" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 64 + }, + "studyType" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 32 + }, + "studyVersion" : { + "type" : "string" + } + }, + "title" : "StudyMetadataBean" + }, + "StudyReqBean" : { + "type" : "object", + "properties" : { + "studyId" : { + "type" : "string" + } + }, + "title" : "StudyReqBean" + }, + "UserRegistrationForm" : { + "type" : "object", + "required" : [ "emailId", "password" ], + "properties" : { + "appId" : { + "type" : "string" + }, + "appName" : { + "type" : "string" + }, + "auth" : { + "type" : "string" + }, + "emailId" : { + "type" : "string", + "minLength" : 0, + "maxLength" : 320 + }, + "firstName" : { + "type" : "string" + }, + "lastName" : { + "type" : "string" + }, + "localNotification" : { + "type" : "boolean" + }, + "password" : { + "type" : "string", + "pattern" : "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])(?=\\S+$).{8,64}$" + }, + "reminderFlag" : { + "type" : "boolean" + }, + "remoteNotification" : { + "type" : "boolean" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "tempPassword" : { + "type" : "boolean" + }, + "tempPasswordDate" : { + "type" : "string", + "format" : "date-time" + }, + "touchId" : { + "type" : "boolean" + }, + "usePassCode" : { + "type" : "boolean" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UserRegistrationForm" + }, + "UserRegistrationResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + }, + "tempRegId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + } + }, + "title" : "UserRegistrationResponse" + }, + "UserRequestBean" : { + "type" : "object", + "properties" : { + "info" : { + "$ref" : "#/definitions/InfoBean" + }, + "participantInfo" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/ParticipantInfoBean" + } + }, + "profile" : { + "$ref" : "#/definitions/ProfileRespBean" + }, + "settings" : { + "$ref" : "#/definitions/SettingsRespBean" + } + }, + "title" : "UserRequestBean" + }, + "ValidationErrorResponse" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string" + }, + "error_description" : { + "type" : "string" + }, + "error_type" : { + "type" : "string" + }, + "violations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Violation" + } + } + }, + "title" : "ValidationErrorResponse" + }, + "Violation" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + }, + "path" : { + "type" : "string" + } + }, + "title" : "Violation" + } + } +} \ No newline at end of file diff --git a/documentation/API/response-datastore/openapi.json b/documentation/API/response-datastore/openapi.json new file mode 100644 index 0000000000..03a16b67dd --- /dev/null +++ b/documentation/API/response-datastore/openapi.json @@ -0,0 +1,831 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "1.0", + "title" : "Api Documentation", + "contact" : { }, + "license" : { + "name" : "View License", + "url" : "https://github.com/GoogleCloudPlatform/fda-mystudies/blob/master/LICENSE.txt" + } + }, + "host" : "localhost:8080", + "basePath" : "/response-datastore", + "tags" : [ { + "name" : "Generate participant id", + "description" : "Generate participant id based on study" + }, { + "name" : "Get Health", + "description" : "Provides an indication about the health of the service" + }, { + "name" : "Process activity response", + "description" : "Response activity operation performed" + }, { + "name" : "Process activity state", + "description" : "Activity state operation performed" + }, { + "name" : "Study metadata", + "description" : "Operations pertaining to study metadata, once study is published from study builder" + } ], + "paths" : { + "/healthCheck" : { + "get" : { + "tags" : [ "Get Health" ], + "summary" : "Provides an indication about the health of the service", + "description" : "Default response codes 400 and 401 are not applicable for this operation", + "operationId" : "healthUsingGET", + "produces" : [ "application/json" ], + "responses" : { + "200" : { + "description" : "Service is Up and Running", + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participant/add" : { + "post" : { + "tags" : [ "Generate participant id" ], + "summary" : "Generate participant id from response datastore", + "operationId" : "addParticipantIdentifierUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "appId", + "in" : "header", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "in" : "body", + "name" : "enrollmentTokenIdentifierBean", + "description" : "enrollmentTokenIdentifierBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/EnrollmentTokenIdentifierBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participant/get-activity-state" : { + "get" : { + "tags" : [ "Process activity state" ], + "summary" : "Get activity state", + "operationId" : "getActivityStateUsingGET", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "participantId", + "in" : "query", + "description" : "participantId", + "required" : true, + "type" : "string" + }, { + "name" : "studyId", + "in" : "query", + "description" : "studyId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participant/getresponse" : { + "get" : { + "tags" : [ "Process activity response" ], + "summary" : "Get activity response data for participant from cloud fire store", + "operationId" : "getActivityResponseDataForParticipantUsingGET", + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "activityId", + "in" : "query", + "description" : "activityId", + "required" : true, + "type" : "string" + }, { + "name" : "appId", + "in" : "query", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "name" : "participantId", + "in" : "query", + "description" : "participantId", + "required" : true, + "type" : "string" + }, { + "name" : "questionKey", + "in" : "query", + "description" : "questionKey", + "required" : true, + "type" : "string" + }, { + "name" : "siteId", + "in" : "query", + "description" : "siteId", + "required" : true, + "type" : "string" + }, { + "name" : "studyId", + "in" : "query", + "description" : "studyId", + "required" : true, + "type" : "string" + }, { + "name" : "tokenId", + "in" : "query", + "description" : "tokenId", + "required" : true, + "type" : "string" + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participant/process-response" : { + "post" : { + "tags" : [ "Process activity response" ], + "summary" : "Process activity response for participant and store in cloud fire store", + "operationId" : "processActivityResponseForParticipantUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "questionnaireActivityResponseBean", + "description" : "questionnaireActivityResponseBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ActivityResponseBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participant/update-activity-state" : { + "post" : { + "tags" : [ "Process activity state" ], + "summary" : "Update activity state", + "operationId" : "updateActivityStateUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "activityStateRequestBean", + "description" : "activityStateRequestBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/ActivityStateRequestBean" + } + }, { + "name" : "userId", + "in" : "header", + "description" : "userId", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/participant/withdraw" : { + "post" : { + "tags" : [ "Process activity response" ], + "summary" : "Withdraw participant from study from response datastore", + "operationId" : "withdrawParticipantFromStudyUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "name" : "appId", + "in" : "header", + "description" : "appId", + "required" : true, + "type" : "string" + }, { + "name" : "participantId", + "in" : "query", + "description" : "participantId", + "required" : true, + "type" : "string" + }, { + "name" : "studyId", + "in" : "query", + "description" : "studyId", + "required" : true, + "type" : "string" + }, { + "name" : "studyVersion", + "in" : "query", + "description" : "studyVersion", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + }, + "/studymetadata" : { + "post" : { + "tags" : [ "Study metadata" ], + "summary" : "Add or update study metadata in response datastore when a study is published from study builder", + "operationId" : "addUpdateStudyMetadataUsingPOST", + "consumes" : [ "application/json" ], + "produces" : [ "*/*" ], + "parameters" : [ { + "in" : "body", + "name" : "studyMetadataBean", + "description" : "studyMetadataBean", + "required" : true, + "schema" : { + "$ref" : "#/definitions/StudyMetadataBean" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "schema" : { + "type" : "object" + } + }, + "400" : { + "description" : "Invalid entries found in the submitted form. Please try again.", + "schema" : { + "$ref" : "#/definitions/ValidationErrorResponse" + } + }, + "401" : { + "description" : "Unauthorized or Invalid token", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + }, + "500" : { + "description" : "Sorry, an error has occurred and your request could not be processed. Please try again later.", + "schema" : { + "$ref" : "#/definitions/BaseResponse" + } + } + }, + "deprecated" : false + } + } + }, + "definitions" : { + "ActivityMetadataBean" : { + "type" : "object", + "properties" : { + "activityId" : { + "type" : "string" + }, + "activityRunId" : { + "type" : "string" + }, + "activityType" : { + "type" : "string" + }, + "endDate" : { + "type" : "string" + }, + "lastModified" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "startDate" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + }, + "studyVersion" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + }, + "title" : "ActivityMetadataBean" + }, + "ActivityResponseBean" : { + "type" : "object", + "properties" : { + "applicationId" : { + "type" : "string" + }, + "createdTimestamp" : { + "type" : "string" + }, + "data" : { + "$ref" : "#/definitions/ActivityResponseDataStructureBean" + }, + "metadata" : { + "$ref" : "#/definitions/ActivityMetadataBean" + }, + "participantId" : { + "type" : "string" + }, + "sharingConsent" : { + "type" : "string" + }, + "siteId" : { + "type" : "string" + }, + "tokenIdentifier" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "withdrawalStatus" : { + "type" : "boolean" + } + }, + "title" : "ActivityResponseBean" + }, + "ActivityResponseDataStructureBean" : { + "type" : "object", + "properties" : { + "endTime" : { + "type" : "string" + }, + "resultType" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/QuestionnaireActivityStepsBean" + } + }, + "startTime" : { + "type" : "string" + } + }, + "title" : "ActivityResponseDataStructureBean" + }, + "ActivityRunBean" : { + "type" : "object", + "properties" : { + "completed" : { + "type" : "integer", + "format" : "int32" + }, + "missed" : { + "type" : "integer", + "format" : "int32" + }, + "total" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "ActivityRunBean" + }, + "ActivityStateRequestBean" : { + "type" : "object", + "properties" : { + "activity" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/ParticipantActivityBean" + } + }, + "message" : { + "type" : "string" + }, + "participantId" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + } + }, + "title" : "ActivityStateRequestBean" + }, + "ActivityValueGroupBean" : { + "type" : "object", + "properties" : { + "valueGroup" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/QuestionnaireActivityStepsBean" + } + } + }, + "title" : "ActivityValueGroupBean" + }, + "BaseResponse" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "string" + }, + "message" : { + "type" : "string" + }, + "status" : { + "type" : "integer", + "format" : "int32" + } + }, + "title" : "BaseResponse" + }, + "DestinationBean" : { + "type" : "object", + "properties" : { + "condition" : { + "type" : "string" + }, + "destination" : { + "type" : "string" + }, + "operator" : { + "type" : "string" + } + }, + "title" : "DestinationBean" + }, + "EnrollmentTokenIdentifierBean" : { + "type" : "object", + "properties" : { + "customStudyId" : { + "type" : "string" + }, + "studyVersion" : { + "type" : "string" + }, + "tokenIdentifier" : { + "type" : "string" + } + }, + "title" : "EnrollmentTokenIdentifierBean" + }, + "ParticipantActivityBean" : { + "type" : "object", + "properties" : { + "activityId" : { + "type" : "string" + }, + "activityRun" : { + "$ref" : "#/definitions/ActivityRunBean" + }, + "activityRunId" : { + "type" : "string" + }, + "activityState" : { + "type" : "string" + }, + "activityVersion" : { + "type" : "string" + }, + "bookmarked" : { + "type" : "boolean" + } + }, + "title" : "ParticipantActivityBean" + }, + "QuestionnaireActivityStepsBean" : { + "type" : "object", + "properties" : { + "actvityValueGroup" : { + "$ref" : "#/definitions/ActivityValueGroupBean" + }, + "destinations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/DestinationBean" + } + }, + "endTime" : { + "type" : "string" + }, + "format" : { + "type" : "object" + }, + "groupName" : { + "type" : "string" + }, + "healthDataKey" : { + "type" : "string" + }, + "key" : { + "type" : "string" + }, + "options" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "repeatable" : { + "type" : "boolean" + }, + "repeatableText" : { + "type" : "string" + }, + "resultType" : { + "type" : "string" + }, + "skippable" : { + "type" : "boolean" + }, + "skipped" : { + "type" : "boolean" + }, + "startTime" : { + "type" : "string" + }, + "steps" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/QuestionnaireActivityStepsBean" + } + }, + "text" : { + "type" : "string" + }, + "title" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "object" + } + }, + "title" : "QuestionnaireActivityStepsBean" + }, + "StudyMetadataBean" : { + "type" : "object", + "properties" : { + "appDescription" : { + "type" : "string" + }, + "appId" : { + "type" : "string" + }, + "appName" : { + "type" : "string" + }, + "contactEmail" : { + "type" : "string" + }, + "logoImageUrl" : { + "type" : "string" + }, + "studyCategory" : { + "type" : "string" + }, + "studyEnrolling" : { + "type" : "string" + }, + "studyId" : { + "type" : "string" + }, + "studySponsor" : { + "type" : "string" + }, + "studyStatus" : { + "type" : "string" + }, + "studyTagline" : { + "type" : "string" + }, + "studyTitle" : { + "type" : "string" + }, + "studyType" : { + "type" : "string" + }, + "studyVersion" : { + "type" : "string" + } + }, + "title" : "StudyMetadataBean" + }, + "ValidationErrorResponse" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string" + }, + "error_description" : { + "type" : "string" + }, + "error_type" : { + "type" : "string" + }, + "violations" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Violation" + } + } + }, + "title" : "ValidationErrorResponse" + }, + "Violation" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + }, + "path" : { + "type" : "string" + } + }, + "title" : "Violation" + } + } +} \ No newline at end of file diff --git a/documentation/whats-new.md b/documentation/whats-new.md index baf66c9436..3766c9897f 100644 --- a/documentation/whats-new.md +++ b/documentation/whats-new.md @@ -7,6 +7,13 @@ > Subscribe to [mystudies-announce@googlegroups.com](https://groups.google.com/g/mystudies-announce/) to receive release notifications and announcements +# Release 2.0.3 +* Added [database migration tools](/db-migration/README.md) using Flyway to support upgrades +* Added [API documentation](/documentation/API/README.md) using Swagger libraries +* Bug fixes and UI refinements +* Additional code improvements +* The full list of bugs and features addressed in this release can be viewed [`here`](https://github.com/GoogleCloudPlatform/fda-mystudies/milestone/6?closed=1) + # Release 2.0.2 * Minor enhancements to Study Builder, Participant Manager, Auth Server and mobile apps * Refined automated deployment process including GKE cluster upgrade diff --git a/iOS/MyStudies/MyStudies.xcodeproj/project.pbxproj b/iOS/MyStudies/MyStudies.xcodeproj/project.pbxproj index 873f5619e3..59f34201d5 100644 --- a/iOS/MyStudies/MyStudies.xcodeproj/project.pbxproj +++ b/iOS/MyStudies/MyStudies.xcodeproj/project.pbxproj @@ -425,8 +425,6 @@ 6F4E1B2E24223CA20051D3E2 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; 6F55B9B9250225AC00A052A9 /* AuthRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRouter.swift; sourceTree = ""; }; 6F55B9BD250239DA00A052A9 /* HydraAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HydraAPI.swift; sourceTree = ""; }; - 6F59DEA7256CE2BA00D9690D /* HydraNew.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = HydraNew.xcconfig; path = ../../../../../../Desktop/Endpoints/HydraNew.xcconfig; sourceTree = ""; }; - 6F59DEA9256CEC7200D9690D /* Dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Dev.xcconfig; path = ../../../../../../Desktop/Endpoints/Dev.xcconfig; sourceTree = ""; }; 6F83039424864431009409A5 /* APIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIError.swift; sourceTree = ""; }; 6F83039924864D6B009409A5 /* LocalizableString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableString.swift; sourceTree = ""; }; 6F83039A24864D6B009409A5 /* Localizable.strings.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Localizable.strings.txt; sourceTree = ""; }; @@ -436,7 +434,6 @@ 6F9975612423B7EA0044944E /* EnrollmentServerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollmentServerConfiguration.swift; sourceTree = ""; }; 6FA10A1724374DC200932622 /* Branding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Branding.swift; sourceTree = ""; }; 6FA7607125A8298B00CD9730 /* CustomInstructionStepTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomInstructionStepTest.swift; sourceTree = ""; }; - 6FA930DF2519CC6E00611C3E /* Hydra.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Hydra.xcconfig; path = ../../../../../../Desktop/Endpoints/Hydra.xcconfig; sourceTree = ""; }; 6FB89C1A225F40EA00420A7B /* TextChoiceQuestionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextChoiceQuestionController.swift; sourceTree = ""; }; 6FB89C20225F410500420A7B /* OtherTextChoiceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OtherTextChoiceCell.swift; sourceTree = ""; }; 6FB89C21225F410500420A7B /* TextChoiceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextChoiceCell.swift; sourceTree = ""; }; @@ -677,9 +674,6 @@ 6E7D2D581E433E8700763D4C /* MyStudies */ = { isa = PBXGroup; children = ( - 6F59DEA9256CEC7200D9690D /* Dev.xcconfig */, - 6F59DEA7256CE2BA00D9690D /* HydraNew.xcconfig */, - 6FA930DF2519CC6E00611C3E /* Hydra.xcconfig */, 6F07148824279452002749DA /* Default.xcconfig */, 6F10BB15234DDFDA006388E2 /* Branding */, 6FE00CD5238FEDE80067A7B8 /* Utils */, @@ -2492,7 +2486,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 30; + CURRENT_PROJECT_VERSION = 175; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 3485DDCN2M; ENABLE_BITCODE = YES; @@ -2524,7 +2518,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 30; + CURRENT_PROJECT_VERSION = 175; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 3485DDCN2M; ENABLE_BITCODE = YES; diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@2x.png b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@2x.png index 355ea0f0a8..b33440a467 100644 Binary files a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@2x.png and b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@2x.png differ diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@3x.png b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@3x.png index b911daca2a..d635d9b62a 100644 Binary files a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@3x.png and b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/OverviewImages/overviewLogo.imageset/logo1@3x.png differ diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/Contents.json b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/Contents.json index 259b5a79c7..adefaf21f4 100644 --- a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/Contents.json +++ b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/Contents.json @@ -5,18 +5,18 @@ "scale" : "1x" }, { + "filename" : "splash@2x-2.jpg", "idiom" : "iphone", - "filename" : "splash@2x.jpg", "scale" : "2x" }, { + "filename" : "splash@3x-2.jpg", "idiom" : "iphone", - "filename" : "splash@3x.jpg", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } } diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@2x-2.jpg b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@2x-2.jpg new file mode 100644 index 0000000000..858f9f6527 Binary files /dev/null and b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@2x-2.jpg differ diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@2x.jpg b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@2x.jpg deleted file mode 100644 index 94a2433b73..0000000000 Binary files a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@2x.jpg and /dev/null differ diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@3x-2.jpg b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@3x-2.jpg new file mode 100644 index 0000000000..6b8b18e7af Binary files /dev/null and b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@3x-2.jpg differ diff --git a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@3x.jpg b/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@3x.jpg deleted file mode 100644 index 90477fca68..0000000000 Binary files a/iOS/MyStudies/MyStudies/Assets/Assets.xcassets/Splash/splash.imageset/splash@3x.jpg and /dev/null differ diff --git a/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/Contents.json b/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/Contents.json index 32cd67b906..44e89703e4 100644 --- a/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/Contents.json +++ b/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/Contents.json @@ -5,18 +5,18 @@ "scale" : "1x" }, { - "idiom" : "universal", "filename" : "play_button1@2x.png", + "idiom" : "universal", "scale" : "2x" }, { - "idiom" : "universal", "filename" : "play_button1@3x.png", + "idiom" : "universal", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@2x.png b/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@2x.png index 3ddc364f5e..da4efac856 100644 Binary files a/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@2x.png and b/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@2x.png differ diff --git a/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@3x.png b/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@3x.png index b3bb545681..52c666440b 100644 Binary files a/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@3x.png and b/iOS/MyStudies/MyStudies/Assets/LoginAssets.xcassets/LoginScreen/watchVideo.imageset/play_button1@3x.png differ diff --git a/iOS/MyStudies/MyStudies/Branding/Generic/Branding.plist b/iOS/MyStudies/MyStudies/Branding/Generic/Branding.plist index 80b74a847a..5ea5f0d2b9 100644 --- a/iOS/MyStudies/MyStudies/Branding/Generic/Branding.plist +++ b/iOS/MyStudies/MyStudies/Branding/Generic/Branding.plist @@ -13,23 +13,23 @@ JoinStudyButtonTitle Participate ViewConsentButtonTitle - View Consent + View consent VisitWebsiteButtonTitle - Visit Website + Visit website ConsentPDF Consent PDF LeaveStudy - Leave Study + Leave study WebsiteLink WebsiteButtonTitle - App Website + App website TermsAndConditionURL PrivacyPolicyURL ValidatedTitle - Eligibility Confirmed + Eligibility confirmed AllowFeedback NavigationTitleName diff --git a/iOS/MyStudies/MyStudies/Controllers/ConsentUI/ConsentSharePdfStepViewController.swift b/iOS/MyStudies/MyStudies/Controllers/ConsentUI/ConsentSharePdfStepViewController.swift index 70c941fd12..e8b9b02443 100644 --- a/iOS/MyStudies/MyStudies/Controllers/ConsentUI/ConsentSharePdfStepViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/ConsentUI/ConsentSharePdfStepViewController.swift @@ -21,7 +21,7 @@ import UIKit let kConsentCompletionResultIdentifier = "ConsentCompletion" let kMainTitle = "Consent confirmed" -let kSubTitle = "You can now start participating in the Study" +let kSubTitle = "You can now start participating in the study" /// Consent Completion Step. class ConsentCompletionStep: ORKStep { diff --git a/iOS/MyStudies/MyStudies/Controllers/EligibilityUI/EligibilityStepViewController.swift b/iOS/MyStudies/MyStudies/Controllers/EligibilityUI/EligibilityStepViewController.swift index e4453a1cec..44d70e9b79 100644 --- a/iOS/MyStudies/MyStudies/Controllers/EligibilityUI/EligibilityStepViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/EligibilityUI/EligibilityStepViewController.swift @@ -21,7 +21,6 @@ import IQKeyboardManagerSwift import ResearchKit import UIKit -let kStudyWithStudyId = "Study with StudyId" let kTitleOK = "OK" class EligibilityStep: ORKStep { @@ -217,18 +216,6 @@ extension EligibilityStepViewController: NMWebServiceDelegate { func failedRequest(_ manager: NetworkManager, requestName: NSString, error: NSError) { self.removeProgressIndicator() - if error.localizedDescription.localizedCaseInsensitiveContains(tokenTextField.text!) { - - self.showAlert(message: kMessageInvalidTokenOrIfStudyDoesNotExist) - - } else { - if error.localizedDescription.localizedCaseInsensitiveContains(kStudyWithStudyId) { - - self.showAlert(message: kMessageInvalidTokenOrIfStudyDoesNotExist) - - } else { - self.showAlert(message: error.localizedDescription) - } - } + self.showAlert(message: error.localizedDescription) } } diff --git a/iOS/MyStudies/MyStudies/Controllers/FetalKickUI/FetalKickCounterStepViewController/FetalKickCounterStepViewController.swift b/iOS/MyStudies/MyStudies/Controllers/FetalKickUI/FetalKickCounterStepViewController/FetalKickCounterStepViewController.swift index 200ebedcec..7bc26dfa1d 100644 --- a/iOS/MyStudies/MyStudies/Controllers/FetalKickUI/FetalKickCounterStepViewController/FetalKickCounterStepViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/FetalKickUI/FetalKickCounterStepViewController/FetalKickCounterStepViewController.swift @@ -38,7 +38,7 @@ let kFetalKickActivityId = "FetalKickActivityId" let kFetalkickStudyId = "FetalKickStudyId" let kFetalKickCounterValue = "FetalKickCounterValue" let kFetalKickCounterRunId = "FetalKickCounterRunid" -let kSelectTimeLabel = "Select Time" +let kSelectTimeLabel = "Select a time" class FetalKickCounterStepViewController: ORKStepViewController { diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ConfirmationViewUI/ConfirmationViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ConfirmationViewUI/ConfirmationViewController.swift index b4cae89324..9e67e15ec1 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ConfirmationViewUI/ConfirmationViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ConfirmationViewUI/ConfirmationViewController.swift @@ -21,18 +21,18 @@ import UIKit let kConfirmationSegueIdentifier = "confirmationSegue" let kHeaderDescription = """ - You have chosen to delete your #APPNAME# account. \ + You have chosen to delete your app account. \ This will result in automatic withdrawal from the studies you were enrolled in. """ let kHeaderDescriptionStandalone = """ - You have chosen to delete your #APPNAME# account. \ + You have chosen to delete your app account. \ This will result in automatic withdrawal from the studies you were enrolled in. """ let kConfirmWithdrawlSelectOptionsAlert = - "Please select an option between Delete Data or Retain Data for all studies." + "Please select a data retention preference for all studies in the list." let kResponseDataDeletedText = "Response data will be deleted" let kResponseDataRetainedText = "Response data will be retained" @@ -142,12 +142,7 @@ class ConfirmationViewController: UIViewController { studyName: studyName, withdrawalConfigration: withdrawalConfigration ) - - if study.withdrawalConfigration?.type == StudyWithdrawalConfigrationType.deleteData { - withdrawnStudy.shouldDelete = true - } else if study.withdrawalConfigration?.type == StudyWithdrawalConfigrationType.noAction { - withdrawnStudy.shouldDelete = false - } + withdrawnStudy.shouldDelete = false studiesToWithdraw.append(withdrawnStudy) } } diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ProfileViewUI/ProfileViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ProfileViewUI/ProfileViewController.swift index dc12a73b78..1f3a8cc5d3 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ProfileViewUI/ProfileViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ProfileViewUI/ProfileViewController.swift @@ -24,26 +24,26 @@ import UIKit let kProfileTableViewCellIdentifier = "ProfileTableViewCell" -let kLeadTimeSelectText = "Select Lead Time" +let kLeadTimeSelectText = "Select lead time" let kActionSheetDoneButtonTitle = "Done" let kActionSheetCancelButtonTitle = "Cancel" let kChangePasswordSegueIdentifier = "changePasswordSegue" let kErrorTitle = "" let kProfileAlertTitleText = "Profile" -let kProfileAlertUpdatedText = "Profile updated Successfully." +let kProfileAlertUpdatedText = "Your profile has been updated." let signupCellLastIndex = 2 -let kProfileTitleText = "My Account" +let kProfileTitleText = "My account" -let kSignOutText = "Sign Out" +let kSignOutText = "Sign out" let kLabelName = "LabelName" -let kUseTouchIdOrPasscode = "Use Passcode or Touch ID to access app" -let kUseFaceIdOrPasscode = "Use Passcode or Face ID to access app" +let kUseTouchIdOrPasscode = "Use passcode or Touch ID to access app" +let kUseFaceIdOrPasscode = "Use passcode or Face ID to access app" -let kUsePasscodeToAccessApp = "Use Passcode to access app" +let kUsePasscodeToAccessApp = "Use passcode to access the app?" let ktouchid = "touchIdEnabled" let korkPasscode = "ORKPasscode" @@ -635,7 +635,7 @@ extension ProfileViewController: UITableViewDataSource { cell.textFieldValue?.isHidden = true cell.buttonChangePassword?.isHidden = false - cell.buttonChangePassword?.setTitle("Change Passcode", for: .normal) + cell.buttonChangePassword?.setTitle("Change passcode", for: .normal) if User.currentUser.settings?.passcode == true { cell.buttonChangePassword?.isUserInteractionEnabled = true diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ContactUsViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ContactUsViewController.swift index 91d690c643..0611f39d0d 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ContactUsViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ContactUsViewController.swift @@ -53,7 +53,7 @@ class ContactUsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - self.navigationItem.title = NSLocalizedString("Contact Us", comment: "") + self.navigationItem.title = NSLocalizedString("Contact us", comment: "") // Used to set border color for bottom view buttonSubmit?.layer.borderColor = kUicolorForButtonBackground diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/FeedBackViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/FeedBackViewController.swift index da03e73d41..b35175a75b 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/FeedBackViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/FeedBackViewController.swift @@ -46,7 +46,7 @@ class FeedBackViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - self.navigationItem.title = NSLocalizedString("Leave Us Your Feedback", comment: "") + self.navigationItem.title = NSLocalizedString("Leave us your feedback", comment: "") // Used to set border color for bottom view buttonSubmit?.layer.borderColor = kUicolorForButtonBackground @@ -72,7 +72,7 @@ class FeedBackViewController: UIViewController { ) } else if FeedbackDetail.subject.isEmpty { UIUtilities.showAlertWithMessage( - alertMessage: NSLocalizedString("Please enter message", comment: "") + alertMessage: NSLocalizedString("Please enter the message", comment: "") ) } else if FeedbackDetail.feedback.isEmpty { UIUtilities.showAlertWithMessage( diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ReachoutOptionsViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ReachoutOptionsViewController.swift index c4bb8efcf5..8257ca185e 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ReachoutOptionsViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/ReachoutUI/ReachoutOptionsViewController.swift @@ -26,7 +26,7 @@ class ReachoutOptionsViewController: UIViewController { // MARK: - Viewcontroller Lifecycle override func viewDidLoad() { super.viewDidLoad() - self.navigationItem.title = NSLocalizedString("Reach Out", comment: "") + self.navigationItem.title = NSLocalizedString("Reach out", comment: "") } override func viewWillAppear(_ animated: Bool) { @@ -51,11 +51,11 @@ extension ReachoutOptionsViewController: UITableViewDataSource { switch indexPath.row { case 0: - cell.labelTitle?.text = NSLocalizedString("Leave Anonymous Feedback", comment: "") + cell.labelTitle?.text = NSLocalizedString("Leave feedback anonymously", comment: "") case 1: - cell.labelTitle?.text = NSLocalizedString("Need Help? Contact Us", comment: "") + cell.labelTitle?.text = NSLocalizedString("Need help? Contact us.", comment: "") default: - cell.labelTitle?.text = NSLocalizedString("Need Help? Contact Us", comment: "") + cell.labelTitle?.text = NSLocalizedString("Need help? Contact us.", comment: "") } return cell diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/SlideMenu/LeftMenuViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/SlideMenu/LeftMenuViewController.swift index 2154583945..0b9420e39e 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/SlideMenu/LeftMenuViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/SlideMenu/LeftMenuViewController.swift @@ -26,9 +26,9 @@ let kLeftMenuTitle = "menuTitle" let kLeftMenuIconName = "iconName" let kLeftMenuCellTitleHome = "Home" let kLeftMenuCellTitleResources = "Resources" -let kLeftMenuCellTitleProfile = "My Account" -let kLeftMenuCellTitleSignIn = "Sign In" -let kLeftMenuCellTitleNewUser = "New User?" +let kLeftMenuCellTitleProfile = "My account" +let kLeftMenuCellTitleSignIn = "Sign in" +let kLeftMenuCellTitleNewUser = "New user?" let kLeftMenuCellSubTitleValue = "Sign up" let kAlertMessageReachoutText = "This feature will be available in the next sprint." @@ -36,7 +36,7 @@ let kAlertMessageForSignOut = "Are you sure you want to sign out?" let kAlertMessageSignOutSync = """ Are you sure you want to sign out? \ - Incomplete activities and activities completed while offline must be re-started when you next sign in. + Incomplete activities and activities that were completed in offline mode must be re-started when you next sign in. """ let kAlertSignOutLaterTitle = "Sign Out later" @@ -280,7 +280,7 @@ class LeftMenuViewController: UIViewController, LeftMenuProtocol { if user.userType == .loggedInUser { menus.append( [ - "menuTitle": "My Account", + "menuTitle": "My account", "iconName": "profile_menu1", "menuType": LeftMenu.profileReachOut, ]) @@ -288,14 +288,14 @@ class LeftMenuViewController: UIViewController, LeftMenuProtocol { if shouldAllowToGiveFeedback { menus.append( [ - "menuTitle": "Reach Out", + "menuTitle": "Reach out", "iconName": "reachout_menu1", "menuType": LeftMenu.reachOutSignIn, ]) } menus.append( [ - "menuTitle": "Sign Out", + "menuTitle": "Sign out", "iconName": "ic_signout_menu", "menuType": LeftMenu.signOut, ]) @@ -303,7 +303,7 @@ class LeftMenuViewController: UIViewController, LeftMenuProtocol { if shouldAllowToGiveFeedback { menus.append( [ - "menuTitle": "Reach Out", + "menuTitle": "Reach out", "iconName": "reachout_menu1", "menuType": LeftMenu.profileReachOut, ]) @@ -311,14 +311,14 @@ class LeftMenuViewController: UIViewController, LeftMenuProtocol { menus.append( [ - "menuTitle": "Sign In", + "menuTitle": "Sign in", "iconName": "signin_menu1", "menuType": LeftMenu.reachOutSignIn, ]) menus.append( [ - "menuTitle": "New User?", + "menuTitle": "New user?", "iconName": "newuser_menu1", "subTitle": "Sign up", "menuType": LeftMenu.signup, diff --git a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/StudyUI/StudyListViewController.swift b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/StudyUI/StudyListViewController.swift index fae161f45c..cdcbeadb18 100644 --- a/iOS/MyStudies/MyStudies/Controllers/GatewayUI/StudyUI/StudyListViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/GatewayUI/StudyUI/StudyListViewController.swift @@ -245,7 +245,7 @@ class StudyListViewController: UIViewController { if daysLastSeen >= 7 { // Notification is disabled for 7 or more Days UIUtilities.showAlertWithTitleAndMessage( title: NSLocalizedString(Branding.productTitle, comment: "") as NSString, - message: NSLocalizedString(kMessageAppNotificationOffRemainder, comment: "") + message: kMessageAppNotificationOffStayup + kMessageAppNotificationOffRemainder as NSString ) diff --git a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/ForgotPasswordViewController.swift b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/ForgotPasswordViewController.swift index dd78ca58c9..9e3bc1ab99 100644 --- a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/ForgotPasswordViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/ForgotPasswordViewController.swift @@ -23,8 +23,8 @@ import UIKit let kVerifyViewControllerSegue = "VerifyViewControllerSegue" let kVerficationMessageFromForgotPassword = """ - Your registered email(xyz@gmail.com) is pending verification. Enter the Verification Code received \ - on this email to complete verification and try the Forgot Password action again. + Your account(xyz@gmail.com) is pending verification. Enter the verification code sent \ + to your registered email to complete this step and try requesting password help again. """ class ForgotPasswordViewController: UIViewController { diff --git a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/SignInViewController.swift b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/SignInViewController.swift index c96059e786..0989eb7b6f 100644 --- a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/SignInViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/LoginUI/SignInViewController.swift @@ -25,8 +25,8 @@ import WebKit let kVerifyMessageFromSignIn = """ - Your registered email is pending verification. Please type in the Verification Code received in the email \ - to complete this step and proceed to using the app. + Your account is pending verification. Please type in the verification code received in email \ + to complete this step and use the app. """ enum SignInLoadFrom: Int { diff --git a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/SignUpViewController.swift b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/SignUpViewController.swift index 957b54f328..a464c209a6 100644 --- a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/SignUpViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/SignUpViewController.swift @@ -22,7 +22,7 @@ import IQKeyboardManagerSwift import UIKit let kVerifyMessageFromSignUp = - "An email has been sent to xyz@gmail.com. Please type in the Verification Code received in the email to complete the verification step." + "An email has been sent to xyz@gmail.com. Please type in the verification code received in the email to complete account setup." enum SignUpLoadFrom: Int { case gatewayOverview @@ -398,6 +398,7 @@ extension SignUpViewController: UITextFieldDelegate { func textFieldDidBeginEditing(_ textField: UITextField) { if textField.tag == TextFieldTags.emailId.rawValue { textField.keyboardType = .emailAddress + textField.isSecureTextEntry = false } } @@ -421,6 +422,7 @@ extension SignUpViewController: UITextFieldDelegate { return true } } else if tag == .password || tag == .confirmPassword { + textField.isSecureTextEntry = true if finalString.count > 64 { return false } else { @@ -445,6 +447,7 @@ extension SignUpViewController: UITextFieldDelegate { switch tag { case .emailId: self.user.emailId = textField.text! + textField.isSecureTextEntry = false case .password: if let password = textField.text { @@ -455,8 +458,10 @@ extension SignUpViewController: UITextFieldDelegate { } self.user.password = password } + textField.isSecureTextEntry = true case .confirmPassword: confirmPassword = textField.text! + textField.isSecureTextEntry = true } } } diff --git a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/VerificationViewController.swift b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/VerificationViewController.swift index 91d86bd1a8..e5c6215620 100644 --- a/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/VerificationViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/LoginRegisterUI/RegisterUI/VerificationViewController.swift @@ -24,7 +24,7 @@ let kDefaultEmail = "xyz@gmail.com" let kSignupCompletionSegue = "signupCompletionSegue" let kAlertMessageText = "Message" let kAlertMessageVerifyEmail = "Please verify your email address." -let kAlertMessageResendEmail = "An email verification code has been sent to your registered email." +let kAlertMessageResendEmail = "A verification code has been sent to your registered email" let kChangePasswordViewControllerIdentifier = "ChangePasswordViewController" diff --git a/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/ActivitiesViewController.swift b/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/ActivitiesViewController.swift index bac9039761..8c7d4a3f8e 100644 --- a/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/ActivitiesViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/ActivitiesViewController.swift @@ -27,7 +27,7 @@ let kActivities = "activities" let kActivityUnwindToStudyListIdentifier = "unwindeToStudyListIdentier" let kActivityAbondonedAlertMessage = """ - It is not yet time to take the next run of this activity. + The next run of this activity is not available yet. Please try again later. """ enum ActivityAvailabilityStatus: Int { @@ -84,7 +84,7 @@ class ActivitiesViewController: UIViewController { self.tableView?.estimatedRowHeight = 126 self.tableView?.rowHeight = UITableView.automaticDimension self.tableView?.tableFooterView = UIView() - self.navigationItem.title = NSLocalizedString("Study Activities", comment: "") + self.navigationItem.title = NSLocalizedString("Study activities", comment: "") self.tableView?.sectionHeaderHeight = 30 self.navigationController?.navigationItem.rightBarButtonItem?.tintColor = UIColor.gray @@ -586,8 +586,8 @@ class ActivitiesViewController: UIViewController { if previousMissed < totalIncompletedRuns { // show alert let message = - "We noticed you missed an activity in " + (Study.currentStudy?.name!)! - + " today. That’s ok! We know you’re busy, but we encourage you to complete study activities before they expire." + "We noticed you missed an activity " + (Study.currentStudy?.name!)! + + " today. Your regular participation is important, we encourage you to complete study activities before they expire." UIUtilities.showAlertWithMessage(alertMessage: message) } } diff --git a/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/QuestionStepController/TextChoiceQuestionController.swift b/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/QuestionStepController/TextChoiceQuestionController.swift index 0fd67879d3..4716f7896c 100644 --- a/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/QuestionStepController/TextChoiceQuestionController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/StudyUI/ActivityUI/QuestionStepController/TextChoiceQuestionController.swift @@ -538,7 +538,7 @@ class TextChoiceQuestionController: ORKQuestionStepViewController { let alertVC = UIAlertController( title: "Answer required", - message: "Please provide an input for the text field too.", + message: "Please fill out the text field too.", preferredStyle: .alert ) diff --git a/iOS/MyStudies/MyStudies/Controllers/StudyUI/ResourceUI/ResourcesViewController.swift b/iOS/MyStudies/MyStudies/Controllers/StudyUI/ResourceUI/ResourcesViewController.swift index c29ddfcc85..2871ac96f7 100644 --- a/iOS/MyStudies/MyStudies/Controllers/StudyUI/ResourceUI/ResourcesViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/StudyUI/ResourceUI/ResourcesViewController.swift @@ -254,68 +254,79 @@ class ResourcesViewController: UIViewController { var withdrawalType = Study.currentStudy?.withdrawalConfigration?.type if withdrawalMessage == nil { - withdrawalMessage = "Are you sure you want to " + leaveStudy + "?" + withdrawalMessage = Utilities.isStandaloneApp() ? kResourceLeaveStandaloneStudy : kResourceLeaveGatewayStudy } if withdrawalType == nil || withdrawalType == .notAvailable { withdrawlInformationNotFound = true withdrawalType = .notAvailable - WCPServices().getStudyInformation( - studyId: (Study.currentStudy?.studyId)!, - delegate: self + noWithdrawInfoAlert(withdrawalMessage ?? "Are you sure you want to leave the study?") + } else { + UIUtilities.showAlertMessageWithTwoActionsAndHandler( + NSLocalizedString((leaveStudy + " ?"), comment: ""), + errorMessage: NSLocalizedString(withdrawalMessage!, comment: ""), + errorAlertActionTitle: NSLocalizedString("Cancel", comment: ""), + errorAlertActionTitle2: NSLocalizedString("Proceed", comment: ""), + viewControllerUsed: self, + action1: {}, + action2: { + + switch withdrawalType! as StudyWithdrawalConfigrationType { + + case .askUser: + + UIUtilities.showAlertMessageWithThreeActionsAndHandler( + kImportantNoteMessage, + errorMessage: kRetainDataOnLeaveStudy, + errorAlertActionTitle: "Retain my data", + errorAlertActionTitle2: "Delete my data", + viewControllerUsed: self, + action1: { + // Retain Action + + self.shouldDeleteData = false + self.withdrawalFromStudy(deleteResponse: false) + + }, + action2: { + + // Delete action + self.shouldDeleteData = true + self.withdrawalFromStudy(deleteResponse: true) + + } + ) + + case .deleteData: + self.shouldDeleteData = true + self.withdrawalFromStudy(deleteResponse: true) + + case .noAction: + self.shouldDeleteData = false + self.withdrawalFromStudy(deleteResponse: false) + + default: break + } + } ) - return } + } + + func noWithdrawInfoAlert(_ withdrawalMessage: String) { UIUtilities.showAlertMessageWithTwoActionsAndHandler( - NSLocalizedString((leaveStudy + " ?"), comment: ""), - errorMessage: NSLocalizedString(withdrawalMessage!, comment: ""), - errorAlertActionTitle: NSLocalizedString("Cancel", comment: ""), - errorAlertActionTitle2: NSLocalizedString("Proceed", comment: ""), + "", + errorMessage: NSLocalizedString(withdrawalMessage, comment: ""), + errorAlertActionTitle: NSLocalizedString("Yes", comment: ""), + errorAlertActionTitle2: NSLocalizedString("Cancel", comment: ""), viewControllerUsed: self, - action1: {}, - action2: { - - switch withdrawalType! as StudyWithdrawalConfigrationType { - - case .askUser: - - UIUtilities.showAlertMessageWithThreeActionsAndHandler( - kImportantNoteMessage, - errorMessage: kRetainDataOnLeaveStudy, - errorAlertActionTitle: "Retain my data", - errorAlertActionTitle2: "Delete my data", - viewControllerUsed: self, - action1: { - // Retain Action - - self.shouldDeleteData = false - self.withdrawalFromStudy(deleteResponse: false) - - }, - action2: { - - // Delete action - self.shouldDeleteData = true - self.withdrawalFromStudy(deleteResponse: true) - - } - ) - - case .deleteData: - self.shouldDeleteData = true - self.withdrawalFromStudy(deleteResponse: true) - - case .noAction: - self.shouldDeleteData = false - self.withdrawalFromStudy(deleteResponse: false) - - default: break - } - } + action1: { + self.shouldDeleteData = false + self.withdrawalFromStudy(deleteResponse: false) + }, + action2: {} ) - } func navigateToStudyHome() { @@ -535,8 +546,6 @@ class ResourcesViewController: UIViewController { self.removeProgressIndicator() self.withdrawlInformationNotFound = false - self.handleLeaveStudy() - } else { self.checkForResourceUpdate() } diff --git a/iOS/MyStudies/MyStudies/Controllers/StudyUI/StudyOverviewUI/StudyHomeViewController.swift b/iOS/MyStudies/MyStudies/Controllers/StudyUI/StudyOverviewUI/StudyHomeViewController.swift index 8a6a9ddcdd..8e0a5a7e73 100644 --- a/iOS/MyStudies/MyStudies/Controllers/StudyUI/StudyOverviewUI/StudyHomeViewController.swift +++ b/iOS/MyStudies/MyStudies/Controllers/StudyUI/StudyOverviewUI/StudyHomeViewController.swift @@ -581,19 +581,10 @@ class StudyHomeViewController: UIViewController { ) } } else if participatedStatus == .withdrawn { - // check if rejoining is allowed after withrdrawn from study - if currentStudy.studySettings.rejoinStudyAfterWithdrawn { - WCPServices().getEligibilityConsentMetadata( - studyId: (Study.currentStudy?.studyId)!, - delegate: self as NMWebServiceDelegate - ) - } else { - UIUtilities.showAlertWithTitleAndMessage( - title: "", - message: NSLocalizedString(kMessageForStudyWithdrawnState, comment: "") - as NSString - ) - } + WCPServices().getEligibilityConsentMetadata( + studyId: (Study.currentStudy?.studyId)!, + delegate: self as NMWebServiceDelegate + ) } case .paused: UIUtilities.showAlertWithTitleAndMessage( @@ -1247,9 +1238,7 @@ extension StudyHomeViewController: ORKTaskViewControllerDelegate { let currentStatus = Study.currentStudy?.userParticipateState.status if currentStatus == .yetToEnroll || currentStatus == .notEligible - || (currentStatus == .withdrawn - && Study.currentStudy?.studySettings - .rejoinStudyAfterWithdrawn ?? false) + || currentStatus == .withdrawn { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { diff --git a/iOS/MyStudies/MyStudies/FDA.entitlements b/iOS/MyStudies/MyStudies/FDA.entitlements index 9b377f2b96..c77f75e0e6 100644 --- a/iOS/MyStudies/MyStudies/FDA.entitlements +++ b/iOS/MyStudies/MyStudies/FDA.entitlements @@ -6,7 +6,5 @@ development com.apple.developer.healthkit - com.apple.developer.healthkit.access - diff --git a/iOS/MyStudies/MyStudies/Info.plist b/iOS/MyStudies/MyStudies/Info.plist index 6099bbfd16..6b07fc2486 100644 --- a/iOS/MyStudies/MyStudies/Info.plist +++ b/iOS/MyStudies/MyStudies/Info.plist @@ -4,8 +4,6 @@ API_KEY $(API_KEY) - HYDRA_CLIENT_ID - $(HYDRA_CLIENT_ID) AUTH_URL $(AUTH_URL) ApplicationID @@ -34,6 +32,8 @@ $(ENROLLMENT_DATASTORE_URL) HYDRA_BASE_URL $(HYDRA_BASE_URL) + HYDRA_CLIENT_ID + $(HYDRA_CLIENT_ID) ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -41,9 +41,9 @@ NSAppTransportSecurity NSAllowsArbitraryLoads - + NSAllowsArbitraryLoadsInWebContent - + NSCameraUsageDescription $(PRODUCT_NAME) would like to access your camera for the task to give more accurate result. @@ -65,10 +65,10 @@ $(PRODUCT_NAME) would like to use Motion data for the task to give more accurate result. NSSpeechRecognitionUsageDescription $(PRODUCT_NAME) would like to use Speech Recognition for the task to give more accurate result. - USER_DATASTORE_URL - $(USER_DATASTORE_URL) RESPONSE_DATASTORE_URL $(RESPONSE_DATASTORE_URL) + STUDY_DATASTORE_URL + $(STUDY_DATASTORE_URL) UIAppFonts Verdana Italic.ttf @@ -94,7 +94,7 @@ Light UIViewControllerBasedStatusBarAppearance - STUDY_DATASTORE_URL - $(STUDY_DATASTORE_URL) + USER_DATASTORE_URL + $(USER_DATASTORE_URL) diff --git a/iOS/MyStudies/MyStudies/Models/Activity/ActivityBuilder/ActivityBuilder.swift b/iOS/MyStudies/MyStudies/Models/Activity/ActivityBuilder/ActivityBuilder.swift index b2ba3b2284..0383db9937 100644 --- a/iOS/MyStudies/MyStudies/Models/Activity/ActivityBuilder/ActivityBuilder.swift +++ b/iOS/MyStudies/MyStudies/Models/Activity/ActivityBuilder/ActivityBuilder.swift @@ -133,10 +133,10 @@ class ActivityBuilder { // addding completion step let completionStep = ORKCompletionStep(identifier: kCompletionStep) - completionStep.title = "Activity Completed" + completionStep.title = "Activity completed" completionStep.image = #imageLiteral(resourceName: "successBlueBig") completionStep.detailText = - "Tap Done to submit responses. Responses cannot be modified after submission." + "Tap on 'Done' to submit your responses. Responses cannot be modified after submission." orkStepArray?.append(completionStep) // Creating ordered or navigable task diff --git a/iOS/MyStudies/MyStudies/Models/Consent/ConsentBuilder/ConsentBuilder.swift b/iOS/MyStudies/MyStudies/Models/Consent/ConsentBuilder/ConsentBuilder.swift index ee80856fe2..ef9ebf3f8e 100644 --- a/iOS/MyStudies/MyStudies/Models/Consent/ConsentBuilder/ConsentBuilder.swift +++ b/iOS/MyStudies/MyStudies/Models/Consent/ConsentBuilder/ConsentBuilder.swift @@ -66,18 +66,18 @@ let kConsentViewPdfStoryboardId = "ConsentPdfViewerStepViewControllerIdentifier" /// Comprehenion Instruction Step Keys let kConsentComprehensionTestTitle = "Comprehension" let kConsentComprehensionTestText = - "Let's do a quick and simple test of your understanding of this Study." + "Let's do a quick and simple test of your understanding of this study." let kComprehensionInstructionStepIdentifier = "ComprehensionInstructionStep" /// Comprehension Completion Step Keys -let kComprehensionCompletionTitle = "Great Job!" +let kComprehensionCompletionTitle = "Great job!" let kComprehensionCompletionText = - "You answered all of the questions correctly. Tap on Next to proceed" + "You answered all of the questions correctly. Tap next to continue." let kComprehensionCompletionStepIdentifier = "ComprehensionCompletionStep" /// Consent Completion let kConsentCompletionMainTitle = "Consent confirmed" -let kConsentCompletionSubTitle = "You can now start participating in the Study" +let kConsentCompletionSubTitle = "You can now start participating in the study" let kSignaturePageContentText = "I agree to participate in this research study." /// Signature Page @@ -497,6 +497,8 @@ struct ReviewConsent { someObject: dict[kConsentReviewStepReasonForConsent] as AnyObject ) { reasonForConsent = dict[kConsentReviewStepReasonForConsent] as? String + } else { + reasonForConsent = kMessageconsentConfirmation } } } diff --git a/iOS/MyStudies/MyStudies/Models/Resource/Resources.plist b/iOS/MyStudies/MyStudies/Models/Resource/Resources.plist index b63dd0cc9d..af0a263e94 100644 --- a/iOS/MyStudies/MyStudies/Models/Resource/Resources.plist +++ b/iOS/MyStudies/MyStudies/Models/Resource/Resources.plist @@ -6,7 +6,7 @@ resourceId 111 title - App Glossary + App glossary type pdf content diff --git a/iOS/MyStudies/MyStudies/Models/StudyModel/Study.swift b/iOS/MyStudies/MyStudies/Models/StudyModel/Study.swift index ee605fe0c8..6e5d289e93 100644 --- a/iOS/MyStudies/MyStudies/Models/StudyModel/Study.swift +++ b/iOS/MyStudies/MyStudies/Models/StudyModel/Study.swift @@ -208,7 +208,7 @@ class Study: Hashable { class StudySettings { lazy var enrollingAllowed = true - lazy var rejoinStudyAfterWithdrawn = false + lazy var rejoinStudyAfterWithdrawn = true lazy var platform = "ios" init() { diff --git a/iOS/MyStudies/MyStudies/Models/UserModel/User.swift b/iOS/MyStudies/MyStudies/Models/UserModel/User.swift index c74568040e..f0031e6d6b 100644 --- a/iOS/MyStudies/MyStudies/Models/UserModel/User.swift +++ b/iOS/MyStudies/MyStudies/Models/UserModel/User.swift @@ -380,7 +380,7 @@ class UserStudyStatus { var description: String { switch self { case .yetToEnroll: - return "Yet To enroll" + return "Yet to enroll" case .enrolled: return "Enrolled" case .completed: @@ -396,7 +396,7 @@ class UserStudyStatus { var closedStudyDescription: String { switch self { case .yetToEnroll: - return "No participation" + return "No Participation" case .enrolled: return "Partial participation" case .completed: @@ -508,8 +508,10 @@ class UserStudyStatus { kStudyId: self.studyId, kStudyStatus: self.status.paramValue, ] as [String: Any] - if !siteID.isEmpty { - studyDetail["siteId"] = siteID + if let siteID = siteID { + if !siteID.isEmpty { + studyDetail["siteId"] = siteID + } } if !id.isEmpty { studyDetail[kStudyParticipantId] = id @@ -527,8 +529,10 @@ class UserStudyStatus { "completion": completion, "adherence": adherence, ] as [String: Any] - if !siteID.isEmpty { - studyDetail["siteId"] = siteID + if let siteID = siteID { + if !siteID.isEmpty { + studyDetail["siteId"] = siteID + } } if !id.isEmpty { studyDetail[kStudyParticipantId] = id diff --git a/iOS/MyStudies/MyStudies/Storyboards/Base.lproj/Main.storyboard b/iOS/MyStudies/MyStudies/Storyboards/Base.lproj/Main.storyboard index 11294584ed..9e08cc1080 100644 --- a/iOS/MyStudies/MyStudies/Storyboards/Base.lproj/Main.storyboard +++ b/iOS/MyStudies/MyStudies/Storyboards/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -15,7 +15,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -136,7 +136,7 @@ - + @@ -149,7 +149,7 @@ - + @@ -185,7 +185,7 @@ - + @@ -224,7 +224,7 @@ - - - -
@@ -104,26 +120,68 @@

Create password

+
- + + + {{ meterStatus }} + +
+
+ {{ getError("EC_0084") }} +
+
+ {{ getError("EC_0081") }} +
+
+ {{ getError("EC_0082") }} +
+
+ {{ getError("EC_0083") }} +
+ { this.setupAccountForm.patchValue(user); @@ -105,4 +137,43 @@ export class SetUpAccountComponent }), ); } + + onChange(): void { + this.setupAccountForm.valueChanges.subscribe(() => { + const secretkeylenth = String( + this.setupAccountForm.controls['password'].value, + ); + if (secretkeylenth.length === 0) { + this.passwordMeterLow = ' '; + this.passwordMeterHigh = ' '; + this.passwordMeterValue = ' '; + this.passwordMeterOptimum = ' '; + this.meterStatus = ' '; + } else if (this.setupAccountForm.controls['password'].errors) { + this.passwordMeterLow = '.25'; + this.passwordMeterHigh = '.75'; + this.passwordMeterValue = '.2'; + this.passwordMeterOptimum = '.8'; + this.meterStatus = 'Weak'; + } else if (secretkeylenth.length === 8) { + this.passwordMeterLow = '.25'; + this.passwordMeterHigh = '.75'; + this.passwordMeterValue = '.5'; + this.passwordMeterOptimum = '.15'; + this.meterStatus = 'Fair'; + } else if (secretkeylenth.length > 8 && secretkeylenth.length <= 12) { + this.passwordMeterLow = '.10'; + this.passwordMeterHigh = '1'; + this.passwordMeterValue = '.7'; + this.passwordMeterOptimum = '.15'; + this.meterStatus = 'Good'; + } else if (secretkeylenth.length > 12) { + this.passwordMeterLow = '.10'; + this.passwordMeterHigh = '1'; + this.passwordMeterValue = '1'; + this.passwordMeterOptimum = '.20'; + this.meterStatus = 'Strong'; + } + }); + } } diff --git a/participant-manager/src/app/entity/mock-studies-data.ts b/participant-manager/src/app/entity/mock-studies-data.ts index d2b1d09d86..df9dd36c7c 100644 --- a/participant-manager/src/app/entity/mock-studies-data.ts +++ b/participant-manager/src/app/entity/mock-studies-data.ts @@ -4,7 +4,10 @@ import { } from '../site-coordinator/studies/shared/study.model'; import {StudyDetails} from '../site-coordinator/studies/shared/study-details'; import {ApiResponse} from './api.response.model'; -import {UpdateTargetEnrollmentRequest} from '../site-coordinator/studies/shared/site.model'; +import { + SiteResponse, + UpdateTargetEnrollmentRequest, +} from '../site-coordinator/studies/shared/site.model'; import {AddSiteRequest} from '../site-coordinator/sites/shared/add.sites.request'; import {EnrollmentStatus, Status, StudyType} from '../shared/enums'; import {Permission} from '../shared/permission-enums'; @@ -200,7 +203,7 @@ export const expectedTargetEnrollment: UpdateTargetEnrollmentRequest = { }; export const expectedSiteResponse = { message: 'New site added successfully', -} as ApiResponse; +} as SiteResponse; export const expectedNewSite = { studyId: '1', diff --git a/participant-manager/src/app/shared/error.codes.enum.ts b/participant-manager/src/app/shared/error.codes.enum.ts index d2cd5f1413..42fbc11811 100644 --- a/participant-manager/src/app/shared/error.codes.enum.ts +++ b/participant-manager/src/app/shared/error.codes.enum.ts @@ -118,6 +118,10 @@ const errorMessages = { EC_0078: 'Temporary password is incorrect', EC_0079: 'This site belongs to an active study that has one or more actively enrolled participants, and cannot be decommissioned.', + EC_0081: 'Consecutive characters and space is not allowed', + EC_0082: 'Password should not contain user name', + EC_0083: 'Password should not contain service name', + EC_0084: 'Please enter strong password', /* eslint-enable @typescript-eslint/naming-convention */ }; export type ErrorCode = keyof typeof errorMessages; diff --git a/participant-manager/src/app/site-coordinator/account/account-profile/account-profile.component.html b/participant-manager/src/app/site-coordinator/account/account-profile/account-profile.component.html index bf26444398..875e7954be 100644 --- a/participant-manager/src/app/site-coordinator/account/account-profile/account-profile.component.html +++ b/participant-manager/src/app/site-coordinator/account/account-profile/account-profile.component.html @@ -32,6 +32,7 @@ id="firstName" placeholder="Enter first name" formControlName="firstName" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
+ Please use characters from the following set only: A-Z a-z + 0-9 *()_+|:.- +
@@ -57,6 +65,7 @@ id="lastName" placeholder="Enter last name" formControlName="lastName" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
+ Please use characters from the following set only: A-Z a-z + 0-9 *()_+|:.- +
diff --git a/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.html b/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.html index 137a8228e6..7af4277450 100644 --- a/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.html +++ b/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.html @@ -72,33 +72,90 @@
+ +
- - Enter a new password - - + + + {{ meterStatus }} + +
+
- Your password must be at least 8 characters long and contain - lower case, upper case, numeric and special characters. - + + Enter a new password + +
+ {{ getError("EC_0081") }} +
+
+ {{ getError("EC_0084") }} +
+
+ {{ getError("EC_0082") }} +
+
+ {{ getError("EC_0083") }} +
+
diff --git a/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.scss b/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.scss index fe2c366668..b5f09e9a6c 100644 --- a/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.scss +++ b/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.scss @@ -1,3 +1,96 @@ .float-left { float: left; } +.has-error input[type="email"], +.has-error input[type="checkbox"], +.has-error input[type="radio"], +.has-error input[type="text"], +.has-error input[type="password"] { + border-color: rgb(216, 12, 12); + color: rgb(230, 14, 14); +} + +.help-block { + color: red; + font-size: small; +} +input[type="number"] { + -moz-appearance: textfield !important; +} + +.form-heading__three { + font-size: 30px; + color: #181919; + text-align: center; + font-weight: 500; + line-height: 36px; + letter-spacing: 0px; +} + +.content_doc_info_two p { + font-size: 18px; + line-height: 28px; + color: #181919; + font-weight: 500; + text-align: center; +} + +.dis_button { + cursor: not-allowed !important; + border: 1px !important; + background-color: rgb(229, 229, 229) !important; +} +/* p { + font-size: 14px; + line-height: 25px; + color: #181919; + font-weight: 500; +} */ + +div p span, +div p a { + font-weight: 600; +} + +.form-control[readonly] { + background-color: transparent !important; +} + +/* .btn__sub:hover{ + color:#3c9a80 !important; +} */ + +.dis_button:hover { + color: #fff !important; +} + +/* select:invalid, select option[value="0"] { + color: #a3a6a6 !important; +} */ + +/* select:invalid { color:#a3a6a6; font-weight: 500; } */ +.field-icon { + float: right; + margin-left: -20px; + margin-right: -2px; + margin-top: -30px; + padding: 5px; + position: relative; + z-index: 2; + color: var(--light-blue); + cursor: pointer; + background: #fff; +} +/* +meter::-webkit-meter-bar {background: #e6e6e9;} +meter::-webkit-meter-optimum-value {background: green;} +meter::-webkit-meter-suboptimum-value{background:orange;} +meter::-webkit-meter-even-less-good-value{background:red;} */ + +/* meter::-webkit-meter-optimum-value { + background: red; +} */ +/* Firefox Pseudo Class */ +/* meter::-moz-meter-bar { + background: red; +} */ diff --git a/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.ts b/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.ts index 98f387a96e..371338f108 100644 --- a/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.ts +++ b/participant-manager/src/app/site-coordinator/account/change-password/change-password.component.ts @@ -9,6 +9,10 @@ import {ChangePassword} from '../shared/profile.model'; import {mustMatch, passwordValidator} from 'src/app/_helper/validator'; import {UnsubscribeOnDestroyAdapter} from 'src/app/unsubscribe-on-destroy-adapter'; import {HeaderDisplayService} from 'src/app/service/header-display.service'; +import { + ErrorCode, + getMessage as getErrorMessage, +} from 'src/app/shared/error.codes.enum'; @Component({ selector: 'app-change-password', templateUrl: './change-password.component.html', @@ -23,6 +27,18 @@ export class ChangePasswordComponent currentPasswordPlaceholder = 'Enter current password'; currentPasswordlabel = 'Current password'; hideClickable = true; + passwordMeterLow = '.25'; + passwordMeterHigh = '.75'; + passwordMeterValue = '.2'; + passwordMeterOptimum = '.8'; + meterStatus = ''; + submitted = false; + fieldTextType = false; + serviceName = ''; + consecutiveCharacter = ''; + passwordLength = ''; + userName = ''; + constructor( private readonly fb: FormBuilder, private readonly accountService: AccountService, @@ -45,7 +61,12 @@ export class ChangePasswordComponent validator: [mustMatch('newPassword', 'confirmPassword')], }, ); + this.onChange(); } + toggleFieldTextType() { + this.fieldTextType = !this.fieldTextType; + } + // eslint-disable-next-line @typescript-eslint/member-ordering passCriteria = ''; get ressetPassword() { return this.resetPasswordForm.controls; @@ -66,8 +87,13 @@ special characters.`); this.hideClickable = visible; }); } + getError(err: ErrorCode): string { + return getErrorMessage(err); + } changePassword(): void { if (!this.resetPasswordForm.valid) return; + this.submitted = true; + const changePassword: ChangePassword = { currentPassword: String( this.resetPasswordForm.controls['currentPassword'].value, @@ -84,7 +110,46 @@ special characters.`); void this.router.navigate(['/coordinator/studies/sites']); }); } - cancel() { + cancel(): void { void this.router.navigate(['/coordinator/studies/sites']); } + onChange(): void { + this.resetPasswordForm.valueChanges.subscribe(() => { + const secretkeylenth = String( + this.resetPasswordForm.controls['newPassword'].value, + ); + + if (secretkeylenth.length === 0) { + this.passwordMeterLow = ' '; + this.passwordMeterHigh = ' '; + this.passwordMeterValue = ' '; + this.passwordMeterOptimum = ' '; + this.meterStatus = ' '; + } else if (this.resetPasswordForm.controls['newPassword'].errors) { + this.passwordMeterLow = '.25'; + this.passwordMeterHigh = '.75'; + this.passwordMeterValue = '.2'; + this.passwordMeterOptimum = '.8'; + this.meterStatus = 'Weak'; + } else if (secretkeylenth.length === 8) { + this.passwordMeterLow = '.25'; + this.passwordMeterHigh = '.75'; + this.passwordMeterValue = '.5'; + this.passwordMeterOptimum = '.15'; + this.meterStatus = 'Fair'; + } else if (secretkeylenth.length > 8 && secretkeylenth.length <= 12) { + this.passwordMeterLow = '.10'; + this.passwordMeterHigh = '1'; + this.passwordMeterValue = '.7'; + this.passwordMeterOptimum = '.15'; + this.meterStatus = 'Good'; + } else if (secretkeylenth.length > 12) { + this.passwordMeterLow = '.10'; + this.passwordMeterHigh = '1'; + this.passwordMeterValue = '1'; + this.passwordMeterOptimum = '.20'; + this.meterStatus = 'Strong'; + } + }); + } } diff --git a/participant-manager/src/app/site-coordinator/location/add-location/add-location.component.html b/participant-manager/src/app/site-coordinator/location/add-location/add-location.component.html index 4a31dea367..4c95bb062f 100644 --- a/participant-manager/src/app/site-coordinator/location/add-location/add-location.component.html +++ b/participant-manager/src/app/site-coordinator/location/add-location/add-location.component.html @@ -32,6 +32,7 @@ required name="customId" #customId="ngModel" + pattern="^[A-Za-z0-9*()_+|:.-]*$" />
Location ID exceeds max length (15 characters) + + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.- + @@ -75,6 +84,7 @@ name="name" maxlength="200" #name="ngModel" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
- Location name exceeds max length (200 characters) + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.- Location name is invalid @@ -118,6 +132,7 @@ name="description" maxlength="500" #description="ngModel" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
Location description exceeds max length (500 characters) + + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.- + diff --git a/participant-manager/src/app/site-coordinator/location/edit-location/edit-location.component.html b/participant-manager/src/app/site-coordinator/location/edit-location/edit-location.component.html index 4c5ec64dd3..39a7691b74 100644 --- a/participant-manager/src/app/site-coordinator/location/edit-location/edit-location.component.html +++ b/participant-manager/src/app/site-coordinator/location/edit-location/edit-location.component.html @@ -26,6 +26,7 @@ name="name" maxlength="200" #name="ngModel" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
Location name exceeds max length (200 characters) + + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.- + Location name is invalid @@ -70,6 +81,7 @@ name="description" maxlength="500" #description="ngModel" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
Location description exceeds max length (500 characters) + + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.- + diff --git a/participant-manager/src/app/site-coordinator/sites/add-email/add-email.component.html b/participant-manager/src/app/site-coordinator/sites/add-email/add-email.component.html index 5e411f1069..3b16fc505a 100644 --- a/participant-manager/src/app/site-coordinator/sites/add-email/add-email.component.html +++ b/participant-manager/src/app/site-coordinator/sites/add-email/add-email.component.html @@ -13,8 +13,23 @@ required [(ngModel)]="model.email" name="email" + #email="ngModel" />
+
+ + Enter an email + + + Enter a valid email + +
diff --git a/participant-manager/src/app/site-coordinator/sites/add-site/add-site.component.ts b/participant-manager/src/app/site-coordinator/sites/add-site/add-site.component.ts index 290ea3e678..7bce77d9b4 100644 --- a/participant-manager/src/app/site-coordinator/sites/add-site/add-site.component.ts +++ b/participant-manager/src/app/site-coordinator/sites/add-site/add-site.component.ts @@ -7,8 +7,8 @@ import {Study} from '../../studies/shared/study.model'; import {AddSiteRequest} from '../shared/add.sites.request'; import {LocationService} from '../../location/shared/location.service'; import {ManageLocations} from '../../location/shared/location.model'; -import {ApiResponse} from 'src/app/entity/api.response.model'; import {Observable, of} from 'rxjs'; +import {Site, SiteResponse} from '../../studies/shared/site.model'; @Component({ selector: 'add-site', templateUrl: './add-site.component.html', @@ -18,12 +18,14 @@ export class AddSiteComponent extends UnsubscribeOnDestroyAdapter implements OnInit { @Input() study = {} as Study; - @Output() closeModalEvent = new EventEmitter(); + @Output() closeModalEvent = new EventEmitter(); @Output() cancelEvent = new EventEmitter(); newSite = {} as Study; site = {} as AddSiteRequest; location$: Observable = of(); disableButton = false; + addedSite = {} as Site; + siteName = ''; constructor( private readonly siteService: SitesService, private readonly toastr: ToastrService, @@ -47,8 +49,11 @@ export class AddSiteComponent this.disableButton = true; this.subs.add( this.siteService.add(this.site).subscribe( - (successResponse: ApiResponse) => { + (successResponse: SiteResponse) => { this.disableButton = false; + this.addedSite.id = successResponse.siteId; + this.addedSite.name = successResponse.siteName; + this.addedSite.invited = 0; if (getMessage(successResponse.code)) { this.toastr.success(getMessage(successResponse.code)); } else { @@ -58,13 +63,13 @@ export class AddSiteComponent }, () => { this.disableButton = false; - this.closeModal(); + this.cancel(); }, ), ); } closeModal(): void { - this.closeModalEvent.next(); + this.closeModalEvent.emit(this.addedSite); } cancel(): void { this.cancelEvent.next(); diff --git a/participant-manager/src/app/site-coordinator/sites/shared/sites.service.ts b/participant-manager/src/app/site-coordinator/sites/shared/sites.service.ts index 3176955769..9c25dd175a 100644 --- a/participant-manager/src/app/site-coordinator/sites/shared/sites.service.ts +++ b/participant-manager/src/app/site-coordinator/sites/shared/sites.service.ts @@ -3,7 +3,7 @@ import {Observable} from 'rxjs'; import {AddSiteRequest} from './add.sites.request'; import {HttpClient} from '@angular/common/http'; import {environment} from '@environment'; -import {ApiResponse} from 'src/app/entity/api.response.model'; +import {SiteResponse} from '../../studies/shared/site.model'; @Injectable({ providedIn: 'root', @@ -11,8 +11,8 @@ import {ApiResponse} from 'src/app/entity/api.response.model'; export class SitesService { constructor(private readonly http: HttpClient) {} - add(addSite: AddSiteRequest): Observable { - return this.http.post( + add(addSite: AddSiteRequest): Observable { + return this.http.post( `${environment.participantManagerDatastoreUrl}/sites`, addSite, ); diff --git a/participant-manager/src/app/site-coordinator/sites/site-details/site-details.component.html b/participant-manager/src/app/site-coordinator/sites/site-details/site-details.component.html index 82ff9a3f2d..124d233f30 100644 --- a/participant-manager/src/app/site-coordinator/sites/site-details/site-details.component.html +++ b/participant-manager/src/app/site-coordinator/sites/site-details/site-details.component.html @@ -643,8 +643,16 @@ enrollmentStatus.NotEligile " > + { - const result = this.newlyImportedParticipants.filter( - (newlyVreatedEmails) => - newlyVreatedEmails.email === participant.email, - ); - if (result.length > 0) { - participant.newlyCreatedUser = true; + if (this.activeTabForDisabled === OnboardingStatus.Disabled) { + const resultFromDisabled = this.userIds.filter( + (idsFromDisabled) => idsFromDisabled === participant.id, + ); + if (resultFromDisabled.length > 0) { + participant.newlyCreatedUser = true; + } + } else { + const result = this.newlyImportedParticipants.filter( + (newlyVreatedEmails) => + newlyVreatedEmails.email === participant.email, + ); + if (result.length > 0) { + participant.newlyCreatedUser = true; + } } return participant; }, @@ -129,9 +141,14 @@ export class SiteDetailsComponent tab === OnboardingStatus.New || tab === OnboardingStatus.Invited ? 'Disable invitation' : 'Enable invitation'; + this.activeTabForDisabled = this.activeTab; this.activeTab = tab; this.toggleDisplay = false; + this.userIdsBackup = this.userIds; this.userIds = []; + if (this.activeTabForDisabled === OnboardingStatus.Disabled) { + this.userIds = this.userIdsBackup; + } this.fetchSiteParticipant(tab); } diff --git a/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.html b/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.html index 49317c5246..6b0525fefe 100644 --- a/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.html +++ b/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.html @@ -147,6 +147,6 @@ diff --git a/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.ts b/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.ts index f53faef530..4f3f12bd57 100644 --- a/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.ts +++ b/participant-manager/src/app/site-coordinator/sites/site-list/site-list.component.ts @@ -56,9 +56,16 @@ export class SiteListComponent implements OnInit { 'Search by site or study ID or name', ); } - closeModal(): void { + closeModal(event: Site): void { + this.study.sites.push(event); + this.study.sites.sort((site1, site2): number => { + if (site1.name !== undefined && site2.name !== undefined) { + if (site1.name.toLowerCase() < site2.name.toLowerCase()) return -1; + if (site1.name.toLowerCase() > site2.name.toLowerCase()) return 1; + } + return 0; + }); this.modalRef.hide(); - this.getStudies(); } getStudies(): void { diff --git a/participant-manager/src/app/site-coordinator/studies/shared/site.model.ts b/participant-manager/src/app/site-coordinator/studies/shared/site.model.ts index cd8b7dd92c..b945414bbb 100644 --- a/participant-manager/src/app/site-coordinator/studies/shared/site.model.ts +++ b/participant-manager/src/app/site-coordinator/studies/shared/site.model.ts @@ -1,3 +1,5 @@ +import {ApiResponse} from 'src/app/entity/api.response.model'; + export interface Site { edit?: number; enrolled: number; @@ -10,3 +12,7 @@ export interface Site { export interface UpdateTargetEnrollmentRequest { targetEnrollment: number; } +export interface SiteResponse extends ApiResponse { + siteId: string; + siteName: string; +} diff --git a/participant-manager/src/app/site-coordinator/studies/study-details/study-details.component.ts b/participant-manager/src/app/site-coordinator/studies/study-details/study-details.component.ts index 0000061ae1..6911afe276 100644 --- a/participant-manager/src/app/site-coordinator/studies/study-details/study-details.component.ts +++ b/participant-manager/src/app/site-coordinator/studies/study-details/study-details.component.ts @@ -77,7 +77,8 @@ export class StudyDetailsComponent ).pipe( map(([studyDetails]) => { if ( - studyDetails.participantRegistryDetail.studyType === StudyType.Open + studyDetails.participantRegistryDetail.studyType === StudyType.Open && + this.searchTerm === '' ) { this.sharedService.updateSearchPlaceHolder( 'Search participant email', diff --git a/participant-manager/src/app/site-coordinator/user/new-user/new-user.component.html b/participant-manager/src/app/site-coordinator/user/new-user/new-user.component.html index 15c5bc3eb3..edf907dc4c 100644 --- a/participant-manager/src/app/site-coordinator/user/new-user/new-user.component.html +++ b/participant-manager/src/app/site-coordinator/user/new-user/new-user.component.html @@ -31,7 +31,7 @@ name="firstName" #firstName="ngModel" required - pattern=".*[^ ].*" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
- Please fill out this field + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.-
@@ -68,6 +69,7 @@ class="form-control" id="lname" placeholder="Enter last name" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
- Please fill out this field + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.-
diff --git a/participant-manager/src/app/site-coordinator/user/update-user/update-user.component.html b/participant-manager/src/app/site-coordinator/user/update-user/update-user.component.html index 997f6b582f..935884a788 100644 --- a/participant-manager/src/app/site-coordinator/user/update-user/update-user.component.html +++ b/participant-manager/src/app/site-coordinator/user/update-user/update-user.component.html @@ -62,7 +62,7 @@ name="firstName" #firstName="ngModel" required - pattern=".*[^ ].*" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
- Please fill out this field + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.-
@@ -103,6 +104,7 @@ class="form-control" id="lname" placeholder="Enter last name" + pattern="^(?!\s)[A-Za-z0-9\s*()_+|:.-]*$" />
- Please fill out this field + Please use characters from the following set only: A-Z a-z 0-9 + *()_+|:.-
diff --git a/participant-manager/src/assets/images/branding/landing-logo-old.png b/participant-manager/src/assets/images/branding/landing-logo-old.png new file mode 100644 index 0000000000..8d950cbff9 Binary files /dev/null and b/participant-manager/src/assets/images/branding/landing-logo-old.png differ diff --git a/participant-manager/src/assets/images/branding/landing-logo.png b/participant-manager/src/assets/images/branding/landing-logo.png index 8d950cbff9..3dd77aaa48 100644 Binary files a/participant-manager/src/assets/images/branding/landing-logo.png and b/participant-manager/src/assets/images/branding/landing-logo.png differ diff --git a/participant-manager/src/assets/images/landing-logo-old.png b/participant-manager/src/assets/images/landing-logo-old.png new file mode 100644 index 0000000000..ca14f019e0 Binary files /dev/null and b/participant-manager/src/assets/images/landing-logo-old.png differ diff --git a/participant-manager/src/assets/images/landing-logo.png b/participant-manager/src/assets/images/landing-logo.png index ca14f019e0..3dd77aaa48 100644 Binary files a/participant-manager/src/assets/images/landing-logo.png and b/participant-manager/src/assets/images/landing-logo.png differ diff --git a/participant-manager/src/styles.scss b/participant-manager/src/styles.scss index 10d0da7142..57a2dec827 100644 --- a/participant-manager/src/styles.scss +++ b/participant-manager/src/styles.scss @@ -12,3 +12,41 @@ font-family: "Muli", sans-serif; src: url("https://fonts.googleapis.com/css?family=Muli:200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i&display=swap"); } +meter, +progress { + width: 65%; +} + +@-moz-document url-prefix() { + meter { + font-size: 6px; + } +} +@supports (-moz-appearance: none) { + meter { + font-size: 6px; + } +} +meter::-webkit-meter-optimum-value { + font-size: 6px; +} + +meter::-webkit-meter-high-value { + font-size: 6px; +} +.weakPwd { + color: var(--orange-red); + font-size: 75%; +} +.fairPwd { + color: var(--yellow); + font-size: 75%; +} +.goodPwd { + color: var(--green); + font-size: 75%; +} +.strongPwd { + color: var(--green); + font-size: 75%; +} diff --git a/response-datastore/response-server-service/pom.xml b/response-datastore/response-server-service/pom.xml index 6c444f84db..a9d39ed824 100644 --- a/response-datastore/response-server-service/pom.xml +++ b/response-datastore/response-server-service/pom.xml @@ -8,7 +8,7 @@ org.springframework.boot spring-boot-starter-parent - 2.2.4.RELEASE + 2.2.6.RELEASE com.google.cloud.healthcare.fdamystudies @@ -98,16 +98,6 @@ org.springframework.boot spring-boot-starter - - - - org.springframework.security - spring-security-config - - - org.springframework.security - spring-security-web - org.springframework.boot spring-boot-starter-data-jpa @@ -143,7 +133,6 @@ com.fasterxml.jackson.core jackson-databind - 2.9.8 org.projectlombok @@ -176,6 +165,11 @@ org.skyscreamer jsonassert + + org.springframework.security + spring-security-crypto + + diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/FdaMystudiesResponseServerApplication.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/FdaMystudiesResponseServerApplication.java index c9d8b09bc8..6adfcd53d0 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/FdaMystudiesResponseServerApplication.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/FdaMystudiesResponseServerApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -10,7 +10,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication(scanBasePackages = {"com.google.cloud.healthcare.fdamystudies"}) @EntityScan("com.google.cloud.healthcare.fdamystudies.response.model") public class FdaMystudiesResponseServerApplication { diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/bean/StudyMetadataBean.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/bean/StudyMetadataBean.java index 10b382d0b9..a0605354da 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/bean/StudyMetadataBean.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/bean/StudyMetadataBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -36,4 +36,5 @@ public class StudyMetadataBean implements Serializable { private String appName; private String appDescription; private String logoImageUrl; + private String contactEmail; } diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/ResponseServerEvent.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/ResponseServerEvent.java index f9e286e44a..9b4a2a08bc 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/ResponseServerEvent.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/common/ResponseServerEvent.java @@ -180,20 +180,6 @@ public enum ResponseServerEvent implements AuditLogEvent { + " activity responses.", "WITHDRAWAL_INFORMATION_UPDATE_FAILED"), - PARTICIPANT_RESPONSE_DATA_DELETED( - RESPONSE_DATASTORE, - RESPONSE_DATASTORE, - null, - "All response data belonging to participant deleted.", - "PARTICIPANT_RESPONSE_DATA_DELETED"), - - PARTICIPANT_RESPONSE_DATA_DELETION_FAILED( - RESPONSE_DATASTORE, - RESPONSE_DATASTORE, - null, - "1 or more of the participant's response datasets failed to get deleted.", - "PARTICIPANT_RESPONSE_DATA_DELETION_FAILED"), - ACTIVITY_STATE_SAVED_OR_UPDATED_AFTER_RESPONSE_SUBMISSION( RESPONSE_DATASTORE, RESPONSE_DATASTORE, @@ -222,8 +208,7 @@ public enum ResponseServerEvent implements AuditLogEvent { RESPONSE_DATASTORE, null, "Information about participant's withdrawal from study received" - + " as withdrawal timestamp '${withdrawal_timetamp}' and " - + "data-retention setting '${dataretention_setting}'.", + + " as withdrawal timestamp '${withdrawal_timetamp}'.", "PARTICIPANT_WITHDRAWAL_INTIMATION_FROM_PARTICIPANT_DATASTORE"), ACTIVITY_STATE_SAVED_OR_UPDATED( diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AppConfig.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AppConfig.java index 6399007be7..8eddd8c7e8 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AppConfig.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AppConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2020-2021 Google LLC * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at @@ -8,7 +8,15 @@ package com.google.cloud.healthcare.fdamystudies.config; +import com.google.cloud.GcpLaunchStage.Beta; import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration -public class AppConfig extends CommonModuleConfiguration {} +public class AppConfig extends CommonModuleConfiguration { + + @Beta + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AuthenticationEntryPointImpl.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AuthenticationEntryPointImpl.java deleted file mode 100644 index 3e0ce80f31..0000000000 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/config/AuthenticationEntryPointImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Use of this source code is governed by an MIT-style - * license that can be found in the LICENSE file or at - * https://opensource.org/licenses/MIT. - */ - -package com.google.cloud.healthcare.fdamystudies.config; - -import java.io.IOException; -import java.io.PrintWriter; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -@Component -public class AuthenticationEntryPointImpl extends BasicAuthenticationEntryPoint { - - @Override - public void commence( - HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) - throws IOException { - response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName()); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - PrintWriter writer = response.getWriter(); - writer.println("HTTP Status 401 - " + authEx.getMessage()); - } - - @Override - public void afterPropertiesSet() { - // RealmName appears in the login window (Firefox). - setRealmName("response-datastore"); - super.afterPropertiesSet(); - } -} diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ParticipantIdController.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ParticipantIdController.java index 406be576e5..0cf4ca447b 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ParticipantIdController.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ParticipantIdController.java @@ -21,6 +21,8 @@ import com.google.cloud.healthcare.fdamystudies.utils.AppConstants; import com.google.cloud.healthcare.fdamystudies.utils.AppUtil; import com.google.cloud.healthcare.fdamystudies.utils.ErrorCode; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -33,6 +35,10 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; +@Api( + tags = "Generate participant id", + value = "Generate participant id based on study", + description = "Generate participant id based on study") @RestController public class ParticipantIdController { @@ -42,6 +48,7 @@ public class ParticipantIdController { private static final Logger logger = LoggerFactory.getLogger(ParticipantIdController.class); + @ApiOperation(value = "Generate participant id from response datastore") @PostMapping("/participant/add") public ResponseEntity addParticipantIdentifier( @RequestHeader("appId") String applicationId, diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseController.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseController.java index 9f2ec58140..94082ab374 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseController.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseController.java @@ -26,7 +26,6 @@ import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.DATA_SHARING_CONSENT_VALUE_RETRIEVED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_ACTIVITY_DATA_DELETED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_ID_INVALID; -import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_RESPONSE_DATA_DELETED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_WITHDRAWAL_INTIMATION_FROM_PARTICIPANT_DATASTORE; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.READ_OPERATION_FOR_RESPONSE_DATA_FAILED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.READ_OPERATION_FOR_RESPONSE_DATA_SUCCEEDED; @@ -56,6 +55,8 @@ import com.google.cloud.healthcare.fdamystudies.utils.AppConstants; import com.google.cloud.healthcare.fdamystudies.utils.AppUtil; import com.google.cloud.healthcare.fdamystudies.utils.ErrorCode; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import java.sql.Timestamp; import java.time.Instant; import java.util.ArrayList; @@ -77,6 +78,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Api(tags = "Process activity response", description = "Response activity operation performed") @RestController public class ProcessActivityResponseController { @Autowired private ParticipantService participantService; @@ -93,6 +95,8 @@ public class ProcessActivityResponseController { private static final Logger logger = LoggerFactory.getLogger(ProcessActivityResponseController.class); + @ApiOperation( + value = "Process activity response for participant and store in Google Cloud Firestore") @PostMapping("/participant/process-response") public ResponseEntity processActivityResponseForParticipant( @RequestBody ActivityResponseBean questionnaireActivityResponseBean, @@ -389,6 +393,7 @@ public ResponseEntity processActivityResponseForParticipant( } } + @ApiOperation(value = "Get activity response data for participant from Google Cloud Firestore") @GetMapping("/participant/getresponse") public ResponseEntity getActivityResponseDataForParticipant( @RequestParam("appId") String applicationId, @@ -486,13 +491,13 @@ public ResponseEntity getActivityResponseDataForParticipant( } } + @ApiOperation(value = "Withdraw participant from study from response datastore") @PostMapping("/participant/withdraw") public ResponseEntity withdrawParticipantFromStudy( @RequestHeader String appId, @RequestParam(name = "studyId") String studyId, @RequestParam(name = "studyVersion") String studyVersion, @RequestParam(name = "participantId") String participantId, - @RequestParam(name = "deleteResponses") String deleteResponses, HttpServletRequest request) { AuditLogEventRequest auditRequest = AuditEventMapper.fromHttpServletRequest(request); @@ -513,23 +518,15 @@ public ResponseEntity withdrawParticipantFromStudy( auditRequest.setParticipantId(participantId); Map map = new HashMap<>(); map.put("withdrawal_timetamp", Timestamp.from(Instant.now()).toString()); - map.put("dataretention_setting", deleteResponses); responseServerAuditLogHelper.logEvent( PARTICIPANT_WITHDRAWAL_INTIMATION_FROM_PARTICIPANT_DATASTORE, auditRequest, map); - if (!StringUtils.isBlank(deleteResponses) - && deleteResponses.equalsIgnoreCase(AppConstants.TRUE_STR)) { - activityResponseProcessorService.deleteActivityResponseDataForParticipant( - studyId, participantId, auditRequest); - responseDataUpdate = true; - responseServerAuditLogHelper.logEvent(PARTICIPANT_RESPONSE_DATA_DELETED, auditRequest); - } else { - activityResponseProcessorService.updateWithdrawalStatusForParticipant( - studyId, participantId); - responseDataUpdate = true; + activityResponseProcessorService.updateWithdrawalStatusForParticipant( + studyId, participantId); + responseDataUpdate = true; + + responseServerAuditLogHelper.logEvent(WITHDRAWAL_INFORMATION_UPDATED, auditRequest); - responseServerAuditLogHelper.logEvent(WITHDRAWAL_INFORMATION_UPDATED, auditRequest); - } // Delete all participant activity state from the table participantActivityStateResponseService.deleteParticipantActivites(studyId, participantId); SuccessResponseBean srBean = new SuccessResponseBean(); @@ -548,9 +545,7 @@ public ResponseEntity withdrawParticipantFromStudy( logger.error( "Could not successfully withdraw for participant.\n Study Id: " + studyId - + "\n Particpant Id: " - + " Withdrawal Action " - + deleteResponses); + + "\n Particpant Id"); return new ResponseEntity<>(errorBean, HttpStatus.BAD_REQUEST); } else { responseServerAuditLogHelper.logEvent(WITHDRAWAL_INFORMATION_UPDATE_FAILED, auditRequest); @@ -563,9 +558,7 @@ public ResponseEntity withdrawParticipantFromStudy( logger.error( "Could not successfully withdraw for participant.\n Study Id: " + studyId - + "\n Particpant Id: " - + " Withdrawal Action " - + deleteResponses); + + "\n Particpant Id"); return new ResponseEntity<>(errorBean, HttpStatus.BAD_REQUEST); } } diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityStateController.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityStateController.java index e37a56ca81..d2e01205a5 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityStateController.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityStateController.java @@ -30,6 +30,8 @@ import com.google.cloud.healthcare.fdamystudies.utils.AppConstants; import com.google.cloud.healthcare.fdamystudies.utils.AppUtil; import com.google.cloud.healthcare.fdamystudies.utils.ErrorCode; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -50,6 +52,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Api(tags = "Process activity state", description = "Activity state operation performed") @RestController public class ProcessActivityStateController { @Autowired @@ -60,6 +63,7 @@ public class ProcessActivityStateController { private static final Logger logger = LoggerFactory.getLogger(ProcessActivityStateController.class); + @ApiOperation(value = "Get activity state") @GetMapping( value = "/participant/get-activity-state", consumes = MediaType.APPLICATION_JSON_VALUE, @@ -96,6 +100,7 @@ public ResponseEntity getActivityState( } } + @ApiOperation(value = "Update activity state") @PostMapping("/participant/update-activity-state") public ResponseEntity updateActivityState( @RequestBody ActivityStateRequestBean activityStateRequestBean, diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataController.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataController.java index 282e91477a..55da71b7fc 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataController.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataController.java @@ -20,6 +20,8 @@ import com.google.cloud.healthcare.fdamystudies.utils.AppUtil; import com.google.cloud.healthcare.fdamystudies.utils.ErrorCode; import com.google.cloud.healthcare.fdamystudies.utils.ProcessResponseException; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import java.beans.IntrospectionException; import java.lang.reflect.InvocationTargetException; import javax.servlet.http.HttpServletRequest; @@ -33,6 +35,11 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +@Api( + tags = "Study metadata", + value = "Study metadata", + description = + "Operations pertaining to study metadata, once study is published from study builder") @RestController public class StudyMetadataController { @Autowired private StudyMetadataService studyMetadataService; @@ -41,6 +48,10 @@ public class StudyMetadataController { private static final Logger logger = LoggerFactory.getLogger(StudyMetadataController.class); + @ApiOperation( + value = + "Add or update study metadata in response datastore" + + " when a study is published from study builder") @PostMapping("/studymetadata") public ResponseEntity addUpdateStudyMetadata( @RequestBody StudyMetadataBean studyMetadataBean, HttpServletRequest request) diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorService.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorService.java index e9ec5b9a0a..25a846b1ac 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorService.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorService.java @@ -26,10 +26,6 @@ StoredResponseBean getActivityResponseDataForParticipant( String studyId, String siteId, String participantId, String activityId, String questionKey) throws ProcessResponseException; - void deleteActivityResponseDataForParticipant( - String studyId, String participantId, AuditLogEventRequest auditRequest) - throws ProcessResponseException; - void updateWithdrawalStatusForParticipant(String studyId, String participantId) throws ProcessResponseException; } diff --git a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorServiceImpl.java b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorServiceImpl.java index 982b89f77c..c154466e25 100644 --- a/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorServiceImpl.java +++ b/response-datastore/response-server-service/src/main/java/com/google/cloud/healthcare/fdamystudies/service/ActivityResponseProcessorServiceImpl.java @@ -14,7 +14,6 @@ import static com.google.cloud.healthcare.fdamystudies.common.CommonConstants.RUN_ID; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.ACTIVITY_METADATA_CONJOINED_WITH_RESPONSE_DATA; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.ACTIVITY_METADATA_CONJOINING_WITH_RESPONSE_DATA_FAILED; -import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_RESPONSE_DATA_DELETION_FAILED; import com.google.cloud.healthcare.fdamystudies.bean.ActivityMetadataBean; import com.google.cloud.healthcare.fdamystudies.bean.ActivityResponseBean; @@ -42,7 +41,6 @@ import java.util.stream.Collectors; import org.apache.commons.collections4.map.HashedMap; import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -135,24 +133,6 @@ public void saveActivityResponseDataForParticipant( "ActivityResponseProcessorServiceImpl saveActivityResponseDataForParticipant() - ends "); } - @Override - public void deleteActivityResponseDataForParticipant( - String studyId, String participantId, AuditLogEventRequest auditRequest) - throws ProcessResponseException { - if (Strings.isBlank(studyId) || Strings.isBlank(participantId)) { - responseServerAuditLogHelper.logEvent( - PARTICIPANT_RESPONSE_DATA_DELETION_FAILED, auditRequest); - throw new ProcessResponseException("Required input parameter is blank or null"); - } else { - - responsesDao.deleteActivityResponseDataForParticipant( - AppUtil.makeStudyCollectionName(studyId), - studyId, - AppConstants.ACTIVITIES_COLLECTION_NAME, - participantId); - } - } - @Override public StoredResponseBean getActivityResponseDataForParticipant( String studyId, String siteId, String participantId, String activityId, String questionKey) diff --git a/response-datastore/response-server-service/src/main/resources/application.properties b/response-datastore/response-server-service/src/main/resources/application.properties index 69b5df51d5..ebe3e3a19a 100644 --- a/response-datastore/response-server-service/src/main/resources/application.properties +++ b/response-datastore/response-server-service/src/main/resources/application.properties @@ -23,7 +23,7 @@ spring.datasource.password=${DB_PASS} spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.ddl-auto=none # Refer https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#data-properties spring.datasource.type=com.zaxxer.hikari.HikariDataSource diff --git a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/SwaggerGeneratorTest.java b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/SwaggerGeneratorTest.java new file mode 100644 index 0000000000..e6559980e2 --- /dev/null +++ b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/SwaggerGeneratorTest.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020-2021 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +package com.google.cloud.healthcare.fdamystudies; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.healthcare.fdamystudies.common.BaseMockIT; +import java.io.File; +import org.junit.jupiter.api.Test; + +class SwaggerGeneratorTest extends BaseMockIT { + + @Test + public void createApiDocs() throws Exception { + String documentPath = generateApiDocs(); + assertTrue(new File(documentPath).exists()); + } +} diff --git a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseControllerTest.java b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseControllerTest.java index 495563cd96..38127df21f 100644 --- a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseControllerTest.java +++ b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/ProcessActivityResponseControllerTest.java @@ -22,7 +22,6 @@ import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.DATA_SHARING_CONSENT_VALUE_RETRIEVED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_ACTIVITY_DATA_DELETED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_ID_INVALID; -import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_RESPONSE_DATA_DELETED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.PARTICIPANT_WITHDRAWAL_INTIMATION_FROM_PARTICIPANT_DATASTORE; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.READ_OPERATION_FOR_RESPONSE_DATA_SUCCEEDED; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.WITHDRAWAL_INFORMATION_RETRIEVED; @@ -401,68 +400,6 @@ public void shouldUpdateWithdrawStatusParticipantFromStudy() throws Exception { PARTICIPANT_ACTIVITY_DATA_DELETED); } - @Test - public void shouldDeleteParticipantFromStudy() throws Exception { - // Step-1 deleteActivityResponseDataForParticipant - doNothing() - .when(responsesDaoMock) - .deleteActivityResponseDataForParticipant( - STUDY_COLLECTION_NAME_VALUE, - STUDY_ID_VALUE, - ACTIVITY_COLLECTION_NAME_VALUE, - participantBo.getParticipantId()); - - // Step-2 call API to delete participant from study - ActivityResponseBean activityResponseBean = setActivityResponseBean(); - HttpHeaders headers = TestUtils.newCommonHeaders(); - headers.add(USER_ID_HEADER, VALID_USER_ID); - mockMvc - .perform( - post(ApiEndpoint.WITHDRAW.getPath()) - .contextPath(getContextPath()) - .content(JsonUtils.asJsonString(activityResponseBean)) - .headers(headers) - .queryParam("deleteResponses", "true") - .queryParam("participantId", participantBo.getParticipantId()) - .queryParam("studyId", STUDY_ID_VALUE) - .queryParam("studyVersion", STUDY_VERSION)) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message", is(SUCCESS))); - - // Step 3: verify deleted values - List participantActivitiesList = - participantActivitiesRepository.findByStudyIdAndParticipantId( - STUDY_ID_VALUE, participantBo.getParticipantId()); - assertTrue(participantActivitiesList.isEmpty()); - - verify(responsesDaoMock) - .deleteActivityResponseDataForParticipant( - studyCollectionNameCaptor.capture(), - studyIdCaptor.capture(), - activityCollectionNameCaptor.capture(), - participantIdCaptor.capture()); - - // Step 4: assert argument capture - assertEquals(STUDY_COLLECTION_NAME_VALUE, studyCollectionNameCaptor.getValue()); - assertEquals(STUDY_ID_VALUE, studyIdCaptor.getValue()); - assertEquals(ACTIVITY_COLLECTION_NAME_VALUE, activityCollectionNameCaptor.getValue()); - assertEquals(participantBo.getParticipantId(), participantIdCaptor.getValue()); - - AuditLogEventRequest auditRequest = new AuditLogEventRequest(); - auditRequest.setStudyId(STUDY_ID_VALUE); - auditRequest.setStudyVersion(STUDY_VERSION); - auditRequest.setParticipantId(participantBo.getParticipantId()); - auditRequest.setUserId(VALID_USER_ID); - - Map auditEventMap = new HashedMap<>(); - auditEventMap.put(PARTICIPANT_RESPONSE_DATA_DELETED.getEventCode(), auditRequest); - auditEventMap.put(PARTICIPANT_ACTIVITY_DATA_DELETED.getEventCode(), auditRequest); - - verifyAuditEventCall( - auditEventMap, PARTICIPANT_RESPONSE_DATA_DELETED, PARTICIPANT_ACTIVITY_DATA_DELETED); - } - @Test public void shouldReturnBadRequestForEmptyInputsOfWithdraw() throws Exception { ActivityResponseBean activityResponseBean = setActivityResponseBean(); diff --git a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataControllerTest.java b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataControllerTest.java index 960802d436..8aebfc0601 100644 --- a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataControllerTest.java +++ b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/controller/StudyMetadataControllerTest.java @@ -11,6 +11,7 @@ import static com.google.cloud.healthcare.fdamystudies.common.JsonUtils.asJsonString; import static com.google.cloud.healthcare.fdamystudies.common.JsonUtils.readJsonFile; import static com.google.cloud.healthcare.fdamystudies.common.ResponseServerEvent.STUDY_METADATA_RECEIVED; +import static com.google.cloud.healthcare.fdamystudies.utils.Constants.CONTACT_EMAIL_ID; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; @@ -139,6 +140,7 @@ private StudyMetadataBean createValidStudyMetadataBean() { APP_ID_VALUE, "Test App", "Test app for population health study", - LOGO_IMAGE_URL); + LOGO_IMAGE_URL, + CONTACT_EMAIL_ID); } } diff --git a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/utils/Constants.java b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/utils/Constants.java index 7137b6220d..b955a7cfd9 100644 --- a/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/utils/Constants.java +++ b/response-datastore/response-server-service/src/test/java/com/google/cloud/healthcare/fdamystudies/utils/Constants.java @@ -58,4 +58,6 @@ private Constants() {} public static final String SUCCESS = "SUCCESS"; public static final String ACTIVITY_COLLECTION_NAME_VALUE = "Activities"; + + public static final String CONTACT_EMAIL_ID = "contactemail@gmail.com"; } diff --git a/study-builder/fdahpStudyDesigner/pom.xml b/study-builder/fdahpStudyDesigner/pom.xml index 039a28924b..287e2b0689 100644 --- a/study-builder/fdahpStudyDesigner/pom.xml +++ b/study-builder/fdahpStudyDesigner/pom.xml @@ -556,10 +556,15 @@ commons-text 1.9 - + com.google.cloud google-cloud-storage 1.96.0 + + org.apache.commons + commons-text + 1.4 + \ No newline at end of file diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bean/StudyDetailsBean.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bean/StudyDetailsBean.java index e1c563f64d..d18a3a9e87 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bean/StudyDetailsBean.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bean/StudyDetailsBean.java @@ -35,7 +35,16 @@ public class StudyDetailsBean { appId = "", appName = "", appDescription = "", - logoImageUrl = ""; + logoImageUrl = "", + contactEmail = ""; + + public String getContactEmail() { + return contactEmail; + } + + public void setContactEmail(String contactEmail) { + this.contactEmail = contactEmail; + } public String getStudyId() { return studyId; diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/ConsentBo.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/ConsentBo.java index 8f39a41887..f9a0f15b70 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/ConsentBo.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/ConsentBo.java @@ -1,5 +1,6 @@ /* * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies @@ -48,6 +49,7 @@ public class ConsentBo implements Serializable { private static final long serialVersionUID = 5564057544960167010L; + @Deprecated @Column(name = "aggrement_of_consent") private String aggrementOfTheConsent; diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/StudyBo.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/StudyBo.java index 767972f2c9..2a38f097d5 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/StudyBo.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/bo/StudyBo.java @@ -1,23 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. * Copyright 2020-2021 Google LLC - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as - * Contract no. HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.bo; @@ -60,9 +46,11 @@ public class StudyBo implements Serializable { private static final long serialVersionUID = 2147840266295837728L; + @Deprecated @Column(name = "allow_rejoin") private String allowRejoin; + @Deprecated @Column(name = "allow_rejoin_text") private String allowRejoinText; @@ -140,6 +128,7 @@ public class StudyBo implements Serializable { @Column(name = "research_sponsor") private String researchSponsor; + @Deprecated @Column(name = "retain_participant") private String retainParticipant; diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEvent.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEvent.java index a2f2350ada..91b209a1c2 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEvent.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEvent.java @@ -335,7 +335,7 @@ public enum StudyBuilderAuditEvent { STUDY_BUILDER, STUDY_DATASTORE, null, - "Settings section marked complete with enrollment setting '${enrollment_setting}', re-join setting '${rejoin_setting}' and data-retention setting '${dataretention_setting}'. ", + "Settings section marked complete with enrollment setting '${enrollment_setting}'.", "STUDY_SETTINGS_MARKED_COMPLETE"), STUDY_DEACTIVATED(STUDY_BUILDER, STUDY_DATASTORE, null, null, "STUDY_DEACTIVATED"), @@ -484,7 +484,21 @@ public enum StudyBuilderAuditEvent { PARTICIPANT_USER_DATASTORE, null, "Failed to send app/study notifications metadata.", - "NOTIFICATION_METADATA_SEND_OPERATION_FAILED"); + "NOTIFICATION_METADATA_SEND_OPERATION_FAILED"), + + STUDY_NEW_ACTIVE_TASK_CREATED( + STUDY_BUILDER, + STUDY_DATASTORE, + null, + "New Active task created (activity ID - ${activetask_id}).", + "STUDY_NEW_ACTIVE_TASK_CREATED"), + + STUDY_QUESTIONNAIRE_MARKED_COMPLETED( + STUDY_BUILDER, + STUDY_DATASTORE, + null, + "Questionnaire marked completed (activity ID - ${questionnaire_id}).", + "STUDY_QUESTIONNAIRE_MARKED_COMPLETED"); private final PlatformComponent source; private final PlatformComponent destination; diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEventHelper.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEventHelper.java index 3cb49d2692..5986ee4085 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEventHelper.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/common/StudyBuilderAuditEventHelper.java @@ -1,3 +1,11 @@ +/* + * Copyright 2020-2021 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + package com.fdahpstudydesigner.common; import com.fdahpstudydesigner.bean.AuditLogEventRequest; diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/CleanPropertyAdvice.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/CleanPropertyAdvice.java index 3e625d684b..ae5e29ae72 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/CleanPropertyAdvice.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/CleanPropertyAdvice.java @@ -1,6 +1,15 @@ +/* + * Copyright 2020-2021 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + package com.fdahpstudydesigner.controller; import java.beans.PropertyEditorSupport; +import org.apache.commons.text.StringEscapeUtils; import org.jsoup.Jsoup; import org.jsoup.safety.Whitelist; import org.springframework.web.bind.WebDataBinder; @@ -16,7 +25,7 @@ public static class CustomPropertyEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { String safe = Jsoup.clean(text, Whitelist.simpleText()); - setValue(safe); + setValue(StringEscapeUtils.unescapeHtml4(safe)); } } diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/LoginController.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/LoginController.java index c591695e79..f22f7119c0 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/LoginController.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/LoginController.java @@ -73,8 +73,9 @@ public class LoginController { public ModelAndView addPassword(HttpServletRequest request, UserBO userBO) { logger.info("LoginController - addPassword() - Starts"); ModelAndView mv = new ModelAndView("redirect:sessionOut.do"); + Map propMap = FdahpStudyDesignerUtil.getAppProperties(); + String sucMsg = ""; try { - Map propMap = FdahpStudyDesignerUtil.getAppProperties(); HttpSession session = request.getSession(false); SessionObject sesObj = (SessionObject) session.getAttribute(FdahpStudyDesignerConstants.SESSION_OBJECT); @@ -101,15 +102,13 @@ public ModelAndView addPassword(HttpServletRequest request, UserBO userBO) { mv = new ModelAndView("redirect:createPassword.do?securityToken=" + securityToken); } else { if ((userBO != null) && StringUtils.isNotEmpty(userBO.getFirstName())) { - request - .getSession(false) - .setAttribute("sucMsg", propMap.get("user.newaccount.success.msg")); + sucMsg = propMap.get("user.account.setup.msg"); } else { - request - .getSession(false) - .setAttribute("sucMsg", propMap.get("user.newpassword.success.msg")); + sucMsg = propMap.get("user.newpassword.success.msg"); } + mv = new ModelAndView("redirect:sessionOut.do?sucMsg=" + sucMsg); } + } catch (Exception e) { logger.error("LoginController - addPassword() - ERROR ", e); } @@ -239,7 +238,6 @@ public ModelAndView forgotPassword(HttpServletRequest request) { : ""; message = loginService.sendPasswordResetLinkToMail(request, email, "", "", auditRequest); if (FdahpStudyDesignerConstants.SUCCESS.equals(message)) { - auditRequest.setUserId(request.getParameter("email")); request.getSession().setAttribute("sucMsg", propMap.get("user.forgot.success.msg")); } else { request.getSession().setAttribute("errMsg", message); diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/NotificationController.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/NotificationController.java index 3ff11506c3..2b090d7fce 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/NotificationController.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/NotificationController.java @@ -1,24 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: + * Copyright 2020-2021 Google LLC * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.controller; @@ -363,7 +348,7 @@ public ModelAndView viewNotificationList(HttpServletRequest request) { && notification .getNotificationScheduleType() .equals(FdahpStudyDesignerConstants.NOTIFICATION_NOTIMMEDIATE)) { - notification.setCheckNotificationSendingStatus("Not sent"); + notification.setCheckNotificationSendingStatus("Scheduled"); } else if (!notification.isNotificationSent() && notification .getNotificationScheduleType() diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyActiveTasksController.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyActiveTasksController.java index fe2d859789..5efbdd9676 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyActiveTasksController.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyActiveTasksController.java @@ -27,6 +27,8 @@ import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_ACTIVE_TASK_MARKED_COMPLETE; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_ACTIVE_TASK_SAVED_OR_UPDATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_ACTIVE_TASK_SECTION_MARKED_COMPLETE; +import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_NEW_ACTIVE_TASK_CREATED; +import static com.fdahpstudydesigner.util.FdahpStudyDesignerConstants.ADD; import com.fdahpstudydesigner.bean.ActiveStatisticsBean; import com.fdahpstudydesigner.bean.AuditLogEventRequest; @@ -37,6 +39,7 @@ import com.fdahpstudydesigner.bo.AnchorDateTypeBo; import com.fdahpstudydesigner.bo.StatisticImageListBo; import com.fdahpstudydesigner.bo.StudyBo; +import com.fdahpstudydesigner.common.StudyBuilderAuditEvent; import com.fdahpstudydesigner.common.StudyBuilderAuditEventHelper; import com.fdahpstudydesigner.mapper.AuditEventMapper; import com.fdahpstudydesigner.service.StudyActiveTasksService; @@ -520,8 +523,11 @@ public ModelAndView saveOrUpdateActiveTaskContent( return new ModelAndView("redirect:/adminStudies/viewStudyActiveTasks.do", map); } else { - auditLogEventHelper.logEvent( - STUDY_ACTIVE_TASK_SAVED_OR_UPDATED, auditRequest, values); + StudyBuilderAuditEvent event = + ADD.equalsIgnoreCase(activeTaskBo.getActionPage()) + ? STUDY_NEW_ACTIVE_TASK_CREATED + : STUDY_ACTIVE_TASK_SAVED_OR_UPDATED; + auditLogEventHelper.logEvent(event, auditRequest, values); request .getSession() .setAttribute( diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyController.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyController.java index b3cbd447eb..b5e3186cd8 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyController.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyController.java @@ -1,23 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. * Copyright 2020-2021 Google LLC - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as - * Contract no. HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.controller; @@ -2218,7 +2204,6 @@ public ModelAndView questionnaireMarkAsCompleted(HttpServletRequest request) { sesObj, customStudyId); if (message.equals(FdahpStudyDesignerConstants.SUCCESS)) { - // STUDY_QUESTIONNAIRES_SECTION_MARKED_COMPLETE StudyBo studyBo = studyService.getStudyById(studyId, sesObj.getUserId()); if (studyBo != null) { auditRequest.setStudyVersion(studyBo.getVersion().toString()); @@ -3462,7 +3447,7 @@ public ModelAndView saveOrUpdateResource(HttpServletRequest request, ResourceBO sessionStudyCount + FdahpStudyDesignerConstants.SUC_MSG, propMap.get(FdahpStudyDesignerConstants.SAVE_STUDY_SUCCESS_MESSAGE)); } else { - auditLogEventHelper.logEvent(STUDY_RESOURCE_SAVED_OR_UPDATED, auditRequest, values); + auditLogEventHelper.logEvent(STUDY_RESOURCE_MARKED_COMPLETED, auditRequest, values); request .getSession() .setAttribute( @@ -5063,7 +5048,7 @@ public ModelAndView viewStudyNotificationList(HttpServletRequest request) { && notification .getNotificationScheduleType() .equals(FdahpStudyDesignerConstants.NOTIFICATION_NOTIMMEDIATE)) { - notification.setCheckNotificationSendingStatus("Not sent"); + notification.setCheckNotificationSendingStatus("Scheduled"); } else if (!notification.isNotificationSent() && notification .getNotificationScheduleType() diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyQuestionnaireController.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyQuestionnaireController.java index 2fef388ea4..a8b4ef6607 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyQuestionnaireController.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/StudyQuestionnaireController.java @@ -11,6 +11,7 @@ import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_ACTIVE_TASK_SECTION_MARKED_COMPLETE; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_NEW_QUESTIONNAIRE_CREATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTIONNAIRE_DELETED; +import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTIONNAIRE_MARKED_COMPLETED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTIONNAIRE_SAVED_OR_UPDATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTION_STEP_IN_FORM_DELETED; import static com.fdahpstudydesigner.common.StudyBuilderConstants.FORM_ID; @@ -2399,6 +2400,7 @@ public void saveQuestionnaireSchedule(HttpServletRequest request, HttpServletRes QuestionnaireBo questionnaireBo = null; String customStudyId = ""; try { + AuditLogEventRequest auditRequest = AuditEventMapper.fromHttpServletRequest(request); SessionObject sesObj = (SessionObject) request.getSession().getAttribute(FdahpStudyDesignerConstants.SESSION_OBJECT); @@ -2445,11 +2447,14 @@ public void saveQuestionnaireSchedule(HttpServletRequest request, HttpServletRes .getSession() .getAttribute( sessionStudyCount + FdahpStudyDesignerConstants.CUSTOM_STUDY_ID); + StudyBo studyBo = studyService.getStudyById(studyId, sesObj.getUserId()); + auditRequest.setStudyId(customStudyId); + auditRequest.setStudyVersion(studyBo.getVersion().toString()); + auditRequest.setAppId(studyBo.getAppId()); updateQuestionnaireBo = studyQuestionnaireService.saveOrUpdateQuestionnaire( questionnaireBo, sesObj, customStudyId); if (updateQuestionnaireBo != null) { - // TODO:STUDY_QUESTIONNAIRE_SAVED_OR_UPDATED jsonobject.put("questionnaireId", updateQuestionnaireBo.getId()); if (updateQuestionnaireBo.getQuestionnairesFrequenciesBo() != null) { jsonobject.put( @@ -2464,6 +2469,18 @@ public void saveQuestionnaireSchedule(HttpServletRequest request, HttpServletRes sesObj, customStudyId); } + Map values = new HashMap<>(); + values.put("questionnaire_id", updateQuestionnaireBo.getId().toString()); + StudyBuilderAuditEvent event = + questionnaireBo.getId() != null + ? STUDY_QUESTIONNAIRE_SAVED_OR_UPDATED + : STUDY_NEW_QUESTIONNAIRE_CREATED; + auditLogEventHelper.logEvent(event, auditRequest, values); + + if (questionnaireBo.getStatus()) { + auditLogEventHelper.logEvent( + STUDY_QUESTIONNAIRE_MARKED_COMPLETED, auditRequest, values); + } message = FdahpStudyDesignerConstants.SUCCESS; } } diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/UsersController.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/UsersController.java index 3e051ef080..514ebead24 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/UsersController.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/controller/UsersController.java @@ -1,24 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: + * Copyright 2020-2021 Google LLC * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.controller; @@ -32,7 +17,6 @@ import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_ENFORCEMENT_FOR_ALL_USERS_EMAIL_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_ENFORCEMENT_FOR_ALL_USERS_EMAIL_SENT; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_ENFORCEMENT_EMAIL_SENT; -import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_HELP_EMAIL_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_ACCOUNT_UPDATED_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_RECORD_VIEWED; @@ -183,10 +167,7 @@ public ModelAndView addOrUpdateUserDetails( SessionObject userSession = (SessionObject) session.getAttribute(FdahpStudyDesignerConstants.SESSION_OBJECT); if (null != userSession) { - String manageUsers = - FdahpStudyDesignerUtil.isEmpty(request.getParameter("manageUsers")) - ? "" - : request.getParameter("manageUsers"); + String manageNotifications = FdahpStudyDesignerUtil.isEmpty(request.getParameter("manageNotifications")) ? "" @@ -220,58 +201,55 @@ public ModelAndView addOrUpdateUserDetails( userBO.setModifiedBy(userSession.getUserId()); userBO.setModifiedOn(FdahpStudyDesignerUtil.getCurrentDateTime()); } - if (!"".equals(manageUsers)) { - if ("0".equals(manageUsers)) { - permissions += count > 1 ? ",ROLE_MANAGE_USERS_VIEW" : "ROLE_MANAGE_USERS_VIEW"; - count++; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_USERS_VIEW); - } else if ("1".equals(manageUsers)) { - permissions += count > 1 ? ",ROLE_MANAGE_USERS_VIEW" : "ROLE_MANAGE_USERS_VIEW"; - count++; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_USERS_VIEW); - permissions += count > 1 ? ",ROLE_MANAGE_USERS_EDIT" : "ROLE_MANAGE_USERS_EDIT"; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_USERS_EDIT); - } - } - if (!"".equals(manageNotifications)) { - if ("0".equals(manageNotifications)) { - permissions += - count > 1 - ? ",ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW" - : "ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW"; - count++; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW); - } else if ("1".equals(manageNotifications)) { - permissions += - count > 1 - ? ",ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW" - : "ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW"; - count++; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW); - permissions += - count > 1 - ? ",ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT" - : "ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT"; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT); - } - } - if (!"".equals(manageStudies)) { - if ("1".equals(manageStudies)) { - permissions += count > 1 ? ",ROLE_MANAGE_STUDIES" : "ROLE_MANAGE_STUDIES"; - count++; - permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_STUDIES); - if (!"".equals(addingNewStudy) && "1".equals(addingNewStudy)) { + + // Superadmin flow + if (userBO.getRoleId().equals(1)) { + permissions = FdahpStudyDesignerConstants.SUPER_ADMIN_PERMISSIONS; + } else { + // Study admin flow + if (!"".equals(manageNotifications)) { + if ("0".equals(manageNotifications)) { permissions += - count > 1 ? ",ROLE_CREATE_MANAGE_STUDIES" : "ROLE_CREATE_MANAGE_STUDIES"; - permissionList.add(FdahpStudyDesignerConstants.ROLE_CREATE_MANAGE_STUDIES); + count > 1 + ? ",ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW" + : "ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW"; + count++; + permissionList.add( + FdahpStudyDesignerConstants.ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW); + } else if ("1".equals(manageNotifications)) { + permissions += + count > 1 + ? ",ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW" + : "ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW"; + count++; + permissionList.add( + FdahpStudyDesignerConstants.ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW); + permissions += + count > 1 + ? ",ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT" + : "ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT"; + permissionList.add( + FdahpStudyDesignerConstants.ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT); + } + } + if (!"".equals(manageStudies)) { + if ("1".equals(manageStudies)) { + permissions += count > 1 ? ",ROLE_MANAGE_STUDIES" : "ROLE_MANAGE_STUDIES"; + count++; + permissionList.add(FdahpStudyDesignerConstants.ROLE_MANAGE_STUDIES); + if (!"".equals(addingNewStudy) && "1".equals(addingNewStudy)) { + permissions += + count > 1 ? ",ROLE_CREATE_MANAGE_STUDIES" : "ROLE_CREATE_MANAGE_STUDIES"; + permissionList.add(FdahpStudyDesignerConstants.ROLE_CREATE_MANAGE_STUDIES); + } + } else { + selectedStudies = ""; + permissionValues = ""; } } else { selectedStudies = ""; permissionValues = ""; } - } else { - selectedStudies = ""; - permissionValues = ""; } AuditLogEventRequest auditRequest = AuditEventMapper.fromHttpServletRequest(request); msg = @@ -279,7 +257,6 @@ public ModelAndView addOrUpdateUserDetails( request, userBO, permissions, - permissionList, selectedStudies, permissionValues, userSession, @@ -361,19 +338,24 @@ public ModelAndView enforcePasswordChange(HttpServletRequest request) { auditLogEventHelper.logEvent(PASSWORD_CHANGE_ENFORCED_FOR_ALL_USERS, auditRequest); emails = usersService.getActiveUserEmailIds(); if ((emails != null) && !emails.isEmpty()) { - boolean allSent = false; + int failedCount = 0; + int successCount = 0; for (String email : emails) { String sent = loginService.sendPasswordResetLinkToMail( request, email, "", "enforcePasswordChange", auditRequest); if (FdahpStudyDesignerConstants.SUCCESS.equals(sent)) { - allSent = true; + successCount++; + } else { + failedCount++; } } - if (allSent) { + if (successCount == emails.size()) { auditLogEventHelper.logEvent( PASSWORD_CHANGE_ENFORCEMENT_FOR_ALL_USERS_EMAIL_SENT, auditRequest); - } else { + } + + if (failedCount > 0) { auditLogEventHelper.logEvent( PASSWORD_CHANGE_ENFORCEMENT_FOR_ALL_USERS_EMAIL_FAILED, auditRequest); } @@ -475,7 +457,6 @@ public ModelAndView resendActivateDetailsLink(HttpServletRequest request) { auditLogEventHelper.logEvent(NEW_USER_INVITATION_RESENT, auditRequest, values); } else { request.getSession().setAttribute(FdahpStudyDesignerConstants.ERR_MSG, msg); - auditLogEventHelper.logEvent(PASSWORD_HELP_EMAIL_FAILED, auditRequest); } } mav = new ModelAndView("redirect:/adminUsersView/getUserList.do"); diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyDAOImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyDAOImpl.java index 85900de350..2df2290dd8 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyDAOImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyDAOImpl.java @@ -1,23 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. * Copyright 2020-2021 Google LLC - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as - * Contract no. HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.dao; @@ -4378,8 +4364,6 @@ public String saveOrUpdateStudySettings(StudyBo studyBo, SessionObject sesObj) { StudySequenceBo studySequence = null; StudyBo study = null; - boolean ownUserForceLogout = false; - StudyBuilderAuditEvent eventEnum = null; Map values = new HashMap<>(); try { AuditLogEventRequest auditRequest = AuditEventMapper.fromHttpServletRequest(request); @@ -4405,9 +4389,6 @@ public String saveOrUpdateStudySettings(StudyBo studyBo, SessionObject sesObj) { // validation of anchor date study.setPlatform(studyBo.getPlatform()); study.setEnrollingParticipants(studyBo.getEnrollingParticipants()); - study.setRetainParticipant(studyBo.getRetainParticipant()); - study.setAllowRejoin(studyBo.getAllowRejoin()); - study.setAllowRejoinText(FdahpStudyDesignerConstants.ALLOW_REJOIN_TEXT); study.setModifiedBy(studyBo.getUserId()); study.setModifiedOn(FdahpStudyDesignerUtil.getCurrentDateTime()); // Phase2a code Start(adding enrollment date as anchor date(yes/no)) @@ -4416,8 +4397,6 @@ public String saveOrUpdateStudySettings(StudyBo studyBo, SessionObject sesObj) { session.saveOrUpdate(study); values.put("enrollment_setting", String.valueOf(study.getEnrollingParticipants())); - values.put("rejoin_setting", String.valueOf(study.getAllowRejoin())); - values.put("dataretention_setting", String.valueOf(study.getRetainParticipant())); // setting true to setting admins // setting and admins section of Study completed or not diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyQuestionnaireDAOImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyQuestionnaireDAOImpl.java index 912e6886c1..ef3dc215b4 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyQuestionnaireDAOImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/StudyQuestionnaireDAOImpl.java @@ -3215,7 +3215,9 @@ public String reOrderQuestionnaireSteps( String searchQuery = "From QuestionnairesStepsBo QSBO where QSBO.questionnairesId=:questionnaireId " + "and QSBO.active=1 order by QSBO.sequenceNo ASC"; - questionnaireStepList = session.createQuery(searchQuery).list(); + Query query = session.createQuery(searchQuery); + query.setInteger("questionnaireId", questionnaireId); + questionnaireStepList = query.list(); if ((null != questionnaireStepList) && !questionnaireStepList.isEmpty()) { if (questionnaireStepList.size() == 1) { questionnaireStepList.get(0).setDestinationStep(0); diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/UsersDAOImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/UsersDAOImpl.java index 21918ba3e9..ea50b60280 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/UsersDAOImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/dao/UsersDAOImpl.java @@ -1,42 +1,27 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. * Copyright 2020-2021 Google LLC - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.dao; import com.fdahpstudydesigner.bean.UserIdAccessLevelInfo; import com.fdahpstudydesigner.bo.RoleBO; +import com.fdahpstudydesigner.bo.StudyBo; import com.fdahpstudydesigner.bo.StudyPermissionBO; import com.fdahpstudydesigner.bo.UserBO; import com.fdahpstudydesigner.bo.UserPermissions; import com.fdahpstudydesigner.util.FdahpStudyDesignerConstants; -import com.fdahpstudydesigner.util.FdahpStudyDesignerUtil; import com.fdahpstudydesigner.util.SessionObject; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.hibernate.Query; @@ -159,7 +144,7 @@ public UserIdAccessLevelInfo addOrUpdateUserDetails( .setParameterList("permissions", permissionList) .list()); userBO2.setPermissionList(permissionSet); - userBO2.setAccessLevel(FdahpStudyDesignerUtil.getUserAccessLevel(permissionSet)); + userBO2.setAccessLevel(userBO2.getRoleId().equals(1) ? "SUPERADMIN" : "STUDY ADMIN"); session.update(userBO2); userIdAccessLevelInfo.setAccessLevel(userBO2.getAccessLevel()); } else { @@ -175,12 +160,14 @@ public UserIdAccessLevelInfo addOrUpdateUserDetails( query.executeUpdate(); } - if (!"".equals(selectedStudies) && !"".equals(permissionValues)) { + // Study admin flow + if (!"".equals(selectedStudies) + && !"".equals(permissionValues) + && userBO2.getRoleId().equals(2)) { selectedStudy = selectedStudies.split(","); permissionValue = permissionValues.split(","); List selectedStudiesList = Arrays.asList(selectedStudies.split(",")); if (updateFlag) { - query = session .createSQLQuery( @@ -189,7 +176,6 @@ public UserIdAccessLevelInfo addOrUpdateUserDetails( .setParameter("userId", userId); query.executeUpdate(); } - for (int i = 0; i < selectedStudy.length; i++) { query = session @@ -210,6 +196,35 @@ public UserIdAccessLevelInfo addOrUpdateUserDetails( session.save(studyPermissionBO); } } + } else if (userBO2.getRoleId().equals(1)) { + // Superadmin flow + query = + session.createQuery( + " FROM StudyBo SBO WHERE SBO.version = 0 AND SBO.status <> :deActivateStatus"); + query.setParameter("deActivateStatus", FdahpStudyDesignerConstants.STUDY_DEACTIVATED); + List studyBOList = query.list(); + if (CollectionUtils.isNotEmpty(studyBOList)) { + for (int i = 0; i < studyBOList.size(); i++) { + query = + session + .createQuery( + " FROM StudyPermissionBO UBO where UBO.studyId=:studyId" + + " AND UBO.userId=:userId") + .setParameter("userId", userId) + .setParameter("studyId", studyBOList.get(i).getId()); + studyPermissionBO = (StudyPermissionBO) query.uniqueResult(); + if (null != studyPermissionBO) { + studyPermissionBO.setViewPermission(true); + session.update(studyPermissionBO); + } else { + studyPermissionBO = new StudyPermissionBO(); + studyPermissionBO.setStudyId(studyBOList.get(i).getId()); + studyPermissionBO.setViewPermission(true); + studyPermissionBO.setUserId(userId); + session.save(studyPermissionBO); + } + } + } } transaction.commit(); msg = FdahpStudyDesignerConstants.SUCCESS; @@ -438,10 +453,7 @@ public List getUserList() { query = session.createSQLQuery( " SELECT u.user_id,u.first_name,u.last_name,u.email,r.role_name,u.status," - + "u.password,u.email_changed,u.access_level FROM users u,roles r WHERE r.role_id = u.role_id and u.user_id " - + "not in (select upm.user_id from user_permission_mapping upm where " - + "upm.permission_id = (select up.permission_id from user_permissions up " - + "where up.permissions ='ROLE_SUPERADMIN')) ORDER BY u.user_id DESC "); + + "u.password,u.email_changed,u.access_level FROM users u,roles r WHERE r.role_id = u.role_id ORDER BY u.user_id DESC "); objList = query.list(); if ((null != objList) && !objList.isEmpty()) { userList = new ArrayList<>(); diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/AuditEventServiceImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/AuditEventServiceImpl.java index a1bb6e44f2..ee69dd5bef 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/AuditEventServiceImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/AuditEventServiceImpl.java @@ -34,22 +34,25 @@ public class AuditEventServiceImpl implements AuditEventService { public void postAuditLogEvent(AuditLogEventRequest auditRequest) { logger.debug( String.format("begin postAuditLogEvent() for %s event", auditRequest.getEventCode())); - - JsonNode requestBody = getObjectMapper().convertValue(auditRequest, JsonNode.class); - Logging logging = LoggingOptions.getDefaultInstance().getService(); - - // The data to write to the log - Map jsonPayloadMap = getObjectMapper().convertValue(auditRequest, Map.class); - - LogEntry entry = - LogEntry.newBuilder(Payload.JsonPayload.of(jsonPayloadMap)) - .setTimestamp(auditRequest.getOccurred().getTime()) - .setSeverity(Severity.INFO) - .setLogName(AUDIT_LOG_NAME) - .setResource(MonitoredResource.newBuilder("global").build()) - .build(); - // Writes the log entry asynchronously - logging.write(Collections.singleton(entry)); + try (Logging logging = LoggingOptions.getDefaultInstance().getService()) { + JsonNode requestBody = getObjectMapper().convertValue(auditRequest, JsonNode.class); + + // The data to write to the log + Map jsonPayloadMap = getObjectMapper().convertValue(auditRequest, Map.class); + + LogEntry entry = + LogEntry.newBuilder(Payload.JsonPayload.of(jsonPayloadMap)) + .setTimestamp(auditRequest.getOccurred().getTime()) + .setSeverity(Severity.INFO) + .setLogName(AUDIT_LOG_NAME) + .setResource(MonitoredResource.newBuilder("global").build()) + .build(); + // Writes the log entry asynchronously + logging.write(Collections.singleton(entry)); + + } catch (Exception e) { + logger.error(String.format("%s failed with an exception", auditRequest.getEventCode()), e); + } logger.debug( String.format("postAuditLogEvent() for %s event finished", auditRequest.getEventCode())); diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/LoginServiceImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/LoginServiceImpl.java index 3a67a5d6d3..9bd38f45e1 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/LoginServiceImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/LoginServiceImpl.java @@ -24,6 +24,7 @@ import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_ACCOUNT_ACTIVATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_ACCOUNT_ACTIVATION_FAILED; +import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_INVITATION_EMAIL_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_SUCCEEDED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_HELP_EMAIL_FAILED; @@ -456,6 +457,12 @@ && new SimpleDateFormat(FdahpStudyDesignerConstants.DB_SDF_DATE_TIME) keyValueForSubject.put("$firstName", userdetails.getFirstName()); keyValueForSubject2.put("$firstName", userdetails.getFirstName()); keyValueForSubject.put("$lastName", userdetails.getLastName()); + keyValueForSubject.put( + "$passwordResetLinkExpirationInDay", + String.valueOf(passwordResetLinkExpirationInDay)); + keyValueForSubject2.put( + "$passwordResetLinkExpirationInDay", + String.valueOf(passwordResetLinkExpirationInDay)); keyValueForSubject.put("$passwordResetLink", acceptLinkMail + passwordResetToken); customerCareMail = propMap.get("email.address.customer.service"); keyValueForSubject.put("$customerCareMail", customerCareMail); @@ -474,6 +481,13 @@ && new SimpleDateFormat(FdahpStudyDesignerConstants.DB_SDF_DATE_TIME) flag = EmailNotification.sendEmailNotification( "userRegistrationSubject", dynamicContent, email, null, null); + + Map values = new HashMap<>(); + values.put(StudyBuilderConstants.USER_ID, String.valueOf(userdetails.getUserId())); + if (!flag) { + auditLogEventHelper.logEvent( + NEW_USER_INVITATION_EMAIL_FAILED, auditRequest, values); + } } else if ("USER_UPDATE".equals(type) && userdetails.isEnabled()) { dynamicContent = FdahpStudyDesignerUtil.genarateEmailContent( @@ -534,9 +548,10 @@ && new SimpleDateFormat(FdahpStudyDesignerConstants.DB_SDF_DATE_TIME) flag ? PASSWORD_HELP_EMAIL_SENT : PASSWORD_HELP_EMAIL_FAILED; auditLogEventHelper.logEvent(auditLogEvent, auditRequest); } - if (flag) { - message = FdahpStudyDesignerConstants.SUCCESS; - } + + message = + flag ? FdahpStudyDesignerConstants.SUCCESS : FdahpStudyDesignerConstants.FAILURE; + if ("".equals(type) && (!userdetails.isEnabled())) { message = propMap.get("user.inactive.msg"); } @@ -607,9 +622,14 @@ public void sendLockedAccountPasswordResetLinkToMail( FdahpStudyDesignerUtil.genarateEmailContent( "accountLockedContent", keyValueForSubject); - EmailNotification.sendEmailNotification( - "accountLockedSubject", dynamicContent, email, null, null); - auditLogEventHelper.logEvent(PASSWORD_RESET_EMAIL_SENT_FOR_LOCKED_ACCOUNT, auditRequest); + boolean response = + EmailNotification.sendEmailNotification( + "accountLockedSubject", dynamicContent, email, null, null); + StudyBuilderAuditEvent auditEvent = + response + ? PASSWORD_RESET_EMAIL_SENT_FOR_LOCKED_ACCOUNT + : PASSWORD_RESET_EMAIL_FAILED_FOR_LOCKED_ACCOUNT; + auditLogEventHelper.logEvent(auditEvent, auditRequest); } } } catch (Exception e) { diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyActiveTasksServiceImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyActiveTasksServiceImpl.java index 68d492c689..ab62ea3dde 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyActiveTasksServiceImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyActiveTasksServiceImpl.java @@ -1,5 +1,6 @@ /* * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies @@ -322,9 +323,7 @@ public ActiveTaskBo saveOrUpdateActiveTask(ActiveTaskBo activeTaskBo, String cus if (activeTaskBo.getScheduleType() != null) { addActiveTaskeBo.setScheduleType(activeTaskBo.getScheduleType()); } - if (activeTaskBo.getAnchorDateId() != null) { - addActiveTaskeBo.setAnchorDateId(activeTaskBo.getAnchorDateId()); - } + addActiveTaskeBo.setAnchorDateId(activeTaskBo.getAnchorDateId()); if ((activeTaskBo.getFrequency() != null) && !activeTaskBo .getFrequency() diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyServiceImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyServiceImpl.java index 2b7af550de..888e831c37 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyServiceImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/StudyServiceImpl.java @@ -801,7 +801,6 @@ public ConsentBo saveOrCompleteConsentReviewDetails( updateConsentBo.setComprehensionTestMinimumScore( consentBo.getComprehensionTestMinimumScore()); } - updateConsentBo.setAggrementOfTheConsent(FdahpStudyDesignerConstants.CONSENT_AGREEMENT); updateConsentBo = studyDAO.saveOrCompleteConsentReviewDetails(updateConsentBo, sesObj, customStudyId); } catch (Exception e) { @@ -1396,6 +1395,7 @@ public StudyDetailsBean getStudyByLatestVersion(String customStudyId) { if (studyBo != null) { eligibilityType = studyDAO.getEligibilityType(studyBo.getId()); studyDetails = new StudyDetailsBean(); + studyDetails.setContactEmail(studyBo.getInboxEmailAddress()); studyDetails.setStudyId(studyBo.getCustomStudyId()); studyDetails.setStudyTitle(studyBo.getName()); studyDetails.setStudyVersion(studyBo.getVersion() + ""); diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersService.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersService.java index ac104cf627..57c92d231d 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersService.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersService.java @@ -1,24 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: + * Copyright 2020-2021 Google LLC * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.service; @@ -43,7 +28,6 @@ public String addOrUpdateUserDetails( HttpServletRequest request, UserBO userBO, String permissions, - List permissionList, String selectedStudies, String permissionValues, SessionObject userSession, diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersServiceImpl.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersServiceImpl.java index f587870cbb..7e85519713 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersServiceImpl.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/service/UsersServiceImpl.java @@ -1,32 +1,15 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. * Copyright 2020-2021 Google LLC - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.fdahpstudydesigner.service; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_CREATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_CREATION_FAILED; -import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_INVITATION_EMAIL_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_INVITATION_EMAIL_SENT; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_ACCOUNT_RE_ACTIVATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_RECORD_DEACTIVATED; @@ -163,7 +146,6 @@ public String addOrUpdateUserDetails( HttpServletRequest request, UserBO userBO, String permissions, - List permissionList, String selectedStudies, String permissionValues, SessionObject userSession, @@ -172,17 +154,10 @@ public String addOrUpdateUserDetails( UserBO userBO2 = null; String msg = FdahpStudyDesignerConstants.FAILURE; boolean addFlag = false; - String activity = ""; - String activityDetail = ""; List auditLogEvents = new LinkedList<>(); Map values = new HashMap<>(); boolean emailIdChange = false; - List superAdminEmailList = null; - Map keyValueForSubject = null; - String dynamicContent = ""; - UserBO adminFullNameIfSizeOne = null; UserBO userBO3 = null; - Map propMap = FdahpStudyDesignerUtil.getAppProperties(); try { if (null == userBO.getUserId()) { @@ -236,8 +211,6 @@ public String addOrUpdateUserDetails( auditLogEvents.add(NEW_USER_CREATED); if (FdahpStudyDesignerConstants.SUCCESS.equals(msg)) { auditLogEvents.add(NEW_USER_INVITATION_EMAIL_SENT); - } else { - auditLogEvents.add(NEW_USER_INVITATION_EMAIL_FAILED); } } if (!addFlag) { @@ -264,49 +237,7 @@ public String addOrUpdateUserDetails( } } auditLogHelper.logEvent(auditLogEvents, auditRequest, values); - superAdminEmailList = usersDAO.getSuperAdminList(); - if (msg.equals(FdahpStudyDesignerConstants.SUCCESS) - && (superAdminEmailList != null) - && !superAdminEmailList.isEmpty()) { - keyValueForSubject = new HashMap(); - if (superAdminEmailList.size() == 1) { - for (String email : superAdminEmailList) { - adminFullNameIfSizeOne = usersDAO.getSuperAdminNameByEmailId(email); - keyValueForSubject.put("$admin", adminFullNameIfSizeOne.getFirstName()); - } - } else { - keyValueForSubject.put("$admin", "Admin"); - } - keyValueForSubject.put("$userEmail", userBO.getUserEmail()); - keyValueForSubject.put( - "$sessionAdminFullName", - userSession.getFirstName() + " " + userSession.getLastName()); - keyValueForSubject.put("$orgName", propMap.get("orgName")); - if (addFlag) { - dynamicContent = - FdahpStudyDesignerUtil.genarateEmailContent( - "mailForAdminUserCreateContent", keyValueForSubject); - EmailNotification.sendEmailNotification( - "mailForAdminUserCreateSubject", dynamicContent, null, superAdminEmailList, null); - } else { - String status = ""; - if (FdahpStudyDesignerUtil.isEmpty(userBO2.getUserPassword())) { - status = "Pending Activation"; - } else { - if (userBO2.isEnabled()) { - status = "Active"; - } else { - status = "Deactivated"; - } - } - keyValueForSubject.put("$userStatus", status); - dynamicContent = - FdahpStudyDesignerUtil.genarateEmailContent( - "mailForAdminUserUpdateContent", keyValueForSubject); - EmailNotification.sendEmailNotification( - "mailForAdminUserUpdateSubject", dynamicContent, null, superAdminEmailList, null); - } - } + } else { auditLogHelper.logEvent(NEW_USER_CREATION_FAILED, auditRequest); } diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/EmailNotification.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/EmailNotification.java index 64f8d52064..7b242da8b6 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/EmailNotification.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/EmailNotification.java @@ -59,8 +59,7 @@ public static boolean sendEmailNotification( mail.setBccEmail(StringUtils.join(bccMailList, ',')); mail.setSubject(propMap.get(subjectProprtyName)); mail.setMessageBody(content); - mail.sendemail(); - sentMail = true; + sentMail = mail.sendemail(); } catch (Exception e) { logger.error("EmailNotification.sendEmailNotification() :: ERROR ", e); } diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerConstants.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerConstants.java index 5b2607dc83..49dba40920 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerConstants.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerConstants.java @@ -1,27 +1,10 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. * Copyright 2020-2021 Google LLC - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ - package com.fdahpstudydesigner.util; public class FdahpStudyDesignerConstants { @@ -446,13 +429,14 @@ public class FdahpStudyDesignerConstants { public static final String FDA_SMD_STUDY_THUMBNAIL_PATH = "fda.smd.study.thumbnailPath"; public static final String FDA_SMD_STUDY_PAGE_PATH = "fda.smd.study.pagePath"; - public static final String ALLOW_REJOIN_TEXT = "Are you sure you want to leave the study?"; public static final String STUDY_BASICINFORMATION_DEFAULT_IMAGE = "study.basicInformation.defaultImage"; - public static final String CONSENT_AGREEMENT = - "By tapping on Agree, you confirm that you have reviewed the consent document and agree to participate in the study."; + public static final String ADD = "add"; + + public static final String SUPER_ADMIN_PERMISSIONS = + "ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW,ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT,ROLE_MANAGE_STUDIES,ROLE_CREATE_MANAGE_STUDIES,ROLE_SUPERADMIN,ROLE_MANAGE_USERS_EDIT,ROLE_MANAGE_USERS_VIEW"; private FdahpStudyDesignerConstants() { // Do nothing diff --git a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerUtil.java b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerUtil.java index 1970ca3c6b..0f942fd682 100644 --- a/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerUtil.java +++ b/study-builder/fdahpStudyDesigner/src/main/java/com/fdahpstudydesigner/util/FdahpStudyDesignerUtil.java @@ -74,6 +74,7 @@ public class FdahpStudyDesignerUtil { + private static Map appProperties = null; /* Read Properties file */ private static Logger logger = Logger.getLogger(FdahpStudyDesignerUtil.class.getName()); @@ -254,8 +255,12 @@ public static String genarateEmailContent(String emailContentName, Map getAppProperties() { - HashMap hm = new HashMap(); logger.info("FdahpStudyDesignerUtil - getAppProperties() :: Properties Initialization"); + if (appProperties != null && !appProperties.isEmpty()) { + return appProperties; + } + appProperties = new HashMap<>(); + Enumeration keys = null; Enumeration objectKeys = null; Resource resource = null; @@ -265,7 +270,7 @@ public static Map getAppProperties() { while (keys.hasMoreElements()) { String key = keys.nextElement(); String value = rb.getString(key); - hm.put(key, value); + appProperties.put(key, value); } ServletContext context = ServletContextHolder.getServletContext(); Properties prop = @@ -274,14 +279,14 @@ public static Map getAppProperties() { while (objectKeys.hasMoreElements()) { String key = (String) objectKeys.nextElement(); String value = prop.getProperty(key); - hm.put(key, value); + appProperties.put(key, value); } } catch (Exception e) { logger.error("FdahpStudyDesignerUtil - getAppProperties() - ERROR ", e); } logger.info("FdahpStudyDesignerUtil - getAppProperties() - ends"); - return hm; + return appProperties; } public static FormulaInfoBean getConditionalFormulaResult( diff --git a/study-builder/fdahpStudyDesigner/src/main/resources/messageResource.properties b/study-builder/fdahpStudyDesigner/src/main/resources/messageResource.properties index fff3258490..12236089a2 100644 --- a/study-builder/fdahpStudyDesigner/src/main/resources/messageResource.properties +++ b/study-builder/fdahpStudyDesigner/src/main/resources/messageResource.properties @@ -24,14 +24,14 @@ old.password.error.msg=New Password should not be the same as the last $countPas invalid.oldpassword.msg=Old password is not valid. user.forgot.success.msg=Password help has been sent to your registered email user.forgot.error.msg=This email is not registered. -user.newaccount.success.msg=Your account has been created. user.newpassword.success.msg=Your password has been changed. +user.account.setup.msg=Your account has been set up. Please sign in to continue password.max.char.match.error.msg=There should be at least $countMatch unique characters that are different from your previous password. password.name.contains.error.msg=Password should not contain first name or last name. resent.link.success.message=A new account activation link has been sent to the user's email. resent.link.failure.message=Sorry, an error occurred and a new account activation link could not be emailed to this user. -password.enforce.link.success.message=Password change enforcement email successfully sent to user(s). +password.enforce.link.success.message=Password change enforcement initiated for user(s). password.enforce.failure.message=Password change enforcement operation failed. Please try again. # Hibernate Connection Message @@ -43,40 +43,37 @@ close.hibernate.session=Hibernate Session Closed # Email content passwordResetLinkSubject=Password help for your account -passwordResetLinkContent= Hi $firstName,

Thanks for reaching out for password help for your account on $orgName's Study Builder portal.

Please use the link given below, to set up a new password.

Link: $passwordResetLink

Please note that the link can be used only once and is valid for a period of 48 hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note : This is an auto-generated email. Please do not reply. In case you did not request for password help, please visit the portal and change your password as a precautionary measure. +passwordResetLinkContent= Hi $firstName

Thanks for reaching out for password help.

Please use the link given below to set up a new password to sign in to your $orgName's Study Builder portal.

$passwordResetLink

Please note that the link can be used only once and is valid for a period of $passwordResetLinkExpirationInDay hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note : This is an auto-generated email. Please do not reply. In case you did not request for password help, please visit the portal and change your password as a precautionary measure. accountLockedSubject=Your account has been locked -accountLockedContent= Hi $firstName,

This is to inform you that, as a security measure, your user account for $orgName's Study Builder portal has been temporarily locked for a period of 1 hour, due to multiple consecutive failed sign-in attempts with incorrect password.

Please try signing in again after this period.

Alternatively, you could reset your password using the link provided below:

Link: $passwordResetLink

Please note that the link can be used only once and is valid for a period of 1 hour only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +accountLockedContent= Hi $firstName

This is to inform you that, as a security measure, your user account for $orgName's Study Builder portal has been temporarily locked for a period of 1 hour, due to consecutive failed sign-in attempts.

Please try signing in again after this period.

Alternatively, you could reset your password using the link provided below:

$passwordResetLink

Please note that the link can be used only once and is valid for a period of 1 hour only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. userRegistrationSubject=Your account has been created -userRegistrationContent= Hi $firstName,

Welcome to the $orgName's Study Builder portal!

Please use the activation link given below to set up and start using your account.

Link: $passwordResetLink

Please note that the link can be used only once and is valid for a period of 48 hours only.

For any questions or assistance, please write to $customerCareMail

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +userRegistrationContent= Hi $firstName

Welcome to $orgName's Study Builder portal!

Please use the link given below to set up and start using your account.

$passwordResetLink

Please note that the link can be used only once and is valid for a period of $passwordResetLinkExpirationInDay hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailForUserUpdateSubject=Your account has been updated -mailForUserUpdateContent= Hi $firstName,

This is to inform you that one or more details of your user account have been updated in $orgName's Study Builder portal.

You may be required to sign in to the portal again in case you were in an active session.

For any questions or assistance, please write to $customerCareMail

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailForUserUpdateContent= Hi $firstName

This is to inform you that one or more details of your user account have been updated in $orgName's Study Builder portal.

You may be required to sign in to the portal again in case you were in an active session.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailForAdminUserUpdateSubject=A user's account has been updated -mailForAdminUserUpdateContent= Hi $admin,

This is to inform you that one or more details of a user's account has been updated by an administrator user with username $sessionAdminFullName in the $orgName's Study Builder portal.

The user's current registered email is $userEmail and the account status is $userStatus.

The user may be required to sign in to the portal again in case he/she was currently in an active session.

Note: You are receiving this email because you have a 'superadmin' role on the portal. In case you see this update as unusual or unintended activity, please sign in to the app and exercise the 'Enforce Password Change' option for this user.



Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. - -mailForAdminUserCreateSubject=New user account created -mailForAdminUserCreateContent= Hi $admin,

This is to inform you that a new user record with email $userEmail has been created by an administrator user with username $sessionAdminFullName in $orgName's Study Builder portal.

The user account will get activated once the user completes the account setup process via the activation link sent to him/her in email. The link is valid for 48 hours.

Note: You are receiving this email because you have a 'superadmin' role on the portal.



Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailForAdminUserUpdateContent= Hi $admin

This is to inform you that one or more details of a user's account has been updated in $orgName's Study Builder portal.

The user's current registered email is $userEmail and the account status is $userStatus.

The user may be required to sign in to the portal again in case he/she was currently in an active session.

Note: You are receiving this email because you have a 'superadmin' role on the portal. In case you see this update as unusual or unintended activity, please sign in to the portal and exercise the 'Enforce Password Change' option for this user.



Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailToOldEmailForUserEmailUpdateSubject=Your account has been updated -mailToOldEmailForUserEmailUpdateContent= Hi $firstName,

This is to inform you that one or more details of your user account including your registered email have been updated by the administrator in the $orgName's Study Builder portal.

You may be signed out of the portal in case you were in an active session.

A verification email has been sent to your new email address $newUpdatedMail. Please follow the instructions provided therein to verify your new email and continue using the portal.

Note that the previously registered email associated with your account can no longer be used to sign in to your account on the portal.

As an added security measure, we advise you to change your password for the portal, after signing in to it with the new email.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailToOldEmailForUserEmailUpdateContent= Hi $firstName

This is to inform you that one or more details of your user account including your registered email have been updated in $orgName's Study Builder portal.

You may be signed out of the portal in case you were in an active session.

A verification email has been sent to your new email address $newUpdatedMail. Please follow the instructions provided therein to verify your new email and continue using the portal.

Note that the previously registered email associated with your account can no longer be used to sign in to your account on the portal.

As an added security measure, we advise you to change your password for the portal, after signing in to it with the new email.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mail.audit.failure.subject=Failure to generate audit log file mail.audit.failure.content= Hi $firstName

This is to inform you that audit log file could not be generated for the period $startTime to $endTime for the $orgName's Study Builder portal. This could be due to technical or other issues.



The system will re-attempt to generate the audit log file in the next scheduled cycle for log file generation.



Note: You are receiving this email because you have a 'superadmin' role on the portal. We advise you to contact the technical support team for more information on this issue.



Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailForReactivatingUserSubject=Your account has been re-activated -mailForReactivatingUserContent= Hi $userFirstName,

This is to inform you that your user account has been re-activated in the $orgName's Study Builder portal.

You can now sign in and continue using the portal.

For any questions or assistance, please write to $customerCareMail

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailForReactivatingUserContent= Hi $userFirstName

This is to inform you that your user account has been re-activated in $orgName's Study Builder portal.

You can now sign in and continue using the portal.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailForReactivatingUserAfterEnforcePassChangeSubject=Your account has been re-activated -mailForReactivatingUserAfterEnforcePassChangeContent= Hi $firstName,

This is to inform you that your user account has been re-activated by the administrator in $orgName's Study Builder portal.

As an added security measure, you would need to set up your account password again. Please follow the link below to set up your new password

Link: $passwordResetLink

Once this is done, you can sign in and continue to use the portal.

Please note that the link can be used only once and is valid for a period of 48 hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailForReactivatingUserAfterEnforcePassChangeContent= Hi $firstName

This is to inform you that your user account has been re-activated in $orgName's Study Builder portal.

As an added security measure, you would need to set up your account password again. Please follow the link below to set up your new password

$passwordResetLink

Once this is done, you can sign in and continue to use the portal.

Please note that the link can be used only once and is valid for a period of $passwordResetLinkExpirationInDay hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailForEnforcePasswordChangeSubject=Your account needs a new password -mailForEnforcePasswordChangeContent= Hi $firstName,

This is to inform you that you need to set up a new password for your user account on $orgName's Study Builder portal, to continue using it. This action is being requested as an added security measure for your account on the portal.

Please follow the link below to set up your new password.

Link: $passwordResetLink

Once this is done, you can sign in and continue to use the portal.

Please note that the link can be used only once and is valid for a period of 48 hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailForEnforcePasswordChangeContent= Hi $firstName

This is to inform you that you need to set up a new password for your user account on $orgName's Study Builder portal, to continue using it. This action is being requested as an added security measure for your account on the portal.

Please follow the link below to set up your new password.

$passwordResetLink

Once this is done, you can sign in and continue to use the portal.

Please note that the link can be used only once and is valid for a period of $passwordResetLinkExpirationInDay hours only.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. mailToNewEmailForUserEmailUpdateSubject=Verify your email -mailToNewEmailForUserEmailUpdateContent= Hi $firstName,

This is to inform you that one or more details of your user account including your registered email have been updated by the administrator in $orgName's Study Builder portal. You may be signed out of the portal in case you were in an active session.

Please use the link given below to verify your new email and continue using the portal.

Link: $passwordResetLink

Please note that the link can be used only once and is valid for a period of 48 hours only.

Going forward, please use the new email address $newUpdatedMail to sign in to the portal. Note that the previously registered email associated with your account i.e. $oldMail can no longer be used to sign in to your account on the portal.

As an added security measure, we advise you to change your password for the portal, after signing in to it with the new email.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. +mailToNewEmailForUserEmailUpdateContent= Hi $firstName

This is to inform you that one or more details of your user account including your registered email have been updated in $orgName's Study Builder portal. You may be signed out of the portal in case you were in an active session.

Please use the link given below to verify your new email and continue using the portal.

$passwordResetLink

Please note that the link can be used only once and is valid for a period of $passwordResetLinkExpirationInDay hours only.

Going forward, please use the new email address $newUpdatedMail to sign in to the portal. Note that the previously registered email associated with your account i.e. $oldMail can no longer be used to sign in to your account on the portal.

As an added security measure, we advise you to change your password for the portal, after signing in to it with the new email.

For any questions or assistance, please write to $customerCareMail.

Thanks,
The $orgName MyStudies support team
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: This is an auto-generated email. Please do not reply. # Study message save.study.success.message=Content saved as draft diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/403.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/403.jsp index aa6a6a8749..88118f1749 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/403.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/403.jsp @@ -50,7 +50,7 @@ \ No newline at end of file diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionStep.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionStep.jsp index f371e10318..6ece4fc7d9 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionStep.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionStep.jsp @@ -23,7 +23,7 @@ margin-top: 10px; } - .display__flex__center{ + .questionary_step_edit{ margin-top: 10px !important; } @@ -38,6 +38,9 @@ pointer-events:none; } + .bootstrap-select.btn-group .dropdown-toggle .filter-option { + text-transform: inherit; !important + } \ No newline at end of file diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionaryListPage.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionaryListPage.jsp index dee2c138d0..51e6649661 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionaryListPage.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionaryListPage.jsp @@ -74,8 +74,7 @@ ${questionnaryInfo.createdDate} -

${questionnaryInfo.title}
+
${questionnaryInfo.title}
${questionnaryInfo.frequency == 'Manually Schedule' ? 'Custom Schedule' :questionnaryInfo.frequency} @@ -240,11 +239,11 @@ } $('#alertMsg').show(); } - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); }, error: function (xhr, status, error) { $("#alertMsg").removeClass('s-box').addClass('e-box').text(error); - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); } }); } diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionnaire.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionnaire.jsp index 9a1e9250ac..421cc600a1 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionnaire.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/questionnaire.jsp @@ -267,14 +267,12 @@ -
${subentry.value.title}
+
${subentry.value.title}
-
${entry.value.title}
+
${entry.value.title}
@@ -308,14 +306,16 @@
- cursor-none-without-event " + data-toggle="tooltip" data-placement="top" title="Edit" onclick="editStep(${entry.value.stepId},'${entry.value.stepType}')"> cursor-none-without-event " + data-toggle="tooltip" data-placement="top" title="Delete" onclick="deletStep(${entry.value.stepId},'${entry.value.stepType}')">
@@ -1367,18 +1367,9 @@
-
- -
- - - -
- Select a date range - * +
+ Select a date range and a start and end time for each run of the activity + *
+
+ +
+ +
+ id="RegDate${customVar.index}"> @@ -1479,9 +1478,9 @@ - + + + - - -
- Select a date range - * - - -
- + @@ -1605,7 +1591,7 @@ items="${questionnaireBo.questionnaireCustomScheduleBo}" var="questionnaireCustomScheduleBo" varStatus="customVar">
+ id="AnchorDate${customVar.index}"> @@ -1816,6 +1802,7 @@ var customCount = 0; var frequencey = "${questionnaireBo.frequency}"; customCount = '${customCount}'; + customCount = '${customCount}'==='' ? '0' : '${customCount}'; count = Number('${count}'); var isValidManuallySchedule = true; var multiTimeVal = true; @@ -1857,6 +1844,17 @@ $(".monthlyRegular").show(); } }) + + if ($('.dailyTimeDiv').length == 1) { + $('.dailyTimeDiv').find(".delete").css("visibility", "hidden"); + } + + if($('.manually-option').length == 1){ + $('.manually-option').find(".delete").css("visibility", "hidden"); + } + if($('.manually-anchor-option').length == 1){ + $('.manually-anchor-option').find(".delete").css("visibility", "hidden"); + } $(".typeofschedule").change(function () { @@ -1928,8 +1926,26 @@ $(".manuallyContainer").hide(); $(".manuallyContainer").find('input:text').removeAttr('required'); $(".Selectedtooltip").hide(); - } else { + var j = 0; + for (j = customCount; j > 0; j--) { + var xdays = $("#xdays" + j).val(); + + if(xdays == '') { + document.getElementById('manualTime0').value = ''; + + $("#AnchorDate" + j ).hide(); + $("#AnchorDate" + j ).find('input:text').removeAttr('required', true); + + $("#AddButton").show(); + $("#AddButton").attr('required', true); + } else { + $("#AddButton").hide(); + $("#AddButton").attr('required', false); + } + } + } else { + localStorage.setItem("IsAnchorDateSelected", "false"); localStorage.setItem("IsRegularSelected", "true"); @@ -1974,7 +1990,25 @@ $("#anchorDateId").val(""); $(".Selectedtooltip").show(); - + var i = 0; + for (i = customCount-1; i > 0; i--) { + var RegStartDate = $("#StartDate" + i).val(); + + if(RegStartDate == '') { + document.getElementById('customTime0').value = ''; + $("#customTime0").attr("disabled", true); + + $("#RegDate" + i ).hide(); + $("#RegDate" + i ).find('input:text').removeAttr('required', true); + + $("#AddButton").show(); + $("#AddButton").attr('required', true); + } else { + $("#AddButton").hide(); + $("#AddButton").attr('required', false); + } + } + var startDate = $("#startDate").val(); if(startDate!=''){ @@ -2162,11 +2196,11 @@ "Unable to reorder questionnaire"); } - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); }, error: function (xhr, status, error) { $("#alertMsg").removeClass('s-box').addClass('e-box').text(error); - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); } }); @@ -2977,6 +3011,7 @@ } function addTime() { + $('.dailyTimeDiv').find(".delete ").css("visibility", "visible"); count = count + 1; var newTime = "
" + " " + @@ -3013,14 +3048,20 @@ $(".time-opts").parents("form").validator(); if ($('.time-opts').length > 1) { $(".remBtnDis").removeClass("hide"); + } else if ($('.dailyTimeDiv').length == 1 ) { + $('.dailyTimeDiv').find(".delete").css("visibility", "hidden"); } else { $(".remBtnDis").addClass("hide"); } } function addDate() { - customCount = customCount + 1; - var newDateCon = "
" + $('.manually-option').find(".delete").css("visibility", "visible"); + customCount = parseInt(customCount) + 1; + + $("#AddButton").hide(); + $("#AddButton").attr('required', false); + var newDateCon = "
" + " " + " 1) { $('.manuallyContainer').find(".remBtnDis").removeClass("hide"); + } else if ( $('.manually-option').length == 1 ) { + $('.manually-option').find(".delete").css("visibility", "hidden"); } else { $('.manuallyContainer').find(".remBtnDis").addClass("hide"); } @@ -3298,7 +3341,7 @@ $('.manually-anchor-option').each(function () { var questionnaireCustomFrequencey = new Object(); questionnaireCustomFrequencey.questionnairesId = id; - var id = $(this).attr("id"); + var id = $(this).attr("id").replace('AnchorDate',''); var xSign = $('#xSign' + id).val(); var xSignVal = $('#xdays' + id).val(); var ySign = $('#ySign' + id).val(); @@ -3339,7 +3382,8 @@ $('.manually-option').each(function () { var questionnaireCustomFrequencey = new Object(); questionnaireCustomFrequencey.questionnairesId = id; - var id = $(this).attr("id"); + var id = $(this).attr("id").replace('RegDate',''); + var startdate = $("#StartDate" + id).val(); var enddate = $("#EndDate" + id).val(); var time = $("#customTime" + id).val(); @@ -3392,7 +3436,7 @@ var count = 0; $('.time-opts').each(function () { var questionnaireFrequencey = new Object(); - var id = $(this).attr("id"); + var id = $(this).attr("id").replace('AnchorDate',''); var frequence_time = $('#time' + id).val(); @@ -3890,11 +3934,11 @@ } $('#alertMsg').show(); } - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); }, error: function (xhr, status, error) { $("#alertMsg").removeClass('s-box').addClass('e-box').text(error); - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); } }); } else { @@ -3962,16 +4006,16 @@ } dynamicAction += '' + '
' + - ' '; if (value.status) { - dynamicAction += ''; } else { - dynamicAction += ''; } - dynamicAction += ' ' + '
' + '
'; @@ -4003,6 +4047,7 @@ $('#helpNote').attr('data-original-title', 'Please ensure you add one or more Steps to this questionnaire before attempting to mark this section as Complete.'); } + $('[data-toggle="tooltip"]').tooltip(); } function ellipseHover(item) { @@ -4307,8 +4352,9 @@ } function addDateAnchor() { + $('.manually-anchor-option').find(".delete").css("visibility", "visible"); customAnchorCount = parseInt($('.manually-anchor-option').length); - var newDateCon = "
" + " Anchor date " + " - - +
@@ -74,7 +71,7 @@ name="platform" value="I" checked disabled + test="${not empty studyBo.liveStudyBo && fn:contains(studyBo.liveStudyBo.platform,'I') || studyBo.status eq 'Active'}">disabled data-error="Please check these box if you want to proceed." > @@ -83,7 +80,7 @@ name="platform" value="A" checked disabled + test="${not empty studyBo.liveStudyBo && fn:contains(studyBo.liveStudyBo.platform,'A') || studyBo.status eq 'Active'}">disabled data-error="Please check these box if you want to proceed." > @@ -244,10 +241,7 @@ }); $(".menuNav li.active").removeClass('active'); $(".menuNav li.second").addClass('active'); - checkRadioRequired(); - $(".rejoin_radio").click(function () { - checkRadioRequired(); - }) + $('#settingfoFormId input,textarea,select').prop('disabled', true); $('#settingfoFormId').find('.elaborateClass').addClass('linkDis'); @@ -256,6 +250,10 @@ $('.radcls').prop('disabled', true); $("#completedId").on('click', function (e) { + if ($('.checkbox input:checked').length == 0) { + $("input").attr("required", true); + } + var rowCount = 0; if (isFromValid("#settingfoFormId")) { rowCount = $('.leadCls').length; @@ -278,49 +276,14 @@ $("#saveId").click(function () { platformTypeValidation('save'); }); - var allowRejoin = '${studyBo.allowRejoin}'; - if (allowRejoin != "") { - if (allowRejoin == 'Yes') { - $('.rejointextclassYes').show(); - $('#rejoin_comment_no').empty(); - $('.rejointextclassNo').hide(); - } else { - $('.rejointextclassNo').show(); - $('.rejointextclassYes').hide(); - $('#rejoin_comment_yes').empty(); - } - } + $("[data-toggle=tooltip]").tooltip(); $("#infoIconId").hover(function () { $('#myModal').modal('show'); }); }); - function checkRadioRequired() { - var rejoinRadioVal = $('input[name=allowRejoin]:checked').val(); - if (rejoinRadioVal == 'Yes') { - $('.rejointextclassYes').show(); - $('#rejoin_comment_yes').attr("required", "required"); - $('#rejoin_comment_no').removeAttr("required"); - $('.rejointextclassNo').hide(); - } else { - $('.rejointextclassNo').show(); - $('#rejoin_comment_no').attr("required", "required"); - $('#rejoin_comment_yes').removeAttr("required"); - $('.rejointextclassYes').hide(); - } - } - function setAllowRejoinText() { - var allowRejoin = $('input[name=allowRejoin]:checked').val(); - if (allowRejoin) { - if (allowRejoin == 'Yes') { - $('#rejoin_comment_yes').attr("name", "allowRejoinText"); - $('#rejoin_comment_no').removeAttr("name", "allowRejoinText"); - } else { - $('#rejoin_comment_no').attr("name", "allowRejoinText"); - $('#rejoin_comment_yes').removeAttr("name", "allowRejoinText"); - } - } - } + + function platformTypeValidation(buttonText) { var platformNames = ''; $("input:checkbox[name=platform]:checked").each(function () { @@ -353,11 +316,7 @@ $('#completedId').removeAttr('disabled'); bootbox.alert(errorMessage); } else { - if ($('.checkbox input:checked').length == 0) { - $("input").attr("required", true); - } else { submitButton(buttonText); - } } }, error: function status(data, status) { @@ -369,16 +328,10 @@ global: false }); } else { - if ($('.checkbox input:checked').length == 0) { - $("input").attr("required", true); - } else { submitButton(buttonText); - } } } function submitButton(buttonText) { - setAllowRejoinText(); - admins() //Pradyumn var isAnchorForEnrollmentDraft = '${isAnchorForEnrollmentDraft}'; if (buttonText === 'save') { $('#settingfoFormId').validator('destroy'); @@ -386,41 +339,10 @@ $("#buttonText").val('save'); $("#settingfoFormId").submit(); } else { - var retainParticipant = $('input[name=retainParticipant]:checked').val(); - var enrollmentdateAsAnchordate = $('input[name=enrollmentdateAsAnchordate]:checked').val(); - if (retainParticipant) { - if (retainParticipant == 'All') - retainParticipant = 'Participant Choice'; - bootbox.confirm({ - closeButton: false, - message: 'You have selected "' + retainParticipant - + '" for the retention of participant response data when they leave a study.' - + ' Your Consent content must be worded to convey the same.' - + ' Click OK to proceed with completing this section or Cancel if you wish to make changes.', - buttons: { - 'cancel': { - label: 'Cancel', - }, - 'confirm': { - label: 'OK', - }, - }, - callback: function (result) { - if (result) { - //phase2a anchor - showWarningForAnchor(isAnchorForEnrollmentDraft, enrollmentdateAsAnchordate); - //phase 2a anchor - } else { - $('#completedId').removeAttr('disabled'); - } - } - }); - } else { $("#inlineCheckbox1,#inlineCheckbox2").prop('disabled', false); $("#buttonText").val('completed'); $("#settingfoFormId").submit(); } - } } function admins() { var userIds = ""; diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewSpatialStudyActiveTask.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewSpatialStudyActiveTask.jsp index f43172373a..f1fa333e17 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewSpatialStudyActiveTask.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewSpatialStudyActiveTask.jsp @@ -1,7 +1,16 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> +p<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +
+ title="A human-readable identifier that must be unique across all activities of the study. Allowed characters are lowercase letters (a-z), digits (0-9), _ (underscore) and - (minus).">
@@ -347,7 +356,7 @@ id="${taskMasterAttributeBo.attributeName}_stat_id" class="statisticsBlock" name="taskAttributeValueBos[7].useForStatistic" value="option1"> + for statistic
+ for statistic
+ for statistic
  • ").attr("class","list-unstyled").text( - "The characters like (< >) are not allowed.")); + "Please use allowed characters only: lowercase letters (a-z), digits (0-9), _ (underscore) and - (minus).")); } } }); diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewTowerStudyActiveTask.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewTowerStudyActiveTask.jsp index 895d1bf390..cac9db9b0f 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewTowerStudyActiveTask.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/study/viewTowerStudyActiveTask.jsp @@ -3,6 +3,15 @@ <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +
    + title="A human-readable identifier that must be unique across all activities of the study. Allowed characters are lowercase letters (a-z), digits (0-9), _ (underscore) and - (minus).">
    @@ -213,7 +222,7 @@ id="${taskMasterAttributeBo.attributeName}_stat_id" name="taskAttributeValueBos[1].useForStatistic" value="option1"> + for statistic
    checked value="${taskValueAttributeBo.useForStatistic}"> + for statistic
  • ").attr("class","list-unstyled").text( - "The characters like (< >) are not allowed.")); + "Please use allowed characters only: lowercase letters (a-z), digits (0-9), _ (underscore) and - (minus).")); } } }); diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/userPasswordReset.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/userPasswordReset.jsp index 6110f45756..53af07caef 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/userPasswordReset.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/userPasswordReset.jsp @@ -78,7 +78,7 @@
    + autocomplete="off" style="top: 20% !important;">
    ${errMsg}
    ${sucMsg}
    @@ -100,7 +100,7 @@
    @@ -233,7 +233,7 @@ if (errMsg.length > 0) { $("#errMsg").text(errMsg); $("#errMsg").show("fast"); - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); } } var sucMsg = '${sucMsg}'; @@ -242,7 +242,7 @@ $("#sucMsg").text(sucMsg); $("#sucMsg").show("fast"); $("#errMsg").hide("fast"); - setTimeout(hideDisplayMessage, 4000); + setTimeout(hideDisplayMessage, 5000); } } $("#password").passwordValidator({ diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/addOrEditUserPage.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/addOrEditUserPage.jsp index 31519fe522..6b344591a6 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/addOrEditUserPage.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/addOrEditUserPage.jsp @@ -10,6 +10,9 @@ pointer-events: none; cursor: default; } +.filter-option { + text-transform: inherit !important; +}
    @@ -88,7 +91,7 @@
    Account status: Pending Verification + class="gray-xs-f mb-xs pl-xs"> Pending verification
    @@ -118,7 +121,7 @@
    @@ -203,6 +206,7 @@
    Role * +
    checked - disabled> - - - - checked - disabled> - - - checked - disabled> - - - -
    +
    - + $(document).on('mouseenter', '.dropdown-toggle', function () { + $(this).removeAttr("title"); + }); + $('#roleId').on('change', function () { + var element = $(this).find('option:selected').text(); + if(element == "Study admin"){ + $('.edit-user-list-widget').show(); + $('.perm-assign').show(); + $('.pull-right').show(); + } else{ + $('.edit-user-list-widget').hide(); + $('.perm-assign').hide(); + $('.pull-right').hide(); + } + }); + + \ No newline at end of file diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/userListPage.jsp b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/userListPage.jsp index dbf677656b..3ece7456e3 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/userListPage.jsp +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/WEB-INF/view/users/userListPage.jsp @@ -12,6 +12,10 @@ #user_list tr th { padding-left: 20px !important; } + +.filter-option { + text-transform: inherit !important; +}
    ROLE - Actions + Actions @@ -91,14 +95,13 @@ plRow cRow > -
    ${fn:escapeXml(user.userFullName)}
    +
    ${fn:escapeXml(user.userFullName)}
    -
    ${user.userEmail}
    +
    ${user.userEmail}
    ${user.roleName} - + @@ -113,9 +116,9 @@ data-toggle="tooltip" id="label${user.userId}" data-placement="top" title=Account status: Invitation sent, pending activation" + test="${empty user.userPassword}">title="Account status: Invitation sent, pending activation"
    title="Account status: Pending Verification" + test="${user.emailChanged}">title="Account status: Pending verification" title="Account status: Active" \ No newline at end of file diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/css/sprites_v3.css b/study-builder/fdahpStudyDesigner/src/main/webapp/css/sprites_v3.css index 3f73d11f7a..b2c60ebdcd 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/css/sprites_v3.css +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/css/sprites_v3.css @@ -62,3 +62,4 @@ cursor: pointer; } +.studytip + .tooltip > .tooltip-inner {max-width:50% !important;} diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/css/style.css b/study-builder/fdahpStudyDesigner/src/main/webapp/css/style.css index 270c4dbde0..a94c0d9239 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/css/style.css +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/css/style.css @@ -574,7 +574,7 @@ div.mce-edit-area { } .right-content-head { - padding: 12px 30px; + padding: 12px 20px; z-index: 999; position: relative; border-bottom: 1px solid #ddd; @@ -2679,4 +2679,39 @@ a:focus { .logo__space { padding-left: 40px !important; padding-top: 30px !important; -} \ No newline at end of file +} + + +td span.edit-inc-draft:hover { + background: url(../images/icons/edit_draft_icon__hover.png) no-repeat; + width: 20px; + height: 22px; + display: inline-block; + cursor: pointer; + vertical-align: middle; + } + +td span.edit-inc:hover { + background: url(../images/icons/edit_icn__hover.png) no-repeat; + width: 20px; + height: 22px; + display: inline-block; + cursor: pointer; + vertical-align: middle; + } + +td span.eye-inc:hover { + background: url(../images/icons/live_icn__hover.png) no-repeat; + width: 20px; + height: 16px; + display: inline-block; + cursor: pointer; + vertical-align: middle; +} + + +.tooltip > .tooltip-inner { +text-align: left !important; +align-items: left !important; +width:100% +} diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/edit_draft_icon__hover.png b/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/edit_draft_icon__hover.png new file mode 100644 index 0000000000..2e17a96210 Binary files /dev/null and b/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/edit_draft_icon__hover.png differ diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/edit_icn__hover.png b/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/edit_icn__hover.png new file mode 100644 index 0000000000..a31f8eb9b8 Binary files /dev/null and b/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/edit_icn__hover.png differ diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/live_icn__hover.png b/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/live_icn__hover.png new file mode 100644 index 0000000000..af1022a94e Binary files /dev/null and b/study-builder/fdahpStudyDesigner/src/main/webapp/images/icons/live_icn__hover.png differ diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_innerScreens.png b/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_innerScreens.png index d05e11039d..2738abc376 100644 Binary files a/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_innerScreens.png and b/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_innerScreens.png differ diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_landing_welcome.png b/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_landing_welcome.png index e0a62b941f..a5ca798330 100644 Binary files a/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_landing_welcome.png and b/study-builder/fdahpStudyDesigner/src/main/webapp/images/logo/logo_landing_welcome.png differ diff --git a/study-builder/fdahpStudyDesigner/src/main/webapp/js/common.js b/study-builder/fdahpStudyDesigner/src/main/webapp/js/common.js index 1c9d2d6e28..1c1419e240 100644 --- a/study-builder/fdahpStudyDesigner/src/main/webapp/js/common.js +++ b/study-builder/fdahpStudyDesigner/src/main/webapp/js/common.js @@ -351,12 +351,6 @@ $(document) } }); - $(document).on("contextmenu", function (e) { - e.preventDefault(); - alert("Right click has been disabled."); - return false; - }); - document.onkeypress = function (event) { event = (event || window.event); if (event.keyCode == 123) { @@ -786,7 +780,7 @@ $(document) "fast"); setTimeout( hideDisplayMessage, - 4000); + 5000); $('#password') .attr( "type", diff --git a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/common/BaseMockIT.java b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/common/BaseMockIT.java index b05c0a7a89..b28c07cc19 100644 --- a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/common/BaseMockIT.java +++ b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/common/BaseMockIT.java @@ -23,7 +23,9 @@ import com.fdahpstudydesigner.config.WebAppTestConfig; import com.fdahpstudydesigner.service.AuditEventService; import com.fdahpstudydesigner.util.FdahpStudyDesignerConstants; +import com.fdahpstudydesigner.util.FdahpStudyDesignerUtil; import com.fdahpstudydesigner.util.SessionObject; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -121,9 +123,11 @@ public class BaseMockIT { protected List auditRequests = new ArrayList<>(); @Before - public void setUp() { + public void setUp() throws IOException { logger.debug(String.format("BEGIN TEST: %s", testName.getMethodName())); + loadAppProperties(); + MockHttpServletRequest request = new MockHttpServletRequest(); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); @@ -146,6 +150,15 @@ public AuditLogEventRequest answer(final InvocationOnMock invocation) .postAuditLogEvent(Mockito.any(AuditLogEventRequest.class)); } + private void loadAppProperties() throws IOException { + Map appProperties = FdahpStudyDesignerUtil.getAppProperties(); + appProperties.put("from.email.address", "sendmail029@gmail.com"); + appProperties.put("from.email.password", "Password@009"); + appProperties.put("smtp.hostname", "smtp.gmail.com"); + appProperties.put("from.email.use_ip_whitelist", " "); + appProperties.put("from.email.domain", " "); + } + @After public void cleanUp() { auditRequests.clear(); diff --git a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/LoginControllerTest.java b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/LoginControllerTest.java index c7811dfda7..5d3c991188 100644 --- a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/LoginControllerTest.java +++ b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/LoginControllerTest.java @@ -187,11 +187,7 @@ public void shouldActivateNewUserAccount() throws Exception { .sessionAttrs(getSessionAttributes()); addParams(requestBuilder, userBO); - mockMvc - .perform(requestBuilder) - .andDo(print()) - .andExpect(status().isFound()) - .andExpect(view().name("redirect:sessionOut.do")); + mockMvc.perform(requestBuilder).andDo(print()).andExpect(status().isFound()); verifyAuditEventCall(NEW_USER_ACCOUNT_ACTIVATED); verifyAuditEventCall(PASSWORD_RESET_SUCCEEDED); diff --git a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/StudyQuestionnaireControllerTest.java b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/StudyQuestionnaireControllerTest.java index 8a0c865707..475aab1e41 100644 --- a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/StudyQuestionnaireControllerTest.java +++ b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/StudyQuestionnaireControllerTest.java @@ -9,9 +9,7 @@ package com.fdahpstudydesigner.controller; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_ACTIVE_TASK_SECTION_MARKED_COMPLETE; -import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_NEW_QUESTIONNAIRE_CREATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTIONNAIRE_DELETED; -import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTIONNAIRE_SAVED_OR_UPDATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.STUDY_QUESTION_STEP_IN_FORM_DELETED; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -21,6 +19,9 @@ import com.fdahpstudydesigner.bo.QuestionnaireBo; import com.fdahpstudydesigner.common.BaseMockIT; import com.fdahpstudydesigner.common.PathMappingUri; +import com.fdahpstudydesigner.util.FdahpStudyDesignerConstants; +import com.fdahpstudydesigner.util.SessionObject; +import java.util.HashMap; import org.junit.Test; import org.springframework.http.HttpHeaders; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -65,32 +66,21 @@ public void shouldDeleteQuestionnaire() throws Exception { verifyAuditEventCall(STUDY_QUESTIONNAIRE_DELETED); } - @Test - public void shouldCreateNewStudyActiveTask() throws Exception { - HttpHeaders headers = getCommonHeaders(); - - mockMvc - .perform( - post(PathMappingUri.SAVE_OR_UPDATE_QUETIONNAIR_SCHEDULE.getPath()) - .headers(headers) - .sessionAttr(CUSTOM_STUDY_ID_ATTR_NAME, "customStudyId") - .sessionAttrs(getSessionAttributes())) - .andDo(print()) - .andExpect(status().isFound()) - .andExpect(view().name("redirect:/adminStudies/viewStudyQuestionnaires.do")); - - verifyAuditEventCall(STUDY_NEW_QUESTIONNAIRE_CREATED); - } - @Test public void shouldSaveOrUpdateStudyActiveTask() throws Exception { HttpHeaders headers = getCommonHeaders(); + SessionObject sessionObj = getSessionObject(); + sessionObj.setUserId(1); + HashMap sessionAttributes = new HashMap(); + sessionAttributes.put(FdahpStudyDesignerConstants.SESSION_OBJECT, sessionObj); + MockHttpServletRequestBuilder requestBuilder = post(PathMappingUri.SAVE_OR_UPDATE_QUETIONNAIR_SCHEDULE.getPath()) .headers(headers) .sessionAttr(CUSTOM_STUDY_ID_ATTR_NAME, "OpenStudy003") - .sessionAttrs(getSessionAttributes()); + .sessionAttr(STUDY_ID_ATTR_NAME, "678599") + .sessionAttrs(sessionAttributes); QuestionnaireBo questionnaireBo = new QuestionnaireBo(); questionnaireBo.setId(2); @@ -106,7 +96,7 @@ public void shouldSaveOrUpdateStudyActiveTask() throws Exception { .andExpect(status().isFound()) .andExpect(view().name("redirect:/adminStudies/viewStudyQuestionnaires.do")); - verifyAuditEventCall(STUDY_QUESTIONNAIRE_SAVED_OR_UPDATED); + verifyAuditEventCall(STUDY_ACTIVE_TASK_SECTION_MARKED_COMPLETE); } @Test diff --git a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/UsersControllerTest.java b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/UsersControllerTest.java index 9a2c13d89c..1c21d46f83 100644 --- a/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/UsersControllerTest.java +++ b/study-builder/fdahpStudyDesigner/src/test/java/com/fdahpstudydesigner/controller/UsersControllerTest.java @@ -11,10 +11,10 @@ import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.ACCOUNT_DETAILS_VIEWED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_CREATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_INVITATION_EMAIL_SENT; +import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.NEW_USER_INVITATION_RESENT; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_ENFORCED_FOR_ALL_USERS; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_ENFORCED_FOR_USER; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_CHANGE_ENFORCEMENT_EMAIL_FAILED; -import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.PASSWORD_HELP_EMAIL_FAILED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_ACCOUNT_RE_ACTIVATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_RECORD_DEACTIVATED; import static com.fdahpstudydesigner.common.StudyBuilderAuditEvent.USER_RECORD_UPDATED; @@ -127,16 +127,14 @@ public void shouldResendActivateDetailsLink() throws Exception { mockMvc .perform( post(PathMappingUri.RESEND_ACTIVATE_DETAILS_LINK.getPath()) - .param("userId", "15") + .param("userId", "1") .headers(headers) .sessionAttrs(getSession())) .andDo(print()) .andExpect(status().isFound()) .andExpect(view().name("redirect:/adminUsersView/getUserList.do")); - // H2 database doesn't support Column "BINARY". Expect LoginDAOImpl throws - // org.h2.jdbc.JdbcSQLException: Column "BINARY" not found; - verifyAuditEventCall(PASSWORD_HELP_EMAIL_FAILED); + verifyAuditEventCall(NEW_USER_INVITATION_RESENT); } @Test @@ -184,6 +182,7 @@ public void shouldUpdateUserDetails() throws Exception { HttpHeaders headers = getCommonHeaders(); UserBO userBo = new UserBO(); userBo.setUserEmail("superunittest@grr.la"); + userBo.setRoleId(1); MockHttpServletRequestBuilder requestBuilder = post(PathMappingUri.ADD_OR_UPDATE_USER_DETAILS.getPath()) @@ -218,6 +217,7 @@ public void shouldAddUserDetails() throws Exception { userBo.setFirstName("new_user_first_name"); userBo.setLastName("new_user_last_name"); userBo.setPhoneNumber("654665146432"); + userBo.setRoleId(1); MockHttpServletRequestBuilder requestBuilder = post(PathMappingUri.ADD_OR_UPDATE_USER_DETAILS.getPath()) diff --git a/study-builder/fdahpStudyDesigner/src/test/resources/application-mockit.properties b/study-builder/fdahpStudyDesigner/src/test/resources/application-mockit.properties index d47eab58d6..0ffbb72777 100644 --- a/study-builder/fdahpStudyDesigner/src/test/resources/application-mockit.properties +++ b/study-builder/fdahpStudyDesigner/src/test/resources/application-mockit.properties @@ -1,4 +1,15 @@ # application version applicationVersion=1.0 +from.email.address=sendmail029@gmail.com +# Unused with IP whitelist. +from.email.password=Password@009 +smtp.hostname=smtp.gmail.com +smtp.portvalue=465 +sslfactory.value=javax.net.ssl.SSLSocketFactory +# Use hostname of smtp-relay if enabled. +from.email.use_ip_whitelist= +# Domain should be the email domain excluding the end, +# i.e. domain instead of domain.com +from.email.domain= ########################## Web Configuration Portal Web-Services (WCP WS) Configuration Details ENDS ########################## \ No newline at end of file diff --git a/study-builder/fdahpStudyDesigner/src/test/resources/import.sql b/study-builder/fdahpStudyDesigner/src/test/resources/import.sql index 34b4bd3933..138bbae731 100644 --- a/study-builder/fdahpStudyDesigner/src/test/resources/import.sql +++ b/study-builder/fdahpStudyDesigner/src/test/resources/import.sql @@ -1,6 +1,6 @@ INSERT INTO `user_permissions` (`permission_id`, `permissions`) VALUES (8, 'ROLE_CREATE_MANAGE_STUDIES'), (6, 'ROLE_MANAGE_APP_WIDE_NOTIFICATION_EDIT'), (4, 'ROLE_MANAGE_APP_WIDE_NOTIFICATION_VIEW'), (2, 'ROLE_MANAGE_STUDIES'), (5, 'ROLE_MANAGE_USERS_EDIT'), (7, 'ROLE_MANAGE_USERS_VIEW'), (1, 'ROLE_SUPERADMIN'); -INSERT INTO `users` (`user_id`, `first_name`, `last_name`, `email`, `password`, `role_id`, `created_by`, `modified_by`, `status`, `accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`, `force_logout`, `security_token`) VALUES ('1', 'abc', 'xyx', 'ttt@gmail.com', '$2a$10$uSKnFqkar9ugqrdD1KElcOcPGEtdpuNMvwlHfRGwX4jovq.sH0e/q', '1', '1', '1', '1', '1', '1', '1', 'N', NULL), ('2', 'firstname2', 'laststname2', 'super@gmail.com', NULL, '8', '1', '1', '1', '1', '1', '1', 'N', 'N8K7zYrc0F'), ('3', 'firstname3', 'laststname3', 'super3@gmail.com', '$2a$10$uSKnFqkar9ugqrdD1KElcOcPGEtdpuNMvwlHfRGwX4jovq.sH0e/q', '8', '1', '1', '1', '1', '1', '1', 'N', 'jkh7zYrc0F'); +INSERT INTO `users` (`user_id`, `first_name`, `last_name`, `email`, `password`, `role_id`, `created_by`, `modified_by`, `status`, `accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`, `force_logout`, `security_token`) VALUES ('1', 'abc', 'xyx', 'ttt@gmail.com', '$2a$10$uSKnFqkar9ugqrdD1KElcOcPGEtdpuNMvwlHfRGwX4jovq.sH0e/q', '1', '1', '1', '0', '1', '1', '1', 'N', NULL), ('2', 'firstname2', 'laststname2', 'super@gmail.com', NULL, '8', '1', '1', '1', '1', '1', '1', 'N', 'N8K7zYrc0F'), ('3', 'firstname3', 'laststname3', 'super3@gmail.com', '$2a$10$uSKnFqkar9ugqrdD1KElcOcPGEtdpuNMvwlHfRGwX4jovq.sH0e/q', '8', '1', '1', '1', '1', '1', '1', 'N', 'jkh7zYrc0F'); INSERT INTO `user_permission_mapping` (`user_id`, `permission_id`) VALUES (2, 8); @@ -54,7 +54,7 @@ INSERT INTO `active_task` (`id`, `study_id`, `frequency`, `duration`, `active_ta INSERT INTO `study_version` (`version_id`, `activity_version`, `custom_study_id`, `study_version`, `consent_version`) VALUES (989, 1, '678595', 1, 1); -INSERT INTO `study_sequence` (`study_sequence_id`, `study_id`, `actions`, `basic_info`, `check_list`, `comprehension_test`, `consent_edu_info`, `e_consent`, `eligibility`, `miscellaneous_branding`, `miscellaneous_notification`, `miscellaneous_resources`, `over_view`, `setting_admins`, `study_dashboard_chart`, `study_dashboard_stats`, `study_exc_active_task`, `study_exc_questionnaries`) VALUES (10635, 678579, 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'); +INSERT INTO `study_sequence` (`study_sequence_id`, `study_id`, `actions`, `basic_info`, `check_list`, `comprehension_test`, `consent_edu_info`, `e_consent`, `eligibility`, `miscellaneous_branding`, `miscellaneous_notification`, `miscellaneous_resources`, `over_view`, `setting_admins`, `study_dashboard_chart`, `study_dashboard_stats`, `study_exc_active_task`, `study_exc_questionnaries`) VALUES (10635, 678579, 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'),(10636, 678599, 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'); INSERT INTO `consent` (`id`, `study_id`, `comprehension_test_minimum_score`, `share_data_permissions`, `title`, `custom_study_id`, `is_live`, `tagline_description`, `short_description`, `long_description`, `learn_more_text`, `consent_doc_type`, `consent_doc_content`, `allow_without_permission`, `e_consent_firstname`, `e_consent_lastname`, `e_consent_agree`, `e_consent_signature`, `e_consent_datetime`, `created_on`, `created_by`, `modified_on`, `modified_by`) VALUES (342, 678580, 1, 'Yes', 'TestOnly', NULL, 1, 'TestOnly', 'TestOnly', 'TestOnly', '

    TestOnly

    ', 'Auto', 'Data gathering

    <Elaborated Content >


    ', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', '2020-03-24 07:57:45', 59, '2020-03-24 07:59:39', 59); @@ -62,7 +62,7 @@ INSERT INTO `studies` (`id`, `custom_study_id`, `name`, `full_name`, `type`, `pl INSERT INTO `study_sequence` (`study_sequence_id`, `study_id`, `actions`, `basic_info`, `check_list`, `comprehension_test`, `consent_edu_info`, `e_consent`, `eligibility`, `miscellaneous_branding`, `miscellaneous_notification`, `miscellaneous_resources`, `over_view`, `setting_admins`, `study_dashboard_chart`, `study_dashboard_stats`, `study_exc_active_task`, `study_exc_questionnaries`) VALUES (10644, 678580, 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'); -INSERT INTO `studies` (`id`, `custom_study_id`, `name`, `full_name`, `type`, `platform`, `category`, `research_sponsor`, `tentative_duration`, `tentative_duration_weekmonth`, `description`, `enrolling_participants`, `retain_participant`, `allow_rejoin`, `irb_review`, `inbox_email_address`, `created_by`, `created_on`, `modified_by`, `modified_on`, `status`, `sequence_number`, `thumbnail_image`, `media_link`, `allow_rejoin_text`, `study_website`, `study_tagline`, `study_lunched_date`, `study_pre_active_flag`, `has_activity_draft`, `has_consent_draft`, `has_study_draft`, `is_live`, `version`, `has_activitetask_draft`, `has_questionnaire_draft`, `enrollmentdate_as_anchordate`, `app_id`) VALUES (678680, '678999', 'OpenStudy01', 'OpenStudy01', 'GT', NULL, '8', 'CDC', 100, 'Days', '

    Study for Covid-19

    ', NULL, NULL, NULL, NULL, 'test@gmail.com', 59, '2020-03-24 07:47:00', 59, '2020-03-24 07:47:00', 'Pre-launch', NULL, 'STUDY_OO_03242020074700.jpg', NULL, NULL, 'http://www.google.com', 'Study for Covid-19', NULL, 'N', 0, 0, 1, 1, 1, 0, 0, 'N', 'GCPMS001'); +INSERT INTO `studies` (`id`, `custom_study_id`, `name`, `full_name`, `type`, `platform`, `category`, `research_sponsor`, `tentative_duration`, `tentative_duration_weekmonth`, `description`, `enrolling_participants`, `retain_participant`, `allow_rejoin`, `irb_review`, `inbox_email_address`, `created_by`, `created_on`, `modified_by`, `modified_on`, `status`, `sequence_number`, `thumbnail_image`, `media_link`, `allow_rejoin_text`, `study_website`, `study_tagline`, `study_lunched_date`, `study_pre_active_flag`, `has_activity_draft`, `has_consent_draft`, `has_study_draft`, `is_live`, `version`, `has_activitetask_draft`, `has_questionnaire_draft`, `enrollmentdate_as_anchordate`, `app_id`) VALUES (678680, '678999', 'OpenStudy01', 'OpenStudy01', 'GT', NULL, '8', 'CDC', 100, 'Days', '

    Study for Covid-19

    ', NULL, NULL, NULL, NULL, 'test@gmail.com', 59, '2020-03-24 07:47:00', 59, '2020-03-24 07:47:00', 'Pre-launch', NULL, 'STUDY_OO_03242020074700.jpg', NULL, NULL, 'http://www.google.com', 'Study for Covid-19', NULL, 'N', 0, 0, 1, 1, 1, 0, 0, 'N', 'GCPMS001'), (678599, '678099', 'OpenStudy01', 'OpenStudy01', 'GT', NULL, '8', 'CDC', 100, 'Days', '

    Study for Covid-19

    ', NULL, NULL, NULL, NULL, 'test@gmail.com', 59, '2020-03-24 07:47:00', 59, '2020-03-24 07:47:00', 'Pre-launch', NULL, 'STUDY_OO_03242020074700.jpg', NULL, NULL, 'http://www.google.com', 'Study for Covid-19', NULL, 'N', 0, 0, 1, 1, 1, 0, 0, 'N', 'GCPMS001'); INSERT INTO `study_sequence` (`study_sequence_id`, `study_id`, `actions`, `basic_info`, `check_list`, `comprehension_test`, `consent_edu_info`, `e_consent`, `eligibility`, `miscellaneous_branding`, `miscellaneous_notification`, `miscellaneous_resources`, `over_view`, `setting_admins`, `study_dashboard_chart`, `study_dashboard_stats`, `study_exc_active_task`, `study_exc_questionnaries`) VALUES (10248, 678680, 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'); diff --git a/study-datastore/src/main/java/com/hphc/mystudies/bean/ReviewBean.java b/study-datastore/src/main/java/com/hphc/mystudies/bean/ReviewBean.java index eda65b532a..c7e59b0bf5 100644 --- a/study-datastore/src/main/java/com/hphc/mystudies/bean/ReviewBean.java +++ b/study-datastore/src/main/java/com/hphc/mystudies/bean/ReviewBean.java @@ -1,5 +1,6 @@ /* * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies @@ -24,18 +25,8 @@ public class ReviewBean { - private String reasonForConsent = ""; - private String reviewHTML = ""; - public String getReasonForConsent() { - return reasonForConsent; - } - - public void setReasonForConsent(String reasonForConsent) { - this.reasonForConsent = reasonForConsent; - } - public String getReviewHTML() { return reviewHTML; } diff --git a/study-datastore/src/main/java/com/hphc/mystudies/bean/SettingsBean.java b/study-datastore/src/main/java/com/hphc/mystudies/bean/SettingsBean.java index ae0f2631f0..82babb62da 100644 --- a/study-datastore/src/main/java/com/hphc/mystudies/bean/SettingsBean.java +++ b/study-datastore/src/main/java/com/hphc/mystudies/bean/SettingsBean.java @@ -1,24 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: + * Copyright 2020-2021 Google LLC * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.hphc.mystudies.bean; @@ -28,8 +13,6 @@ public class SettingsBean { private String platform = ""; - private boolean rejoin = false; - public boolean isEnrolling() { return enrolling; } @@ -45,12 +28,4 @@ public String getPlatform() { public void setPlatform(String platform) { this.platform = platform; } - - public boolean isRejoin() { - return rejoin; - } - - public void setRejoin(boolean rejoin) { - this.rejoin = rejoin; - } } diff --git a/study-datastore/src/main/java/com/hphc/mystudies/bean/StudyInfoResponse.java b/study-datastore/src/main/java/com/hphc/mystudies/bean/StudyInfoResponse.java index 3299c06215..6cac80b1e7 100644 --- a/study-datastore/src/main/java/com/hphc/mystudies/bean/StudyInfoResponse.java +++ b/study-datastore/src/main/java/com/hphc/mystudies/bean/StudyInfoResponse.java @@ -1,24 +1,9 @@ /* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: + * Copyright 2020-2021 Google LLC * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.hphc.mystudies.bean; @@ -36,8 +21,6 @@ public class StudyInfoResponse { private AnchorDateBean anchorDate = new AnchorDateBean(); - private WithdrawalConfigBean withdrawalConfig = new WithdrawalConfigBean(); - public String getMessage() { return message; } @@ -69,12 +52,4 @@ public AnchorDateBean getAnchorDate() { public void setAnchorDate(AnchorDateBean anchorDate) { this.anchorDate = anchorDate; } - - public WithdrawalConfigBean getWithdrawalConfig() { - return withdrawalConfig; - } - - public void setWithdrawalConfig(WithdrawalConfigBean withdrawalConfig) { - this.withdrawalConfig = withdrawalConfig; - } } diff --git a/study-datastore/src/main/java/com/hphc/mystudies/bean/WithdrawalConfigBean.java b/study-datastore/src/main/java/com/hphc/mystudies/bean/WithdrawalConfigBean.java deleted file mode 100644 index d3eef6cc34..0000000000 --- a/study-datastore/src/main/java/com/hphc/mystudies/bean/WithdrawalConfigBean.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - * following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ -package com.hphc.mystudies.bean; - -public class WithdrawalConfigBean { - - private String type = ""; - - private String message = ""; - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/study-datastore/src/main/java/com/hphc/mystudies/dao/StudyMetaDataDao.java b/study-datastore/src/main/java/com/hphc/mystudies/dao/StudyMetaDataDao.java index 8220162aeb..7bf4eeb373 100644 --- a/study-datastore/src/main/java/com/hphc/mystudies/dao/StudyMetaDataDao.java +++ b/study-datastore/src/main/java/com/hphc/mystudies/dao/StudyMetaDataDao.java @@ -1,24 +1,15 @@ /* * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the * following conditions: * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * Funding Source: Food and Drug Administration ("Funding Agency") effective 18 September 2014 as Contract no. - * HHSF22320140030I/HHSF22301006T (the "Prime Contract"). - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. */ package com.hphc.mystudies.dao; @@ -44,7 +35,6 @@ import com.hphc.mystudies.bean.StudyBean; import com.hphc.mystudies.bean.StudyInfoResponse; import com.hphc.mystudies.bean.StudyResponse; -import com.hphc.mystudies.bean.WithdrawalConfigBean; import com.hphc.mystudies.dto.ActiveTaskDto; import com.hphc.mystudies.dto.AnchorDateTypeDto; import com.hphc.mystudies.dto.ComprehensionTestQuestionDto; @@ -340,12 +330,7 @@ public StudyResponse studyList(String authorization, String applicationId) throw break; } } - if (StringUtils.isNotEmpty(studyDto.getAllowRejoin()) - && studyDto.getAllowRejoin().equalsIgnoreCase(StudyMetaDataConstants.YES)) { - settings.setRejoin(true); - } else { - settings.setRejoin(false); - } + if (StringUtils.isNotEmpty(studyDto.getEnrollingParticipants()) && studyDto .getEnrollingParticipants() @@ -740,11 +725,6 @@ public EligibilityConsentResponse eligibilityConsentMetadata(String studyId) thr .replaceAll("em>", "i>") .replaceAll(" jsonPayloadMap = getObjectMapper().convertValue(auditRequest, Map.class); - // The data to write to the log - Map jsonPayloadMap = getObjectMapper().convertValue(auditRequest, Map.class); + LogEntry entry = + LogEntry.newBuilder(Payload.JsonPayload.of(jsonPayloadMap)) + .setTimestamp(auditRequest.getOccurred().getTime()) + .setSeverity(Severity.INFO) + .setLogName(AUDIT_LOG_NAME) + .setResource(MonitoredResource.newBuilder("global").build()) + .build(); - LogEntry entry = LogEntry.newBuilder(Payload.JsonPayload.of(jsonPayloadMap)) - .setTimestamp(auditRequest.getOccurred().getTime()) - .setSeverity(Severity.INFO) - .setLogName(AUDIT_LOG_NAME) - .setResource(MonitoredResource.newBuilder("global").build()) - .build(); - // Writes the log entry asynchronously - logging.write(Collections.singleton(entry)); + // Writes the log entry asynchronously + logging.write(Collections.singleton(entry)); - LOGGER.debug(String.format("postAuditLogEvent() for %s event finished", auditRequest.getEventCode())); + } catch (Exception e) { + LOGGER.error(String.format("%s failed with an exception", auditRequest.getEventCode()), e); + } + LOGGER.debug( + String.format("postAuditLogEvent() for %s event finished", auditRequest.getEventCode())); } private static ObjectMapper getObjectMapper() { diff --git a/study-datastore/src/main/java/com/hphc/mystudies/web/servlet/AuthenticationService.java b/study-datastore/src/main/java/com/hphc/mystudies/web/servlet/AuthenticationService.java index b5957f8725..c8573ea25f 100644 --- a/study-datastore/src/main/java/com/hphc/mystudies/web/servlet/AuthenticationService.java +++ b/study-datastore/src/main/java/com/hphc/mystudies/web/servlet/AuthenticationService.java @@ -1,5 +1,6 @@ /* * Copyright © 2017-2018 Harvard Pilgrim Health Care Institute (HPHCI) and its Contributors. + * Copyright 2020-2021 Google LLC * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies @@ -20,11 +21,13 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ + package com.hphc.mystudies.web.servlet; import com.hphc.mystudies.util.StudyMetaDataUtil; import com.sun.jersey.core.util.Base64; import java.util.HashMap; +import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -40,6 +43,8 @@ public boolean authenticate(String authCredentials) { LOGGER.info("INFO: AuthenticationService - authenticate() - Starts"); boolean authenticationStatus = false; String bundleIdAndAppToken = null; + String bundleIdKey = ""; + String appTokenKey = ""; try { if (StringUtils.isNotEmpty(authCredentials) && authCredentials.contains("Basic")) { final String encodedUserPassword = authCredentials.replaceFirst("Basic" + " ", ""); @@ -49,7 +54,19 @@ public boolean authenticate(String authCredentials) { final StringTokenizer tokenizer = new StringTokenizer(bundleIdAndAppToken, ":"); final String bundleId = tokenizer.nextToken(); final String appToken = tokenizer.nextToken(); - if (authPropMap.containsValue(bundleId) && authPropMap.containsValue(appToken)) { + + for (Map.Entry map : authPropMap.entrySet()) { + if (map.getValue().equals(appToken)) { + appTokenKey = map.getKey(); + } + if (map.getValue().equals(bundleId)) { + bundleIdKey = map.getKey(); + } + } + + if (authPropMap.containsValue(bundleId) + && authPropMap.containsValue(appToken) + && isValidPlatformType(appTokenKey, bundleIdKey)) { authenticationStatus = true; } } @@ -61,4 +78,15 @@ public boolean authenticate(String authCredentials) { LOGGER.info("INFO: AuthenticationService - authenticate() - Ends"); return authenticationStatus; } + + private static boolean isValidPlatformType(String appTokenKey, String bundleIdKey) { + + final StringTokenizer appTokenizer = new StringTokenizer(appTokenKey, "."); + final String appTokenPlatFormType = appTokenizer.nextToken(); + + final StringTokenizer bundleIdTokenizer = new StringTokenizer(bundleIdKey, "."); + final String bundleIdPlatformType = bundleIdTokenizer.nextToken(); + + return StringUtils.equals(appTokenPlatFormType, bundleIdPlatformType); + } }