Simplifying the user management codebase
Rationale: We currently represent users with the Account, User, UserProfile, SignUp, StudyParticipant, UserSession, UserSessionInfo, and AccountSummary objects. In addition we have smaller objects like DataGroups to represent the submission of bits of information about a user's account. With the new ParticipantService now providing all the logic for creating/updating users, short of consenting participants to research, we can consolidate these objects AND increase the functionality and maintainability of the API going forward.
Here's a plan to do this in a backwards compatible manner.
SignUp is being converted to use StudyParticipant. It already works but will be expanded so the client can save anything about a StudyParticipant when creating an account. This is backwards compatible on the server-side, do we need a new route for this on the client side? One that sends a "StudyParticipant" object? If so, I would suggest a /v4/auth/signUp route that functions identically to /v3/auth/signUp.
UserProfile is currently in these APIs (or the APIs exist to manipulate a part of what the client sees as the user profile):
GET /v3/users/self
POST /v3/users/self/externalId
GET /v3/users/self/unsubscribeEmail
POST /v3/users/self/dataSharing
POST /v3/users/self/dataGroups
All of the above APIs can be replaced with the new routes:
GET /v3/participants/self
POST /v3/participants/self
To update any information you would retrieve the StudyParticipant object, update it, and save it. The current user does not need the ID of their own account for this API. We could implement a copy behavior where we update the fields that exist in the JSON returned to us, and leave the other fields unchanged, if this is an issue for the client. But this is different from our other APIs.
The old accounts can be maintained by loading and updating specific fields of a StudyParticipant object, or converting StudyParticipant to the same JSON as a UserProfile object in order to keep returning a UserProfile object. Then UserProfile can be deleted with the conversion happening in the UserProfileController.
AccountSummary will be renamed to StudyParticipantSummary. Otherwise it remains unchanged. It could also be a partially filled out StudyParticipant object but I'm not sure how Dwayne and Josh feel about this kind of use of objects.
UserSessionInfo.id will be renamed to UserSessionInfo.studyParticipantId, however, outside of testing, this is not needed by the clients. It is needed for the DELETE call to delete a user account (admins only).
User is not exposed anywhere in the API. We use it as a Bridge object to construct a UserSession, and as such, it should be possible to remove it and directly assemble a session from an Account with all the required information. This is an extensive code change.
Account is never exposed in the API and it must be maintained. It is our backend agnostic representation of PHI information and it handles work like transparently encrypting PHI information.
That would leave us with StudyParticipantSummary(?), StudyParticipant, UserSession, UserSessionInfo, and Account. This will eliminate the need for individual APIs to update specific fields, like data groups, so one or two of those classes might be eliminated as well.
Additional Functionality
The client will be able to provide any information about a user while signing up, and afterward by editing the StudyParticipant record. For example, if we wanted to allow the user to update their language preference, it would be possible through these new APIs without additional work on the server. And in the future, adding new persisted information will usually only require updates to the StudyParticipant/StudyParticipantService code.
Finally, there are some administrative APIs for testing:POST /v3/users
DELETE /v3/users/:userId
They do things like automatically consenting user accounts to research, or sign users in or out of the system, in order to faciliate testing. I will leave these alone but also use the ParticipantService codebase to simplify maintenance.
Other internal refactorings
Some of the code in AccountDao.signUp can be left to ParticipantService.createParticipant. It may be possible to avoid two calls to Stormpath by refactoring these calls as well (finishing with one create call), so I'll do that.