In brief: Lilly researcher will sign in to researcher portal and create a user account with a PIN (which we will validate is unique and hasn't been used before). Then on phone, they'll enter the pin to initialize the application (sign in).
Study
Add flag, isExternalIdValidated. At the very least it should toggle between current behavior (any string can be set) and a stricter set of rules described below.
There are actually many behaviors and there may need to be more than one flag:
- should ID be validated?
- should ID use be assigned to only one person?
- is the ID required at sign up (that is, don't create an account until you've validated the external ID you're going to use for it)
- can the ID be unassigned or re-assigned?
- can the ID be deleted once assigned? (you'd think no, but see below)
ExternalId Table
- String studyId (key)
- String externalId (range)
- String healthCode
- long reservation
To assign healthCode to an ID is to assign it, but I propose a reservation design so that during sign up, we can validate and hold an ID until we have an account with a healthCode that we can register in the first place.
ExternalIdDao
asdfasdf
ExternalIdService
// With optional filter to return only unassigned IDs, useful for Lilly
getExternalIds(StudyIdentifier, int offsetBy, int pageSize, Boolean assignmentFilter);
// Existing IDs would be completely ignored. To reset IDs, as was done multiple times in FPHS, let's implement
// the ability for researchers or admins to delete users through the researcher UI.
addExternalIds(StudyIdentifier studyId, List<String> externalIdentifiers);
// Returns true if there's no healthCode and the reservation timestamp minus time of request is
// less than some timeout value (or zero/unset). If it is going to return true, it sets the reservation
// field to current timestamp, preventing other callers from proceeding to use the ID. If the call that
// reserved the ID fails, it will become available again after the timeout.
boolean reserveExternalId(StudyIdentifier studyId, String externalIdentifier);
// Assigns the ID once you have the healthCode. If reservation failed, the caller should not
// proceed to call this method.
assignExternalId(StudyIdentifier studyId, String externalIdentifier, String healthCode);
unassignExternalId(StudyIdentifier studyId, String externalIdentifier);
ExternalIdController
GET /v3/studies/externalIds?offsetBy=n&pageSize=n&assignmentFilter=<boolean>
get paged list of identifiers, should be able to filter on assigned/unassigned for Lilly. Would basically just return the IDs and assignment status
POST /3/studies/externalIds
add identifiers in bulk
The rest is tied into existing APIs or new participant APIs. Validation and assignment would occur anywhere we change externalId:
POST /v3/participants
Create a user, including the externalId. Creation should fail if externalId is in invalid or assigned
- /v3/auth/signUp
Should be able to provide an externalId at sign up, sign up should fail if the ID is invalid or assigned. - /v3/participants/member/options
- /v3/users/self/externalId
- physically delete user - unassign the external ID associated with the user.
Researcher UI
We should create a Lilly-specific screen in researcher UI that simplifies creating a user down to selecting a free external ID. Then they can then enter the code on the device. Actually creating a user off of an externalID might be generically useful.
SignUp
Can provide an externalId at sign up and sign up will be rejected if the ID is invalid or assigned. This does allow for enumeration of the IDs, unfortunately, but meets some researcher needs (FPHS).