/**
* AngularJS Service for getting/storing user data and managing logins/sessions.
*
* @module userService
* @see https://docs.angularjs.org/api/ng/type/angular.Module#service
*
* @requires authService
* @requires domainService
* @requires gameDataService
* @requires gameTrackingService
* @requires instructorService
* @requires URLParamsService
* @requires utilityService
*/
angular.module('tgaApp').service('userService', [
'$log',
'$http',
'$location',
'$window',
'authService',
'localStorageService',
'envService',
'domainService',
'gameDataService',
'gameTrackingService',
'utilityService',
'instructorService',
'URLParamsService',
'scormService',
function userService(
$log,
$http,
$location,
$window,
authService,
localStorageService,
envService,
domainService,
gameDataService,
gameTrackingService,
utilityService,
instructorService,
URLParamsService,
scormService,
) {
const serverAddress = envService.read('serverAddress');
const storageKey = 'player';
/**
* @private
* @member {Object} user
*/
let user = null;
let session_limit_reached = false;
/**
*
* @returns {boolean} to let game know that the user has reached their session limit.
*/
this.checkSessionLimitReached = () => session_limit_reached;
/**
* Check authentication using the {@link module:authService}.
*
* @returns {boolean} Whether or not the user is authenticated.
*/
this.isLoggedIn = () => authService.getAuthentication().isAuth;
/**
* Get the user via the API.
* @see https://api.thetrainingarcade.com/documentation/#api-Player-GetPlayer
*
* @returns {Promise} Promise object, either:
* - Resolved with the HTTP response data.
* - Rejected with the full HTTP response object or an error.
*/
this.getUser = () => {
const { game_id } = gameDataService.get();
return $http({
method: 'GET',
url: `${serverAddress}player?game_id=${game_id}`,
}).then(
(response) => response.data,
(error) => Promise.reject(error),
);
};
/**
* Get the current user data from localStorage.
*
* @returns {Object}
*/
this.getUserData = () => {
if (!user) {
user = localStorageService.get(storageKey);
}
return user ?? {};
};
/**
* Set the current user data in localStorage.
*
* @param {Object} newUser
*/
this.setUser = (newUser) => {
user = newUser;
localStorageService.set(storageKey, user);
};
/**
* Update a user via the API.
* @see https://api.thetrainingarcade.com/documentation/#api-Player-UpdatePlayer
*
* @param {Object} data Request message data
* @returns {Promise} Promise object, either:
* - Resolved with the HTTP response data.
* - Rejected with the full HTTP response object or an error.
*/
this.updateUser = (data) => $http({
method: 'PUT',
url: `${serverAddress}player`,
data,
}).then(
(response) => {
this.setUser(response.data);
return response.data;
},
(error) => Promise.reject(error),
);
/**
* Clear authentication and stored local user data.
*/
this.logout = () => {
authService.clearAuthentication();
this.setUser(null);
};
/**
* Start a session via the API.
* @see https://api.thetrainingarcade.com/documentation/#api-Player-PostSession
*
* @param {Object} [data] Request message data.
* @returns {Promise} Promise object, either:
* - Resolved with the HTTP response data.
* - Rejected with the full HTTP response object or an error.
*/
this.startSession = (data) => {
const { game_id, language_id } = gameDataService.get();
const {
hub_id, course_id, challenge_id, token, session_group_id, segment, team_id,
} = URLParamsService.getUserParams();
const sendData = {
...data,
game_id,
language_id,
hub_id,
course_id,
challenge_id,
team_id,
token,
segment,
session_group_id: instructorService.getSessionGroupId() || session_group_id,
is_scorm: scormService.isConnected(),
};
gameTrackingService.reset();
return $http({
method: 'POST',
url: `${serverAddress}player/session`,
data: sendData,
}).then(
(response) => {
if (sendData.hub_id) {
$window.parent.postMessage({ session_id: response.data.session_id }, '*');
}
session_limit_reached = response.data.session_limit_reached ?? false;
return response.data;
},
(error) => Promise.reject(error),
);
};
// function to clear the scores for a SCORM player if they are restarting the game in the LMS
const clearSCORMScoresOrNot = () => {
if (scormService.isConnected() && scormService.provideEntryStatus() === 'new') {
$log.debug('clearSCORMScoresOrNot: clearing prior scores for SCORM');
return this.clearScores({
slug: gameDataService.get().slug,
});
}
return new Promise((res) => {
res();
});
};
/**
* Internal method used to handle a successful API response of login and
* registration requests. It attempts to do the following:
* - Set authentication data in {@link module:authService}
* - Get the currently logged in user object (via {@link module:userService.getUser})
* - Set the currently stored user object (via {@link module:userService.SetUser})
* - Start a session (via {@link module:userService.startSession})
*
* @private
* @method authDataApply
* @param {Object} data Request message data.
* @param {Object} response HTTP response object.
* @returns {Promise} Promise object, either:
* - Resolved with the current user Object.
* - Rejected with the full HTTP response object or an error.
*/
const authDataApply = (data, response, isSSOAuth) => {
const authentication = authService.clearAuthentication();
authentication.isAuth = true;
authentication.username = data.username;
authentication.token = response.data.key;
authentication.userId = response.data.id;
authService.setAuthentication(authentication, true);
return this.getUser().then((currentUser) => {
this.setUser(currentUser);
if (!isSSOAuth) {
clearSCORMScoresOrNot().then(() => {
$log.debug('registered, starting session');
this.startSession();
});
}
return currentUser;
});
};
/**
* Login as a user.
* Calls {@link module:userService~authDataApply} on success.
*
* @see https://api.thetrainingarcade.com/documentation/#api-Player-LoginPlayer
*
* @param {Object} data Request message data.
* @param {string} [data.username]
* @param {string} [data.email]
* @param {string} [data.password]
* @param {string} [data.token]
* @param {string} [data.game_id]
* @returns {Promise} Promise object, either:
* - Resolved with the current user Object.
* - Rejected with the full HTTP response object or an error.
*/
this.login = (data) => {
const sendData = { ...data };
return $http({
method: 'POST',
url: `${serverAddress}player/login`,
data: sendData,
}).then(
(response) => authDataApply(data, response, true),
(error) => {
this.logout();
return Promise.reject(error);
},
);
};
/**
* Login as a user.
* Calls {@link module:userService~authDataApply} on success.
*
* @see https://api.thetrainingarcade.com/documentation/#api-Player-RegisterPlayer
*
* @param {Object} data Request message data.
* @returns {Promise} Promise object, either:
* - Resolved with the current user Object.
* - Rejected with the full HTTP response object or an error.
*/
this.register = (data) => {
const sendData = { ...data };
sendData.game_id = gameDataService.get().game_id;
if (angular.isUndefined(sendData.username)) {
sendData.username = utilityService.generateUUID();
}
return $http({
method: 'POST',
url: `${serverAddress}player/register`,
data: sendData,
}).then(
(response) => authDataApply(sendData, response),
(error) => Promise.reject(error),
);
};
/**
* Check authorization via the API.
* @see https://api.thetrainingarcade.com/documentation/#api-Player-AuthPost
*
* @param {Object} data Request message data.
* @returns {Promise} Promise object, either:
* - Resolved with the response data's `authorization` property.
* - Rejected with the full HTTP response object or an error.
*/
this.checkAuthorizedUser = (data) => {
const sendData = { ...data };
sendData.domain = domainService.domain;
return $http({
method: 'POST',
url: `${serverAddress}player/auth`,
data: sendData,
}).then(
(response) => response.data.authorization,
(error) => Promise.reject(error),
);
};
/**
* Clear player scores via the API.
* @see https://api.thetrainingarcade.com/documentation/#api-Player-PostClearScores
*
* @param {Object} data Request message data.
* @returns {Promise} Promise object, either:
* - Resolved with the ull HTTP response object.
* - Rejected with the full HTTP response object or an error.
*/
this.clearScores = (data) => $http({
method: 'POST',
url: `${serverAddress}player/clear_scores`,
data,
}).then(
(response) => {
$log.debug('scores cleared');
return response;
},
(error) => {
$log.debug('error clearing scores');
return Promise.reject(error);
},
);
},
]);