src/services/UserService.js
import { deprecate } from 'core-decorators';
import Client from '../Client';
import JsonApiService from './JsonApiService';
import AwardedBadge from '../helpers/gamification/AwardedBadge';
import Schedule from '../helpers/reminder/Schedule';
import User from '../helpers/user/User';
import Profile from '../helpers/user/Profile';
import Session from '../helpers/user/Session';
import Entry from '../helpers/ediary/Entry';
import Notification from '../helpers/Notification';
import {
hasAttribute,
hasToken,
isA,
isRequired,
isValid,
validate,
} from '../utilities/ValidationUtility';
import { isEmail } from '../utilities/FormatUtility';
/**
* Service handling User calls with specific endpoints.
*
* In order to manage user-specific data, access, and info, our SDK provides a user sign-in as well
* as a guest sign-in. With the plugin, as soon as a user launches your app, they will
* automatically be signed in as a guest. After you build a login page/UI, your users can enter
* their credentials to sign in to their own account.
*/
class UserService extends JsonApiService {
/**
* Update type to be mobile_users
*/
constructor(type = undefined) {
super(type || Client.instance.user.type || 'mobile_users',
{
title: 'UserService',
obj: 'user',
tokenRequired: ['getChildren']
});
/** @type {String} - The type of the default data */
this.type = type || Client.instance.user.type; // mobile_users or users
}
/**
* Get and Login Session From Response
*
* @private
* @param {Session|User} response - Session or User object from server with authentication token and user information
* @param {Device} device - Mobile user's device
* @return {Session} - Current logged in session
*/
_loginGetSession(response, device = {}) {
let session = new Session();
if (response.type === 'mobile_users') {
session.user = response;
if (session.user.devices && device.id) {
Client.instance.device = session.user.devices.find(obj => obj.id === device.id);
if (Client.instance.device) {
session.authenticationToken = Client.instance.device.accessToken;
}
}
} else if (response.type === 'user_sessions') {
session = response;
}
Client.instance.user = session.user;
Client.instance.authToken = session.authenticationToken;
return session;
}
/**
* Get and Login User From Response
*
* @private
* @param {Session|User} response - Session or User object from server with authentication token and user information
* @param {Device} device - Mobile user's device
* @return {User} - Current logged in user
*/
_loginGetUser(response, device = {}) {
let user = response;
if (response.type === 'mobile_users') {
if (user.devices && device.id) {
Client.instance.device = user.devices.find(obj => obj.id === device.id);
if (Client.instance.device) {
Client.instance.authToken = Client.instance.device.accessToken;
}
}
} else if (response.type === 'user_sessions') {
({ user } = response);
Client.instance.authToken = response.authenticationToken;
}
Client.instance.user = user;
return user;
}
/**
* Allows a user to accept an invitation given a device and token
*
* @throws {Clinical6Error} - If missing required parameters
* @param {!String} token - Token from request password reset email or accepting invitation email
* @param {String} [options] - Additional options for accepting invitations
* @param {Boolean} [options.anti_spam_accepted] - If the anti-spam policy was accepted by the user.
* @param {Object} [options.device] - Accept invitation with this device
* @param {Number} [options.device.id] - Must be the device id
* @param {String} [options.email] - Email, if it is set with the invitation acceptance page.
* @param {String} [options.password] - Password, if it is set with the invitation acceptance page.
* @param {Boolean} [options.privacy_policy_accepted] - If the privacy policy was accepted by the user.
* @param {Boolean} [options.terms_of_use_accepted] - If the terms of use accepted by the user.
* @return {Promise<User>} - Session object is returned. For Mobile Users, this is a local Session object
* with the authentication token and the user object inserted locally.
*
* @example <caption>Mobile Users</caption>
* import { mobileUserService } from 'clinical6';
* mobileUserService.acceptInvitation('1afw00f...', {
* password: 'myPassword!@',
* terms_of_use_accepted: true,
* privacy_policy_accepted: true,
* anti_spam_accepted: true,
* device: { id: 15 }
* }).then(user => console.log(user));
*
* @example <caption>Admin Users</caption>
* import { userService } from 'clinical6';
* userService.acceptInvitation('1afw00f...', {
* password: 'myPassword!@',
* terms_of_use_accepted: true,
* privacy_policy_accepted: true,
* anti_spam_accepted: true
* }).then(session => console.log(session.user));
*/
async acceptInvitation(token, options = {}) {
validate('UserService.acceptInvitation',
isRequired({ token }),
isA({ token }, 'string'));
const data = { type: 'invitations' };
// add attributes
data.attributes = { invitation_token: token };
if (options.email) {
validate('UserService.acceptInvitation',
isValid(isEmail(options.email), `email is not a valid email`));
data.attributes.email = options.email;
}
if (options.password) {
data.attributes.password = options.password;
}
if (options.terms_of_use_accepted) {
data.attributes.terms_of_use_accepted = options.terms_of_use_accepted;
}
if (options.privacy_policy_accepted) {
data.attributes.privacy_policy_accepted = options.privacy_policy_accepted;
}
if (options.anti_spam_accepted) {
data.attributes.anti_spam_accepted = options.anti_spam_accepted;
}
// add relationships (if any)
const device = options.device || Client.instance.device;
if (options.device || Client.instance.device.id) {
validate('UserService.acceptInvitation',
hasAttribute({ device }, 'id'));
data.relationships = {
devices: {
data: {
type: 'devices',
id: device.id
}
}
};
}
return super.update(data, { url: `/v3/${this.type}/invitation`, cacheMode: 'networkOnly' })
.then((response => this._loginGetUser(response, device)));
}
/**
* Sets the pin value for an invited user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} json - The JSON value with the attributes to provide for
* set pin.
* @param {!String} json.pin - The PIN
* @param {!String} json.pin_confirmation - The PIN Confirmation
* @param {!String} json.invitation_token - The invitation token
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Accept Invitation</caption>
* import { userService } from 'clinical6';
* userService.acceptInvitationPinV2({
* "pin": "123456",
* "pin_confirmation": "123456",
* "invitation_token": "12345678910"
* });
*
* @example <caption>Success Response</caption>
* {
* "status": "ok"
* }
*
* @example <caption>Failure, Password and confirmation do not match</caption>
* {
* "error_detail": "Review the new_password and new_password_confirmation parameters",
* "friendly_error": "Password and confirmation do not match",
* "internal_code": 50362,
* "more_info": "",
* "status": "fail",
* "message": "Password and confirmation do not match"
* }
*
* @example <caption>Failure, No user found with the specified invitation_token</caption>
* {
* "error_detail": {
* "invitation_token": [
* "invalid_value"
* ]
* },
* "friendly_error": "No user found with the specified invitation_token",
* "internal_code": 50364,
* "more_info": "",
* "status": "fail",
* "message": "No user found with the specified invitation_token"
* }
*
* @example <caption>Failure, Pin already exists</caption>
* {
* "error_detail": {
* "password": [
* "already_exists"
* ]
* },
* "friendly_error": "Pin already exists",
* "internal_code": 50374,
* "more_info": "",
* "status": "fail",
* "message": "Pin already exists"
* }
*
* @deprecated
*/
@deprecate
acceptInvitationPinV2(json) {
validate('UserService.acceptInvitationPinV2',
hasToken(),
isRequired({ json }),
hasAttribute({ json }, 'pin'),
hasAttribute({ json }, 'pin_confirmation'),
hasAttribute({ json }, 'invitation_token'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/accept_invitation`, 'put', json).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Add Awarded Badge
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} awardedBadge - The user, must have id
* @param {!Object} awardedBadge.attributes - Hashmap to add for award badge
* @param {!User} awardedBadge.user - The user, must have id
* @param {!Number} awardedBadge.user.id - The user id
* @param {!Badge} awardedBadge.badge - The user, must have id
* @param {!Number} awardedBadge.badge.id - The user id
* @return {Promise<AwardedBadge>} - Promise object that returns one object or hashmap
*
* @example
* import { userService, AwardedBadge, Badge, User } from 'clinical6';
* const awardedBadge = new AwardedBadge({ updatedAt: '2018-06-02' });
* awardedBadge.awardee = new User({ id: 5, ... });
* awardedBadge.badge = new Badge({ id: 5, ... });
*
* // You can add an awarded badge using the `addAwardedBadge` method
* userService.addAwardedBadge(awardedBadge).then(result => console.log(result));
*
* // Additionally, you can do the same thing using `User.addAwardedBadge()`
* user.addAwardedBadge(awardedBadge).then(result => console.log(result));
*/
async addAwardedBadge(awardedBadge) {
validate('UserService.addAwardedBadge',
hasToken(),
isRequired({ awardedBadge }));
awardedBadge = (awardedBadge instanceof AwardedBadge) ? awardedBadge : new AwardedBadge(awardedBadge);
if (!awardedBadge.awardee) {
awardedBadge.awardee = Client.instance.user;
}
validate('UserService.addAwardedBadge',
hasAttribute({ awardedBadge }, 'awardee'),
hasAttribute({ awardedBadge }, 'badge'));
validate('UserService.addAwardedBadge',
hasAttribute({
awardee: awardedBadge.awardee,
badge: awardedBadge.badge,
}, 'id'));
const url = `/v3/${this.type}/${awardedBadge.awardee.id}/awarded_badges`;
return super.insert(awardedBadge.toJSON(), { url, cacheMode: 'networkOnly' });
}
/**
* Add Schedule
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} schedule - The user, must have id
* @param {!Object} schedule.attributes - Hashmap to add for a schedule
* @param {!User} [schedule.mobileUser] - The user, must have id
* @param {!Number} [schedule.mobileUser.id] - The user id
* @return {Promise<Schedule>} - Promise object that returns one object or hashmap
*
* @example
* import { userService, Schedule, User } from 'clinical6';
* const schedule = new Schedule({ updatedAt: '2018-06-02' });
*
* // You can add a schedule using the `addSchedule` method
* userService.addSchedule(schedule).then(result => console.log(result));
*
* // Additionally, you can do the same thing using `User.addSchedule()`
* user.addSchedule(schedule).then(result => console.log(result));
*/
async addSchedule(schedule) {
validate('UserService.addSchedule',
hasToken(),
isRequired({ schedule }));
const user = schedule.mobileUser || Client.instance.user;
validate('UserService.addSchedule',
isRequired({ user }),
hasAttribute({ user }, 'id'));
schedule = (schedule instanceof Schedule) ? schedule : new Schedule(schedule);
const url = `/v3/${this.type}/${user.id}/scheduler/personalized_rule_schedules`;
return super.insert(schedule, { url, cacheMode: 'networkOnly' });
}
/**
* Confirms user with token
*
* @throws {Clinical6Error} - If missing required parameters
* @param {String} token - Token to confirm user with
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise} - Promise object that returns success or failure
*
* @example
* import { userService.confirm('1afji13jiifjadif0ijadf');
*/
async confirm(token, cacheMode = 'networkOnly') {
validate('UserService.confirm',
isRequired({ token }),
isA({ token }, 'string'));
const url = `/v3/${this.type}/confirmation?confirmation_token=${token}`;
return super.get({ }, { url, cacheMode });
}
/**
* @override
* Call a PATCH request on the main obj.type expecting JSON API information.
*
* @param {User} user - User object in which to update
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise} - Message
*
* @example
* import { mobileUserService } from 'clinical6';
*
* // You will be able to delete a User using `delete`.
* mobileUserService.delete({ id: 23 });
* // This will also clear the local storage of this type and id
*/
async delete(user, cacheMode = undefined) {
validate('UserService.delete',
hasToken(),
isRequired({ user }));
return super.delete(user, { cacheMode });
}
/**
* Removes a related mobile user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Number} id - The ID value of the related mobile user to remove.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Delete User</caption>
* import { userService } from 'clinical6';
* userService.deleteRelated(32);
*
* @example <caption>Success Response</caption>
* {
* "invited": []
* }
*
* @example <caption>Failure, Mobile User Not Found</caption>
* {
* "error_detail": {
* "id": "invalid_value"
* },
* "friendly_error": "Mobile User not found using the value:id",
* "internal_code": 50364,
* "more_info": "",
* "status": "fail",
* "message": "Mobile User not found using the value:id"
* }
*/
@deprecate
deleteRelated(id) {
validate('UserService.deleteRelated',
hasToken(),
isRequired({ id }));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/related_mobile_users/${id}`, 'delete').then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Deletes the profile attribute value with the given ID.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Number} id - The ID value of the profile attribute
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Delete Profile Attribute</caption>
* import { userService } from 'clinical6';
* userService.deleteProfileAttribute(3);
*
* @example <caption>Success Response</caption>
* {
* "status": "ok",
* "message": "Profile value was successfully deleted"
* }
*
* @example <caption>Failure, Not Found</caption>
* {
* "error_detail": {
* "ProfileValue": [
* "not_found"
* ]
* },
* "friendly_error": "Profile value not found using the value:-1",
* "internal_code": 50364,
* "more_info": "",
* "status": "fail",
* "message": "Profile value not found using the value:-1"
* }
*/
@deprecate
async deleteProfileAttribute(id) {
validate('UserService.deleteProfileAttribute',
hasToken(),
isRequired({ id }));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/profile_attributes/${id}`, 'delete').then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Removes the current user from the system. This action cannot be undone.
*
* @throws {Clinical6Error} - If missing token
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Delete User</caption>
* import { userService } from 'clinical6';
* userService.deleteCurrent(32);
*/
@deprecate
deleteCurrent() {
validate('UserService.deleteCurrent',
hasToken());
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/mobile_users`, 'delete').then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Disables the current user's account. This action will revoke the user's access to the system.
* The method does not delete any data related to the user.
*
* @throws {Clinical6Error} - If missing token
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Disable User</caption>
* import { userService } from 'clinical6';
* userService.disableCurrent(32);
*/
@deprecate
disableCurrent() {
validate('UserService.disableCurrent',
hasToken());
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/disable`, 'delete').then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Disables a related mobile user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Number} id - The ID value of the related mobile user to disable.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Disable User</caption>
* import { userService } from 'clinical6';
* userService.disable(32);
*
* @example <caption>Failure, Mobile User Not Found</caption>
* {
* "error_detail": {
* "id": "invalid_value"
* },
* "friendly_error": "Mobile User not found using the value:id",
* "internal_code": 50364,
* "more_info": "",
* "status": "fail",
* "message": "Mobile User not found using the value:id"
* }
*/
@deprecate
disable(id) {
validate('UserService.disable',
hasToken(),
isRequired({ id }));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/related_mobile_users/${id}/disable`, 'post', {}).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Sends user's account name via email. This method does not require an access token.
*
* @throws {Clinical6Error} - If missing required parameters
* @param {String} emailAddress - The email address of the account.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Email Account Name</caption>
* import { userService } from 'clinical6';
* userService.emailAccountName();
*/
@deprecate
emailAccountName(emailAddress) {
validate('UserService.emailAccountName',
isRequired({ emailAddress }),
isA({ emailAddress }, 'string'));
const data = {
email: emailAddress,
};
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/forgot_account_name`, 'post', data).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Starts the reset password process and sends an email to the user with instructions.
*
* @throws {Clinical6Error} - If missing required parameters
* @param {String} emailAddress - Email address to send the reset password email to.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Email Password</caption>
* import { userService } from 'clinical6';
* userService.emailPassword();
*/
@deprecate
emailPassword(emailAddress) {
validate('UserService.emailPassword',
isRequired({ emailAddress }),
isA({ emailAddress }, 'string'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/forgot_password`, 'post', { email: emailAddress }).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Enable a related mobile user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Number} id - The ID value of the related mobile user to enable.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Enable User</caption>
* import { userService } from 'clinical6';
* userService.enableUser(19);
*
* @example <caption>Success Response</caption>
* {
* "invited": []
* }
*
* @example <caption>Failure, Mobile User Not Found</caption>
* {
* "error_detail": {
* "id": "invalid_value"
* },
* "friendly_error": "Mobile User not found using the value:id",
* "internal_code": 50364,
* "more_info": "",
* "status": "fail",
* "message": "Mobile User not found using the value:id"
* }
*/
@deprecate
enable(id) {
validate('UserService.enable',
hasToken(),
isRequired({ id }));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/related_mobile_users/${id}/enable`, 'post', {}).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* @override
* Call a GET request expecting JSON API information.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} [params] - Parameters used to get information from server (such as id)
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<User[] | User>}
* - Promise object that returns one User or a key:User
* hashtable
*
* @example
* import { mobileUserService } from 'clinical6';
*
* // You will be able to access these mobile users using the `get` method.
* mobileUserService.get().then(users => console.log(users));
*
* // Additionally, you can retrieve the subcategories for a specific mobile user with the `get`
* // method, using the ID of the desired mobile user as a parameter.
* mobileUserService.get({id: 23}).then(users => console.log(users));
*
* // This method will also pull back the `user.profile` should it exist for the user or list of users
*/
async get(params = {}, cacheMode = undefined) {
validate('UserService.get',
hasToken());
return super.get(params, { cacheMode });
}
/**
* Returns all the values for the given profile attribute.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {String} apiKey - The API key value for the profile attribute.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Get All Profile Attributes</caption>
* import { userService } from 'clinical6';
* userService.getAllProfileAttributes('fj103jaf08j23jfds').then(attr => console.log(attr));
*/
@deprecate
async getAllProfileAttributes(apiKey) {
validate('UserService.getAllProfileAttributes',
hasToken(),
isRequired({ apiKey }),
isA({ apiKey }, 'string'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/profile_attributes/${apiKey}`).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Get User Badges
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} awardee - The user, must have id
* @param {!Number} awardee.id - The user id
* @param {Object} [awardedBadge] - Filters to modify result (date, entry_group_id)
* @param {Number} [awardedBadge.id] - Pulling back a specific badge via id from the user
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Badge[] | Badge>}
* - Promise object that returns one object or hashmap
*
* @example
* import { mobileUserService, User } from 'clinical6';
*
* // You can get entries using the `getAwardedBadges` method
* mobileUserService.getAwardedBadges(new User({ id: 5 })).then(entry => console.log(entry));
*
* const filters = { entry_group_id: 7, date: '2018-06-02'};
* mobileUserService.getAwardedBadges({ id: 5 }, filter).then(entry => console.log(entry));
*
* // Additionally, you can do the same thing using `User.getAwardedBadges()`
* const user = new User({ id: 5, ... });
* user.getAwardedBadges().then(entry => console.log(entry));
* user.getAwardedBadges(filter).then(entry => console.log(entry));
*/
async getAwardedBadges(awardee = undefined, awardedBadge = {}, cacheMode = undefined) {
awardee = awardee || Client.instance.user;
validate('UserService.getAwardedBadges',
hasToken(),
isRequired({ awardee }),
hasAttribute({ awardee }, 'id'),
hasAttribute({ awardee }, 'type'));
awardedBadge.type = AwardedBadge.type;
if (awardedBadge.awardee) {
validate('UserService.getAwardedBadges',
isValid((awardee.id === awardedBadge.awardee.id), `awardee.id must be the same as awardedBadge.awardee.id`));
}
return super.getChildren(awardee, awardedBadge, { cacheMode });
}
/**
* Get Flow Data Group for a user. This method can get back data with or without a Flow Data Group ID.
* This endpoint does not support filters and can only process the page URL params.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} [user] - User - if not defined, use logged in user
* @param {!Number} [user.id] - User ID must exist
* @param {!String} [user.type] - User type ('mobile_users' or 'users') must exist
* @param {!Object} [params] - Parameters used to get information from server (such as id)
* @param {!Object} [params.page] - Pagination information
* @param {Number} [params.page.number] - Number of pages of data to get back
* @param {Number} [params.page.size] - Number of results per page to get back
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<FlowDataGroup>} - Promise object that returns one object
*
* @example
* import { userService, FlowDataGroup } from 'clinical6';
* userService.getDataGroup({ id: 24 });
* const capturedValueGroup = new FlowDataGroup( {id: 24 });
* userService.getDataGroup(capturedValueGroup);
*/
// async getDataGroup(user, params, cacheMode = undefined) {
async getDataGroup(user = undefined, params = {}, cacheMode = undefined) {
user = user || Client.instance.user;
validate('UserService.getDataGroup',
hasToken(),
isRequired({ user }),
hasAttribute({ user }, 'id'),
hasAttribute({ user }, 'type'));
validate('UserService.getDataGroup',
isA({ userId: user.id }, 'number'));
params.type = 'data_collection__captured_value_groups';
if (params.id) {
validate('UserService.getDataGroup',
isA({ paramsId: params.id }, 'number'));
}
return super.getChildren(user, params, { cacheMode });
}
/**
* Get eDiary entries
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} user - The mobile user, must have id
* @param {!Number} user.id - The mobile user id
* @param {Object} [filters] - Filters to modify result (date, entry_group_id)
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Entry[] | Entry>} - Promise object that returns one object or hashmap
*
* @example
* import { mobileUserService, User } from 'clinical6';
*
* // You can get entries using the `getEntries` method
* mobileUserService.getEntries({ id: 5 }).then(entry => console.log(entry));
*
* const filters = { entry_group_id: 7, date: '2018-06-02'};
* mobileUserService.getEntries({ id: 5 }, filter).then(entry => console.log(entry));
*
* // Additionally, you can do the same thing using `User.getEntries()`
* const user = new User({ id: 5, ... });
* user.getEntries().then(entry => console.log(entry));
* user.getEntries(filter).then(entry => console.log(entry));
*/
async getEntries(user, filters = undefined, cacheMode = undefined) {
validate('UserService.getEntries',
hasToken(),
isRequired({ user }));
validate('UserService.getEntries',
hasAttribute({ user }, 'id'));
return super.getChildren(user, { type: Entry.type, filters }, { cacheMode });
}
/**
* Invite a user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {User} user - The user in which to invite (required)
* @param {!String} user.email - The email address of the invited user. (required)
* @param {Object} attributes - A list of optional attributes not found in user or profile. This can contain any key/value.
* @param {String} attributes.invitation_token - The token used for accepting an invitation
* @param {Object} [relationships] - A list of optional relationships not found in user or profile. This can contain any key/value.
* @return {Promise<User>} - Promise object that returns success or failure
*
* @example <caption>Get Invitation Status</caption>
* import { userService, User } from 'clinical6';
* const user = new User({ email: 'john.brown@parallel6.com' });
* let status = await userService.getInvitationStatus(user, { invitation_token: 'asdf1234' });
*
* @example <caption>Get Default User Invitation Status</caption>
* import { userService } from 'clinical6';
* // first param will default to clinical6.user
* let status = await userService.getInvitationStatus(undefined, { invitation_token: 'asdf1234' });
*/
async getInvitationStatus(user = undefined, attributes = {}, relationships = {}) {
user = user || Client.instance.user;
validate('UserService.getInvitationStatus',
hasToken(),
isRequired({ user }),
isRequired({ invitation_token: attributes.invitation_token }));
const invitation = user.toInvitation(attributes, relationships);
validate('UserService.invite',
hasAttribute(invitation, 'email'),
hasAttribute(invitation, 'type'));
return super.insert(invitation, { url: `/v3/${user.type}/invitation/status`, cacheMode: 'networkOnly' });
}
/**
* Get Notifications
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} [user] - The user, must have id
* @param {!Number} [user.id] - The user id
* @param {!String} [user.type] - The user type
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Notification[]} - Promise object that returns a list of Notifications
*
* @example
* import { mobileUserService, User } from 'clinical6';
*
* // You can get entries using the `getNotifications` method
* mobileUserService.getNotifications().then(notification => console.log(notification));
*
* const user = new User({ id: 5, ... });
* mobileUserService.getNotifications(user).then(notification => console.log(notification));
*
* // Additionally, you can do the same thing using `User.getNotifications()`
* const user = new User({ id: 5, ... });
* user.getNotifications().then(notification => console.log(notification));
*/
async getNotifications(user = undefined, cacheMode = undefined) {
user = user || Client.instance.user;
validate('UserService.getNotifications',
hasToken(),
isRequired({ user }),
hasAttribute({ user }, 'id'),
hasAttribute({ user }, 'type'));
return super.getChildren(user, new Notification(), { cacheMode });
}
/**
* Get the profile of a user (or current user if null).
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} [user] - Parameters used to get information from server (such as id)
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Profile>} - Promise object that returns one User or a key:User
* hashtable
*
* @example
* import { mobileUserService, User } from 'clinical6';
*
* // You will be able to access these mobile users using the `getProfile` method.
* mobileUserService.getProfile().then(users => console.log(users));
*
* // Additionally, you can retrieve the profile for a specific mobile user with the `getProfile`
* // method, using the ID of the desired mobile user as a parameter.
* mobileUserService.getProfile(new User({id: 23})).then(users => console.log(users));
*
* // using the default `type`
* mobileUserService.getProfile({id: 23}).then(users => console.log(users));
*/
async getProfile(user = undefined, cacheMode = undefined) {
user = user || Client.instance.user;
const type = user.type || this.type;
validate('UserService.getProfile',
hasToken(),
isRequired({ type }),
hasAttribute({ user }, 'id'));
// const url = `/v3/${type}/${user.id}/profile`;
// return super.get({ id: user.id, type: 'profiles' }, { url, cacheMode })
// .then(profile => (user.profile = profile));
return super.getChildren(user, { type: 'profile' }, { cacheMode });
}
/**
* Gets all the custom attributes for the profile along with its values. This is here for
* ordering and labeling profile attributes.
*
* @throws {Clinical6Error} - If missing token
* @param {Boolean} [includeHidden=false] - Optional parameter which determines whether the
* response should include the hidden attributes
* (Encrypted Data).
* @param {Boolean} [includeValues=false] - Optional parameter which determiens whether the
* response should include the existing values of each
* attribute.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Get Profile Attributes</caption>
* import { userService } from 'clinical6';
* userService.getProfileAttributes();
*
* @example <caption>Success, both false</caption>
* {
* "status": "ok",
* "content": [
* {
* "id": 2,
* "api_key": "dummy_774",
* "name": "dummy_773",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:37.000Z",
* "updated_at": "2016-10-07T00:54:37.000Z",
* "is_visible": true,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 3,
* "api_key": "dummy_776",
* "name": "dummy_775",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:37.000Z",
* "updated_at": "2016-10-07T00:54:37.000Z",
* "is_visible": true,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 4,
* "api_key": "dummy_778",
* "name": "dummy_777",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:37.000Z",
* "updated_at": "2016-10-07T00:54:37.000Z",
* "is_visible": true,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* }
* ],
* "total_pages": 1
* }
*
* @example <caption>Success, include_hidden true</caption>
* {
* "status": "ok",
* "content": [
* {
* "id": 14,
* "api_key": "dummy_798",
* "name": "dummy_797",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.000Z",
* "is_visible": true,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 15,
* "api_key": "dummy_800",
* "name": "dummy_799",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.000Z",
* "is_visible": true,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 16,
* "api_key": "dummy_802",
* "name": "dummy_801",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.000Z",
* "is_visible": true,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 17,
* "api_key": "dummy_804",
* "name": "dummy_803",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.000Z",
* "is_visible": false,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 18,
* "api_key": "dummy_806",
* "name": "dummy_805",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.000Z",
* "is_visible": false,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* },
* {
* "id": 19,
* "api_key": "dummy_808",
* "name": "dummy_807",
* "attribute_type": "list",
* "options": null,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.000Z",
* "is_visible": false,
* "allow_multiple_values": null,
* "is_recurrent_capture": false
* }
* ],
* "total_pages": 1
* }
*/
@deprecate
getProfileAttributes(includeHidden = false, includeValues = false) {
validate('UserService.getProfileAttributes',
hasToken());
let httpQuery = '';
// Use param encoded into http query when including subcategories
httpQuery = includeHidden ? '?include_hidden=true' : '';
let prefix = '';
prefix = httpQuery === '' ? '?' : '&';
httpQuery = includeValues ? `${httpQuery}${prefix}include_values=true` : httpQuery;
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/profile_attributes${httpQuery}`).then(
(response) => {
resolve(response);
},
err => reject(err)
);
});
}
/**
* Returns the registration status of the user.
*
* @param {Object} attributes - The a mobile user or object
* @param {String} attributes.accountName - Account name to login.
* @param {String} attributes.email - Email to login.
* @param {String} [meta] - Meta data to be passed back
* @param {Boolean} [meta.passwordSet] - If the password is set
* @returns {Promise<String>} - A Promise with the status
*
* @example
* import { mobileUserService } from 'clinical6';
*
* mobileUserService.getRegistrationStatus({ account_name: 'asdf' });
* mobileUserService.getRegistrationStatus({ email: 'asdf@asdf.com'});
*/
async getRegistrationStatus(attributes, meta = {}) {
validate('UserService.getRegistrationStatus',
isRequired({ attributes }));
validate('UserService.getRegistrationStatus',
isValid((
(attributes.accountName || attributes.account_name)
|| (attributes.email && isEmail(attributes.email))
), 'attributes must be valid'));
const data = {
type: 'registration_validations',
attributes
};
return new Promise((resolve, reject) => {
super.insert(data, { url: `/v3/${this.type}/registration_validation`, cacheMode: 'networkOnly', meta })
.then(
(response) => {
if (meta && attributes instanceof User) {
attributes.meta = meta;
}
resolve(response);
},
err => reject(err)
);
});
}
/**
* Retrieves all the related mobile users.
*
* @throws {Clinical6Error} - If missing token
* @return {Promise<{followed_users: User[], followers: User[]}>} Promise object
* that returns success or failure
*
* @deprecated
*
* @example <caption>Get Related Mobile Users</caption>
* import { userService } from 'clinical6';
* userService.getRelatedMobileUsers().then(related => {
* console.log('followed_users', related.followed_users);
* console.log('followers', related.followers);
* });
*/
@deprecate
getRelatedMobileUsers() {
validate('UserService.getRelatedMobileUsers',
hasToken());
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/related_mobile_users`).then(
// response => resolve(response), // {followed_users: [], followers: []}
response => resolve({
followed_users: response.followed_users.map(obj => new User(obj)),
followers: response.followers.map(obj => new User(obj))
}),
err => reject(err)
);
});
}
/**
* Get Schedules for a user based on an id.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} [user] - User - if not defined, use logged in user
* @param {!Number} [user.id] - User ID must exist
* @param {!String} [user.type] - User type ('mobile_users' or 'users') must exist
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Schedule[] | Schedule>} - Promise object that returns a schedule or list of schedules
*
* @example
* import { userService } from 'clinical6';
* userService.getSchedules(); // current user
* userService.getSchedules({ id: 24, type: 'mobile_users' }); // user with id 24
*/
async getSchedules(user = undefined, cacheMode = undefined) {
user = user || Client.instance.user;
validate('UserService.getSchedules',
hasToken(),
isRequired({ user }),
hasAttribute({ user }, 'id'),
hasAttribute({ user }, 'type'));
validate('UserService.getSchedules',
isA({ userId: user.id }, 'number'));
const url = `/v3/${user.type}/${user.id}/scheduler/personalized_rule_schedules`;
return super.get({ type: 'scheduler__personalized_rule_schedules' }, { url, cacheMode });
}
/**
* Get the session of the logged in user.
*
* @throws {Clinical6Error} - If token is not a string
* @param {String} [token] - Access token
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise} - Promise object that returns success or failure
*
* @example
* import { mobileUserService } from 'clinical6';
* mobileUserService.getSession();
*/
async getSession(token = undefined, cacheMode) {
validate('UserService.getSession',
isA({ token }, 'string'));
let httpQuery = ``;
if (token) {
httpQuery = `?access_token=${token}`;
}
const url = `/v3/${this.type}/sessions${httpQuery}`;
return super.get({ }, { url, cacheMode });
}
/**
* Get User Sites
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} [user] - The user, must have id and type
* @param {!Number} [user.id] - The user id
* @param {!String} [user.type] - User type ('mobile_users' or 'users') must exist
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Site[] | Site>} - Promise object that returns one object or hashmap
*
* @example
* import { mobileUserService, User } from 'clinical6';
*
* // You can get entries using the `getSites` method
* mobileUserService.getSites({ id: 5, type: 'mobile_users' }).then(sites => console.log(sites));
*
* mobileUserService.getSites({ id: 5, type: 'mobile_users' }, filter).then(sites => console.log(sites));
*
* // Additionally, you can do the same thing using `User.getSites()`
* const user = new User({ id: 5, type: 'mobile_user', ... });
* user.getSites().then(sites => console.log(sites));
* user.getSites(filter).then(sites => console.log(sites));
*/
async getSites(user = undefined, cacheMode = undefined) {
user = user || Client.instance.user;
validate('UserService.getSites',
hasToken(),
isRequired({ user }));
validate('UserService.getSites',
hasAttribute({ user }, 'id'),
hasAttribute({ user }, 'type'));
const url = `/v3/${user.type}/${user.id}/sites`;
return super.get({}, { url, cacheMode });
}
/**
* @override
* Call a POST request on the main obj.type expecting JSON API information.
*
* @param {User} user - User object in which to insert
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<User>} - Inserted user
*
* @example
* import { User, mobileUserService } from 'clinical6';
* const user = new User({...});
*
* // you can insert a user using the `insert` method.
* mobileUserService.insert(mobileUser).then(user => console.log(user));
*
* // you could also just call `save` on the user if it doesn't have an id, which will also
* // invoke the `insert` method
* user.save();
*/
async insert(user, cacheMode = undefined) {
validate('UserService.insert',
hasToken(),
isRequired({ user }));
return super.insert(user, cacheMode);
}
/**
* Inserts a new value for the given profile attribute.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {String} key - The key value for the profile.
* @param {String|Number} val - The value of the attribute.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Create Profile Attribute</caption>
* import { userService } from 'clinical6';
* userService.insertProfileAttribute('make', 'Toyota');
*
* @example <caption>Success Response</caption>
* {
* "status": "ok",
* "id": 6,
* "content": {
* "id": 6,
* "value": "new_value",
* "profile_attribute_id": 23,
* "profile_id": 150,
* "created_at": "2016-10-07T00:54:38.460Z",
* "updated_at": "2016-10-07T00:54:38.460Z",
* "recurrent_capture_id": null,
* "recurrent_capture_type": null
* }
* }
*
* @example <caption>Failure, Exists</caption>
* {
* "error_detail": "This attribute has an existing value, use update method to overwrite it'",
* "friendly_error": null,
* "internal_code": 50367,
* "more_info": "",
* "status": "fail",
* "message": null
* }
*/
@deprecate
async insertProfileAttribute(key, val) {
validate('UserService.insertProfileAttribute',
hasToken(),
isRequired({ key }),
isRequired({ val }));
const data = {
api_key: Client.instance.authToken
};
data[key] = val;
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/profile_attributes`, 'post', data).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Invite a user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {User} user - The user in which to invite (required)
* @param {!String} user.email - The email address of the invited user. (required)
* @param {Profile} user.profile - The user's profile that contains information like first name, last name, and phone
* @param {String} [user.role] - The user's role
* @param {Object} [attributes] - A list of optional attributes not found in user or profile. This can contain any key/value.
* @param {String} [attributes.invitation_token] - The token used for accepting an invitation
* @param {String} [attributes.password] - A password chosen for the user
* @param {String} [attributes.relationship] - A relationship of the user to patient ('father', 'mother', 'uncle', 'aunt', etc)
* @param {Object} [relationships] - A list of optional relationships not found in user or profile. This can contain any key/value.
* @param {Site} [relationships.site] - The user's site
* @param {Role} [relationships.user_role] - The user's role (especially used if not defined in the user object)
* @param {Language} [relationships.language] - The user's language (especially used if not defined in the user profile)
* @return {Promise<User>} - Promise object that returns success or failure
*
* @example <caption>Invite Simple</caption>
* import { userService, User } from 'clinical6';
* const user = new User({ email: 'john.brown@parallel6.com' });
* user.profile.firstName = 'John';
* user.profile.lastName = 'Brown';
* userService.invite(user);
*
* @example <caption>Invite With Site, Role, Language, Token and Password</caption>
* import { userService, Site, Role, Language, User } from 'clinical6';
* const user = new User({ email: 'john.brown@parallel6.com' });
* user.profile.firstName = 'John';
* user.profile.lastName = 'Brown';
* userService.invite(user, {
* invitation_token: 'asdf12124',
* password: 'mypassword@1'
* }, {
* followed: new User({ id: 283 }),
* language: new Language({ id: 43 }),
* site: new Site({ id: 5 }),
* user_role: new Role({ id: 23 })
* });
*/
async invite(user, attributes = {}, relationships = {}) {
validate('UserService.invite',
hasToken(),
isRequired({ user }));
const invitation = user.toInvitation(attributes, relationships);
validate('UserService.invite',
hasAttribute(invitation, 'email'),
hasAttribute(invitation, 'type'));
return super.insert(invitation, { url: `/v3/${user.type}/invitation`, cacheMode: 'networkOnly' });
}
/**
* Register a device for guest, a username, or email
*
* @param {Object} attributes - User object in which to insert
* @param {String} [attributes.guest] - Guest must be true or false (null)
* @param {String} [attributes.account_name] - Either use account name or email
* @param {String} [attributes.email] - Either use account name or email
* @param {String} [attributes.password] - Password must exist if guest is false
* @param {Device} [device] - Device with id to register
* @return {Promise<User>} - Registered Mobile User
*
* @example
* import { mobileUserService } from 'clinical6';
* const user = new User({...});
*
* // you can register a guest using the `register` method.
* mobileUserService.register({ guest: true }, device)
* .then(user => console.log(user));
*
* // Register using an account name
* mobileUserService.register({ account_name: 'abc', password: 'asdf' }, device)
* .then(user => console.log(user));
*
* // Register using an email address
* mobileUserService.register({ email: 'test@test.com', password: 'asdf' }, device)
* .then(user => console.log(user));
*/
async register(attributes, device) {
validate('UserService.register',
isRequired({ attributes, device }),
hasAttribute({ device }, 'id'));
validate('UserService.register',
isValid((attributes.guest
|| ((attributes.account_name || attributes.email)
&& attributes.password)), 'attributes must be valid'));
const data = {
type: 'mobile_users',
attributes,
relationships: {
devices: {
data: {
id: device.id,
type: 'devices'
}
}
}
};
return super.insert(data, { url: `/v3/${data.type}/registrations`, cacheMode: 'networkOnly' })
.then(user => this._loginGetUser(user));
}
/**
* Remove Awarded Badge
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} awardedBadge - The mobile user, must have id
* @param {!Object} awardedBadge.attributes - Hashmap to add for award badge
* @param {!User} awardedBadge.user - The mobile user, must have id
* @param {!Number} awardedBadge.user.id - The mobile user id
* @param {!Badge} awardedBadge.badge - The mobile user, must have id
* @param {!Number} awardedBadge.badge.id - The mobile user id
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<AwardedBadge>} - Promise object that returns one object or hashmap
*
* @example
* import { userService, AwardedBadge, Badge, User } from 'clinical6';
* const data = new AwardedBadge({ updatedAt: '2018-06-02' });
* data.awardee = new User({ id: 5, ... });
* data.badge = new Badge({ id: 5, ... });
*
* // You can add an awarded badge using the `removeAwardedBadge` method
* userService.removeAwardedBadge(data).then(data => console.log(data));
*
* // Additionally, you can do the same thing using `User.removeAwardedBadge()`
* user.removeAwardedBadge(badge).then(data => console.log(data));
*/
async removeAwardedBadge(awardedBadge, cacheMode = undefined) {
const vTitle = 'UserService.removeAwardedBadge';
validate(vTitle, hasToken(), isRequired({ awardedBadge }));
validate(vTitle, hasAttribute({ awardedBadge }, 'awardee'));
validate(vTitle, hasAttribute({ awardee: awardedBadge.awardee, awardedBadge }, 'id'));
const url = `/v3/${this.type}/${awardedBadge.awardee.id}/awarded_badges/${awardedBadge.id}`;
return super.delete(awardedBadge, { url, cacheMode });
}
/**
* Starts the reset password process and sends an email to the user with instructions.
*
* @throws {Clinical6Error} - If missing required parameters
* @param {Object} attributes - The authorization object
* @param {String} attributes.account_name - Account name to login.
* @param {String} attributes.email - Email to login.
* @return {Promise} - Promise object that returns success or failure
*
* @example <caption>Mobile User</caption>
* import { mobileUserService } from 'clinical6';
* mobileUserService.requestPasswordReset({ email: 'test@test.com' });
* mobileUserService.requestPasswordReset({ account_name: 'jsmith' });
*
* @example <caption>User</caption>
* import { userService } from 'clinical6';
* userService.requestPasswordReset({ email: 'test@test.com' });
*/
async requestPasswordReset(attributes) {
validate('UserService.requestPasswordReset',
isRequired({ attributes }));
validate('UserService.requestPasswordReset',
isValid((attributes.account_name || (attributes.email && isEmail(attributes.email))), 'attributes must be valid'),
isA({ account_name: attributes.account_name, email: attributes.email }, 'string'));
const data = { type: 'password_resets', attributes };
const url = `/v3/${this.type}/password`;
return super.insert(data, { url, cacheMode: `networkOnly` });
}
/**
* Resends an invite to a related mobile user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Number} id - The id of the related mobile user to resend the invite to.
* @param {any} [options] - Additional options submitted to the server
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Resend Invite</caption>
* import { userService } from 'clinical6';
* userService.resendInvite(23);
*
* @example <caption>Resend Invite</caption>
* import { userService } from 'clinical6';
* userService.resendInvite(23, {});
*
* @deprecated
*/
@deprecate
resendInvite(id, options = {}) {
validate('UserService.resendInvite',
hasToken(),
isRequired({ id }),
isA({ id }, 'number'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/related_mobile_users/${id}/reinvite`, 'post', options).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Allows users to complete the Password reset flow.
*
* @throws {Clinical6Error} - If missing required parameters
* @param {Object} attributes - The authorization object
* @param {!String} attributes.reset_password_token - Token from request password reset email
* @param {!String} attributes.password - New password to reset to.
* @param {String} [attributes.email] - Email to login.
* @param {Device} [device] - Reset with this device (Default to saved device).
* @param {!Number} device.id - Reset with this device (Default to saved device).
* @return {Promise<User>} - Promise object that returns the user if successful
*
* @example
* import { mobileUserService } from 'clinical6';
* mobileUserService.resetPassword({ reset_password_token: 'a820348103828f8as0d8f', password: 'password1234'});
*/
async resetPassword(attributes, device = undefined) {
if (Client.instance.device.id && this.type === 'mobile_users') {
device = device || Client.instance.device;
}
validate('UserService.resetPassword',
isRequired({ attributes }),
hasAttribute({ attributes }, 'reset_password_token'),
hasAttribute({ attributes }, 'password'));
validate('UserService.resetPassword',
isA({ reset_password_token: attributes.reset_password_token, password: attributes.password }, 'string'));
if (attributes.email) {
validate('UserService.resetPassword',
isA({ email: attributes.email }, 'string'),
isValid(isEmail(attributes.email), 'email must be a valid email'));
}
const data = { type: 'password_resets', attributes };
if (device) {
validate('UserService.resetPassword',
hasAttribute({ device }, 'id'));
data.relationships = {
devices: {
data: {
id: device.id,
type: 'devices'
}
}
};
}
const url = `/v3/${this.type}/password`;
return super.update(data, { url, cacheMode: `networkOnly` }).then((response) => {
// Attempt to log in if a new authToken is present in the device information
if (response && response.id && response.type === 'mobile_users') {
Client.instance.user = response;
if (response.devices && device) {
const newDevice = response.devices.find(d => (d.id === device.id));
if (newDevice.accessToken) {
Client.instance.authToken = newDevice.accessToken;
Client.instance.device.accessToken = newDevice.accessToken;
}
}
}
return response;
});
}
/**
* Sends user's account name via email. This method does not require an access token.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!String} newPassword - New password for the account.
* @param {!String} newPasswordConf - Confirmation of the new password.
* @param {!String} oldPassword - New password for the account.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Reset Password Simple</caption>
* import { userService } from 'clinical6';
* userService.resetPasswordSimple('test', 'test', 'test2');
*
* @example <caption>Failure, Mismatched Passwords</caption>
* {
* "error_detail": "Review the new_password and new_password_confirmation parameters",
* "friendly_error": "Password and confirmation do not match",
* "internal_code": 50362,
* "more_info": "",
* "status": "fail",
* "message": "Password and confirmation do not match"
* }
*
* @deprecated
*/
@deprecate
resetPasswordSimple(newPassword, newPasswordConf, oldPassword) {
validate('UserService.resetPasswordSimple',
hasToken(),
isRequired({
newPassword,
newPasswordConf,
oldPassword
}));
const data = {
old_password: oldPassword,
new_password: newPassword,
new_password_confirmation: newPasswordConf,
};
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/reset_password`, 'put', data).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* This method revokes the consent for a user.
*
* @throws {Clinical6Error} - If missing required parameters
*
* @param {User} user - User object in which to revoke consent for
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise} - Message
*
*/
async revokeConsent(user, cacheMode = undefined) {
validate('UserService.revokeConsent',
hasToken(),
isRequired({ user }),
hasAttribute({ user }, 'id'),
hasAttribute({ user }, 'type'));
return super.delete(user, { url: `/v3/${user.type}/${user.id}/consent/consent`, cacheMode });
}
/**
* Sends a confirmation email to an email address
*
* @throws {Clinical6Error} - If missing required parameters
* @param {String} email - Email address to send the reset password email to.
* @return {Promise} - Promise object that returns success or failure
*
* @example
* import { mobileUserService } from 'clinical6';
* mobileUserService.sendConfirmation('test@test.com');
*/
async sendConfirmation(email) {
validate('UserService.sendConfirmation',
isRequired({ email }),
isA({ email }, 'string'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/v3/${this.type}/confirmation`, 'post', {
data: {
type: 'confirmations',
attributes: { email }
}
}).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Sign in user with account name.
*
* @throws {Clinical6Error} - If missing required parameters
* @param {Object} attributes - The authorization object
* @param {String} attributes.account_name - Account name to login.
* @param {String} attributes.email - Email to login.
* @param {String} attributes.password - Login with this password.
* @param {Device} [device] - Login with this device (Default to saved device).
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Session>} - Promise returning a session with a user and authenticationToken and
* logging in future calls
*
* @example
* import { mobileUserService } from 'clinical6';
* mobileUserService.signIn({ account_name: 'jsmith', password: 'password1234'});
* mobileUserService.signIn({ email: 'jsmith@gmail.com', password: 'password1234'});
*/
async signIn(attributes, device = Client.instance.device, cacheMode = 'networkOnly') {
validate('UserService.signIn',
isRequired({ attributes }));
const data = { type: 'sessions', attributes };
Client.instance.user = new User();
Client.instance.user.type = this.type;
switch (this.type) {
case 'mobile_users': {
validate('UserService.signIn',
isValid((attributes.guest
|| ((attributes.account_name || (attributes.email && isEmail(attributes.email)))
&& attributes.password)), 'attributes must be valid'));
validate('UserService.signIn',
isRequired({ device }),
hasAttribute({ device }, 'id'));
data.relationships = {
devices: {
data: {
id: (device && device.id) ? device.id : Client.instance.device.id,
type: 'devices'
}
}
};
break;
}
case 'users': {
validate('UserService.signIn',
isValid((attributes.guest
|| ((attributes.username || (attributes.email && isEmail(attributes.email)))
&& attributes.password)), 'attributes must be valid'));
break;
}
default: {
break;
}
}
Client.instance.authToken = undefined;
const url = `/v3/${this.type}/sessions`;
return super.insert(data, { url, cacheMode }).then((response => this._loginGetSession(response, device)));
}
/**
* Sets the pin value for an invited user. This method will set the initial value of a PIN. In
* some workflows it is also possible to specify account_name if it is pre-provisioned.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} json - The JSON value with the attributes to provide for set
* pin.
* @param {!Number} json.pin - The PIN
* @param {!Number} json.pin_confirmation - The PIN Confirmation
* @param {String} [json.account_name] - The Account Name
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Set Pin</caption>
* import { userService } from 'clinical6';
* userService.setPin({
* pin: 123456,
* pin_confirmation: 123456
* });
*
* @example <caption>Set Pin with Account Name</caption>
* import { userService } from 'clinical6';
* userService.setPin({
* pin: 123456,
* pin_confirmation: 123456,
* account_name
* });
*
* @deprecated
*/
@deprecate
setPin(json) {
validate('UserService.setPin',
hasToken(),
isRequired({ json }));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/set_pin`, 'put', json).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* Detaches the device from the mobile_user. If the current mobile_user is a guest, his
* information will be destroyed. After this method is executed the device won't have any
* access_token associated and won't be able to make other requests until it signs in.
* Destroys a user's session
*
* This method does not require access token.
*
* @return {Promise} Promise object that returns success or failure
*/
async signOut() {
const url = `/v3/${this.type}/sessions`;
const cacheMode = 'networkOnly';
let _return = Promise.resolve('');
try {
_return = await super.delete(new Session({ id: 1 }), { url, cacheMode });
} catch (e) {
_return = Promise.resolve(e.message);
} finally {
Client.instance.reset();
}
return _return;
}
/**
* Sign up a user with a user profile. The {@link Client#device} fields `uuid`, `technology`,
* `version`, and `pushId` are required to exist sign in to occur
*
* In order to sign up for a new user account, profile information and credentials can be used to
* execute the `signUp` method:
*
* @throws {Clinical6Error} - If missing required parameters and device info
* @param {String} username - The username associated with the user account
* @param {String} password - The password for the user
* @param {Object} profile - The user profile to register for the user
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Sign Up</caption>
* import { UserService } from 'clinical6';
*
* const userService = new UserService();
* userService.signUp(username, pw, profile).then(function(data){
* // User is created and an authtotken is passed back
* }, function(err){
* // error
* });
*
* @example <caption>Success Response</caption>
* {
* "status": "ok",
* "auth_token": "93a46e8f956ea7be0cd6a087629e8550",
* "verification_status": "unverified",
* "encryption_key": "87206e2605a4bcfeb213e3c702c55190"
* }
*
* @deprecated
*/
@deprecate
signUp(username = '', password = '', profile) {
const { device } = Client.instance;
validate('UserService.signUp',
isRequired({
udid: device.udid,
technology: device.technology,
version: device.appVersion,
username,
password,
profile
}),
isA({ username }, 'string'),
isA({ password }, 'string'));
// Chain our response to extract authToken
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/${this.type}/sign_up`, 'post', {
mobile_user: {
account_name: username,
password,
},
device: {
udid: device.udid,
technology: device.technology,
push_id: device.pushId,
app_version: device.appVersion,
},
profile,
}).then(
(response) => {
// ensure we have an auth token
if (response.auth_token) {
Client.instance.authToken = response.auth_token;
resolve(response);
}
// fail when we don't
reject(response);
},
err => reject(err)
);
});
}
/**
* Unlocks a user
*
* @throws {Clinical6Error} - If missing required parameters
* @param {String} email - Email address of the user to unlock.
* @param {String} unlockToken - Unlock token to unlock the user.
* @return {Promise} - Promise object that returns success or failure
*
* @example
* import { mobileUserService } from 'clinical6';
* mobileUserService.unlockUser('test@test.com', 'fakeToken123');
*/
async unlockUser(email, unlockToken) {
validate('UserService.unlockUser',
isRequired({ email }),
isRequired({ unlockToken }),
isA({ email }, 'string'),
isA({ unlockToken }, 'string'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/v3/${this.type}/unlock`, 'patch', {
data: {
type: 'mobile_user__unlocks',
attributes: { email, unlock_token: unlockToken }
}
}).then(
response => resolve(response),
err => reject(err)
);
});
}
/**
* @override
* Call a PATCH request on the main obj.type expecting JSON API information.
*
* @param {User} user - User object in which to update
* @param {String} [options] - Modify the nature of the call and response
* @param {String} [options.cacheMode] - Override the caching method for this call
* @param {Object} [options.meta] - Pass and retrieve through reference meta data
* @return {Promise<User>} - Updated user
*
* @example
* import { User, mobileUserService } from 'clinical6';
* const user = new User({...});
*
* // you can update a user using the `update` method.
* mobileUserService.update(user).then(user => console.log(user));
*
* // you could also just call `save` on the user if it has an id, which will also
* // invoke the `update` method
* user.save();
*/
update(user = undefined, options = {}) {
user = user || Client.instance.user;
validate('UserService.update',
hasToken(),
isRequired({ user }));
return super.update(user, options);
}
/**
* Update Awarded Badge
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} awardedBadge - The mobile user, must have id
* @param {!Object} awardedBadge.attributes - Hashmap to add for award badge
* @param {!User} awardedBadge.user - The mobile user, must have id
* @param {!Number} awardedBadge.user.id - The mobile user id
* @param {!Badge} awardedBadge.badge - The mobile user, must have id
* @param {!Number} awardedBadge.badge.id - The mobile user id
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<AwardedBadge>} - Promise object that returns one object or hashmap
*
* @example
* import { userService, AwardedBadge, Badge, User } from 'clinical6';
* const data = new AwardedBadge({ updatedAt: '2018-06-02' });
* data.awardee = new User({ id: 5, ... });
* data.badge = new Badge({ id: 5, ... });
*
* // You can update an awarded badge using the `updateAwardedBadge` method
* userService.updateAwardedBadge(data).then(awardedBadge => console.log(awardedBadge));
*
* // Additionally, you can do the same thing using `User.updateAwardedBadge()`
* user.updateAwardedBadge(data).then(awardedBadge => console.log(awardedBadge));
*/
async updateAwardedBadge(awardedBadge, cacheMode = undefined) {
validate('UserService.updateAwardedBadge',
hasToken(),
isRequired({ awardedBadge }));
awardedBadge = (awardedBadge instanceof AwardedBadge) ? awardedBadge : new AwardedBadge(awardedBadge);
if (!awardedBadge.awardee) {
awardedBadge.awardee = Client.instance.user;
}
validate('UserService.updateAwardedBadge',
hasAttribute({ awardedBadge }, 'awardee'),
hasAttribute({ awardedBadge }, 'badge'));
validate('UserService.updateAwardedBadge',
hasAttribute({
awardedBadge,
awardee: awardedBadge.awardee,
badge: awardedBadge.badge,
}, 'id'));
const url = `/v3/${this.type}/${awardedBadge.awardee.id}/awarded_badges/${awardedBadge.id}`;
return super.update(awardedBadge.toJSON(), { url, cacheMode });
}
/**
* Update Profile
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} param - The user or profile
* @param {String} [options] - Modify the nature of the call and response
* @param {String} [options.cacheMode] - Override the caching method for this call
* @param {Object} [options.meta] - Pass and retrieve through reference meta data
* @return {Promise<Profile>} - Promise object that returns one object or hashmap
*
* @example
* import { mobileUserService, Profile, User } from 'clinical6';
*
* // You can update a profile using the `saveProfile` method
*
* // Update profile using the Current User, `clinical6.user`
* mobileUserService.saveProfile().then(profile => console.log(profile));
*
* // Update profile using a User object
* const user1 = new User({ ... }); // user must have a related profile, which it does by default
* mobileUserService.saveProfile(user1).then(profile => console.log(profile));
*
* // Update profile using a Profile object
* const profile = new Profile({ ... }); // profile must have a related user
* mobileUserService.saveProfile(profile).then(profile => console.log(profile));
*
* // Update profile using a Profile object and apply meta data
* const profile = new Profile({ ... }); // profile must have a related user
* mobileUserService.saveProfile(profile, { meta: { something: 23 }}).then(profile => console.log(profile));
*
* // Updating just one attribute on the logged in user
* mobileUserService.saveProfile({
* profile: { zipCode: 51038 }
* }).then(profile => console.log(profile));
*
* // Updating just one attribute on a different user
* const user2 = new User({ ... });
* mobileUserService.saveProfile({
* profile: { zipCode: 51038 },
* user: user2
* }).then(profile => console.log(profile));
*
* // Additionally, you can do the same thing using `User.saveProfile()`
* const user3 = new User({ ... }); // user must have a related profile, which it does by default
* user3.saveProfile().then(profile => console.log(profile));
*/
async saveProfile(param = undefined, options = {}) {
let user = {};
let profile = {};
if (!param) {
({ user } = Client.instance);
({ profile } = user);
} else if (param instanceof User) {
user = param;
({ profile } = user);
} else if (param instanceof Profile) {
({ user } = param);
profile = param;
} else if (param.profile) {
user = param.user || Client.instance.user;
profile = {
type: 'profiles',
attributes: param.profile
};
}
const type = (user && user.type) ? user.type : this.type;
validate('UserService.saveProfile',
hasToken(),
isRequired({ user, profile, type }),
hasAttribute({ user }, 'id'));
const url = `/v3/${type}/${user.id}/profile`;
if (profile.id) {
return super.update(profile, { url, cacheMode: options.cacheMode, meta: options.meta }).then(p => (user.profile = p));
}
return super.insert(profile, { url, cacheMode: options.cacheMode, meta: options.meta }).then(p => (user.profile = p));
}
/**
* Update Schedule
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {!Object} schedule - The mobile user, must have id
* @param {!Object} schedule.attributes - Hashmap to add for a schedule
* @param {!User} [schedule.mobileUser] - The mobile user, must have id
* @param {!Number} [schedule.mobileUser.id] - The mobile user id
* @param {String} [cacheMode] - Override the caching method for this call
* @return {Promise<Schedule>} - Promise object that returns one object or hashmap
*
* @example
* import { userService, Schedule } from 'clinical6';
* const data = new Schedule({ updatedAt: '2018-06-02' });
*
* // You can update a schedule using the `updateSchedule` method
* userService.updateSchedule(data).then(schedule => console.log(schedule));
*
* // Additionally, you can do the same thing using `User.updateSchedule()`
* user.updateSchedule(data).then(schedule => console.log(schedule));
*/
async updateSchedule(schedule, cacheMode = undefined) {
validate('UserService.updateSchedule',
hasToken(),
isRequired({ schedule }),
hasAttribute({ schedule }, 'id'));
const user = schedule.mobileUser || Client.instance.user;
validate('UserService.updateSchedule',
hasToken(),
isRequired({ schedule, user }),
hasAttribute({ schedule, user }, 'id'));
const url = `/v3/${user.type}/${user.id}/scheduler/personalized_rule_schedules/${schedule.id}`;
return super.update(schedule, { url, cacheMode });
}
/**
* Updates the profile information for the current, authenticated user.
*
* @throws {Clinical6Error} - If missing token or missing required parameters
* @param {Object} profile - Profile object that is used to update an existing profile.
* @param {Number} id - The profile ID value of the profile to update.
* @return {Promise} - Promise object that returns success or failure
*
* @deprecated
*
* @example <caption>Update Profile Attribute</caption>
* import { userService } from 'clinical6';
* userService.updateProfileAttribute({
* "email": "test@test.com"
* }, 23);
*
* @example <caption>Success Response</caption>
* {
* "status": "ok",
* "id": 2,
* "content": {
* "id": 2,
* "value": "new_value",
* "profile_attribute_id": 20,
* "profile_id": 146,
* "created_at": "2016-10-07T00:54:38.000Z",
* "updated_at": "2016-10-07T00:54:38.175Z",
* "recurrent_capture_id": null,
* "recurrent_capture_type": null
* }
* }
*
* @example <caption>Failure, Not Found</caption>
* {
* "error_detail": {
* "ProfileValue": [
* "not_found"
* ]
* },
* "friendly_error": "Profile value not found using the value:-1",
* "internal_code": 50364,
* "more_info": "",
* "status": "fail",
* "message": "Profile value not found using the value:-1"
* }
*/
@deprecate
async updateProfileAttribute(profile, id) {
validate('UserService.updateProfileAttribute',
hasToken(),
isRequired({
profile,
id
}),
isA({ id }, 'number'));
return new Promise((resolve, reject) => {
Client.instance.fetch(`/api/profile_attributes/${id}`, 'put', profile).then(
response => resolve(response),
err => reject(err)
);
});
}
}
export default UserService;