import { push as historyPush } from 'react-router-redux';
import createAction from 'redux-actions/lib/createAction';
import { createPath } from '../../util/routeUtils';
import { apiPost, apiGet, apiPut } from './apiActions/apiRequest';
import {
GATEWAY_ACCOUNT_MIGRATION,
GATEWAY_MIGRATION_AUTH,
AUTHENTICATION_MANAGER,
} from '../../data/Injectables';
import SortType from '../../data/enum/SortType';
import Pages, { PARAM_WAIT_FOR_MIGRATION_CREATION } from '../../data/enum/Pages';
import { pollApi } from '../../util/pollUtils';
import { getValue } from '../../util/injector';
import {
MigrationState,
MigrationErrorStateToString,
MigrationStaticError,
} from '../../data/enum/MigrationState';
import { ResourceType, ResourceTypeString } from '../../data/enum/ResourceType';
import WebHost from '../../data/enum/WebHost';
import RedirectError from '../../util/RedirectError';
import LoginErrorCode from '../../data/enum/LoginErrorCode';
import LegacyDataObjectName from '../../data/enum/LegacyDataObjectName';
import StatusCode from '../../data/enum/StatusCode';
import getValuesFromArray from '../../util/getValuesFromArray';
import { getUrlParameter } from '../../util/urlUtils';
import authenticate from '../../util/auth/authenticate';
export const MIGRATION_SIGN_UP = 'migrationActions/SIGN_UP';
export const signUp = ({ newPassword, legacyPassword }) => async (dispatch, getState) => {
try {
const { requireNewPassword, id } = await dispatch(
apiPost(MIGRATION_SIGN_UP, GATEWAY_ACCOUNT_MIGRATION, '/migration/sign-up', {
legacyPassword,
newPassword,
}),
);
if (!requireNewPassword && id) {
setMigrationUserId(id);
}
if (!requireNewPassword) {
const returnHost = getState().config.environmentConfig.web[WebHost.MIGRATION].host;
document.location.href = `${returnHost}${Pages.MIGRATION_STATE_CHECK_WAIT_FOR_MIGRATION_CREATION}`;
return true;
}
return dispatch(historyPush(Pages.MIGRATION_UPDATE_PASSWORD));
} catch (error) {
const { status, text } = error?.response;
const { code } = text && JSON.parse(text)?.error;
if (status === StatusCode.STATUS_401 && code === LoginErrorCode.UNAUTHORIZED) {
dispatch(setMigrationIsUnauthorized(true));
return true;
}
throw error;
}
};
export const MIGRATION_SET_SECURITY_ANSWER_STATE =
'migrationActions/MIGRATION_SET_SECURITY_ANSWER_STATE';
export const setSecurityAnswerState = () => dispatch =>
dispatch(
apiPost(
MIGRATION_SET_SECURITY_ANSWER_STATE,
GATEWAY_ACCOUNT_MIGRATION,
'/migration/update-state-to-security-answer-set',
),
);
export const SET_MIGRATION_USER_ID = 'migrationActions/SET_MIGRATION_USER_ID';
export const setMigrationUserId = createAction(SET_MIGRATION_USER_ID, userId => ({ userId }));
export const SET_MIGRATION_IS_UNAUTHORIZED = 'migrationActions/SET_MIGRATION_IS_UNAUTHORIZED';
export const setMigrationIsUnauthorized = createAction(
SET_MIGRATION_IS_UNAUTHORIZED,
isUnauthorized => ({ isUnauthorized }),
);
/**
* Returns the migration id/state and userId for the migrating account
*
* @type {string}
*/
export const GET_MIGRATIONS = 'migrationActions/GET_MIGRATIONS';
export const getMigrations = () => (dispatch, getState) => {
const userId = getState().authentication?.userInfo?.sub;
return dispatch(
apiGet(
GET_MIGRATIONS,
GATEWAY_MIGRATION_AUTH,
'/migrations',
{
userId,
limit: 1,
sort: SortType.NEWEST,
},
{
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
},
),
).catch(error => {
// Logged in migration user not found, so let's redirect to the not-allowed page
const { status, text } = error?.response;
const { code } = text && JSON.parse(text)?.error;
if (status === StatusCode.STATUS_404 && code === LoginErrorCode.NOT_FOUND) {
throw new RedirectError(
`${createPath(Pages.MIGRATION_ERROR, { errorState: MigrationStaticError.NOT_ALLOWED })}`,
);
}
throw error;
});
};
/**
* Redirect the user to the specific error content page
*
* @returns {Function}
*/
const redirectToMigrationErrorPage = () => (dispatch, getState) => {
const migrationState = getState().migration?.migrationState;
if (!Object.keys(MigrationErrorStateToString).includes(migrationState)) return true;
return dispatch(
historyPush(createPath(Pages.MIGRATION_ERROR, { errorState: MigrationStaticError.DEFAULT })),
);
};
/**
* User accepts to lock his state to load the legacy data
*
* @type {string}
*/
export const ACCEPT_LOCK = 'migrationActions/ACCEPT_LOCK';
export const acceptLock = () => async (dispatch, getState) => {
const state = getState();
const id = state.migration?.id;
await dispatch(apiPost(ACCEPT_LOCK, GATEWAY_MIGRATION_AUTH, `/migrations/${id}/accept-lock`));
// We need to poll the state till it's LOCKING_LEGACY_MEMBER || LOADING_LEGACY_DATA || ErrorsState
await dispatch(
pollingCurrentMigrationState([
MigrationState.LOCKING_LEGACY_MEMBER,
MigrationState.LOADING_LEGACY_DATA,
MigrationState.LEGACY_DATA_LOADED,
MigrationState.LEGACY_MEMBER_UNLOCKED_DUE_TO_FAILURE,
MigrationState.LOCK_LEGACY_MEMBER_FAILED,
MigrationState.UNLOCKING_LEGACY_MEMBER_FAILED,
MigrationState.FAILED,
MigrationState.ROLLING_BACK,
MigrationState.IMPORT_FAILING,
]),
);
};
/**
* Retrieves the summary for the weight/ goals/ awards page
*
* @type {string}
*/
export const GET_MIGRATIONS_SUMMARY = 'migrationActions/GET_MIGRATIONS_SUMMARY';
export const getMigrationsSummary = () => (dispatch, getState) => {
const id = getState().migration?.id;
return dispatch(
apiGet(GET_MIGRATIONS_SUMMARY, GATEWAY_MIGRATION_AUTH, `/migrations/${id}/resources/summary`),
);
};
/**
* Returns boolean whether the user is a group account or not
*
* @type {string}
*/
export const GET_IS_GROUP_MEMBER = 'migrationActions/GET_IS_GROUP_MEMBER';
export const getIsGroupMember = () => async (dispatch, getState) => {
await authenticate();
const userId = getState().authentication?.userInfo?.sub;
return dispatch(
apiGet(GET_IS_GROUP_MEMBER, GATEWAY_MIGRATION_AUTH, `/users/${userId}/is-group-member`),
);
};
/**
* Returns boolean whether the user has a beta account or not
*
* @type {string}
*/
export const GET_IS_BETA_MEMBER = 'migrationActions/GET_IS_BETA_MEMBER';
export const getIsBetaMember = () => async (dispatch, getState) => {
await authenticate();
const userId = getState().authentication?.userInfo?.sub;
return dispatch(
apiGet(GET_IS_BETA_MEMBER, GATEWAY_MIGRATION_AUTH, `/users/${userId}/is-beta-member`),
);
};
/**
* Returns legacy data url
*
* @type {string}
*/
export const GET_LEGACY_SITE_URL = 'migrationActions/GET_LEGACY_SITE_URL';
export const getLegacySiteUrl = () => async (dispatch, getState) => {
await authenticate();
const userId = getState().authentication?.userInfo?.sub;
return dispatch(
apiGet(GET_LEGACY_SITE_URL, GATEWAY_MIGRATION_AUTH, `/users/${userId}/legacy-site-url`),
);
};
/**
* Returns the data for a specific resource (personalDetails || addresses)
*
* @type {string}
*/
export const GET_VERIFIABLE_RESOURCE = 'migrationActions/GET_VERIFIABLE_RESOURCE';
export const getVerifiableResource = type => (dispatch, getState) => {
const migrationId = getState().migration?.id;
return dispatch(
apiGet(GET_VERIFIABLE_RESOURCE, GATEWAY_MIGRATION_AUTH, '/verifiable-resources', {
migrationId,
type,
}),
);
};
/**
* Update the data in the redux state for a specific resource
*
* @type {string}
*/
export const SET_VERIFIABLE_RESOURCE = 'migrationActions/SET_VERIFIABLE_RESOURCE';
export const setVerifiableResource = createAction(
SET_VERIFIABLE_RESOURCE,
(values, updateAtIndex, isArray) => ({
...values,
updateAtIndex,
isArray,
}),
);
/**
* Update the verified redux data into the database
*
* @type {string}
*/
export const PUT_VERIFIABLE_RESOURCE = 'migrationActions/PUT_VERIFIABLE_RESOURCE';
export const putVerifiableResource = (type, dataFields) => (dispatch, getState) => {
const { id: resourceId, data } = getState().migration?.[ResourceTypeString[type]];
let resources = data;
if (type === ResourceType.ADDRESSES) {
resources = [...data];
} else if (dataFields) {
// Get only the fields that we want to submit
resources = getValuesFromArray(dataFields, data);
}
return dispatch(
apiPut(
PUT_VERIFIABLE_RESOURCE,
GATEWAY_MIGRATION_AUTH,
`/verifiable-resources/${resourceId}/verified-data`,
resources,
),
);
};
export const putVerifiableResources = () => async dispatch => {
await Promise.all([
dispatch(putVerifiableResource(ResourceType.WEIGHTS)),
dispatch(putVerifiableResource(ResourceType.TARGETS)),
dispatch(putVerifiableResource(ResourceType.HEIGHTS)),
dispatch(
putVerifiableResource(ResourceType.PERSONAL_DETAILS, Object.values(LegacyDataObjectName)),
),
dispatch(putVerifiableResource(ResourceType.ADDRESSES)),
]);
return dispatch(historyPush(Pages.MIGRATION_TRANSFER));
};
/**
* returns the current migration state
*
* @type {string}
*/
export const GET_MIGRATIONS_STATE = 'migrationActions/GET_MIGRATIONS_STATE';
export const getMigrationsState = () => (dispatch, getState) => {
const id = getState().migration?.id;
return dispatch(
apiGet(
GET_MIGRATIONS_STATE,
GATEWAY_MIGRATION_AUTH,
`/migrations/${id}/state`,
{},
{
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
},
),
);
};
const TIMES_IN_MIN = 60 * 1000 * 10;
const TIMES_INTERVAL_MS = 5000;
/**
* This method is only used after the signUp call when the requireNewPassword = false
*
* location = location object
*
* @param arrayValuesToCheck
* @returns {function(*, *): Promise<any>}
*/
const useForWmfcPollingStatesArray = [
MigrationState.INITIAL,
MigrationState.SECURITY_ANSWER_NOT_SET,
MigrationState.SECURITY_ANSWER_SET,
MigrationState.LOCKING_LEGACY_MEMBER,
MigrationState.LOADING_LEGACY_DATA,
MigrationState.LEGACY_DATA_LOADED,
MigrationState.IMPORTING,
MigrationState.COMPLETING,
];
const waitForMigrationCreationPolling = location => dispatch => {
if (location) {
const hasParamWfmc = getUrlParameter(location, PARAM_WAIT_FOR_MIGRATION_CREATION) === 'true';
if (hasParamWfmc) {
// Infinite polling until the migration user is created with a correct state
return pollApi(
() => dispatch(getMigrations()),
response =>
// Let's poll the state until it has one of the states in the array
response?.data?.[0] && useForWmfcPollingStatesArray.includes(response?.data?.[0].state),
TIMES_INTERVAL_MS,
);
}
}
return Promise.resolve();
};
/**
* Poll the current migration state and redirect the user to the correct page according to the state
*
* arrayValuesToCheck = optional array that you wanted to check until the polling stops
* gotoRedirectPage = When you wanted to redirect to a page according to a state
*
* @param arrayValuesToCheck
* @returns {function(*, *): Promise<any>}
*/
export const pollingCurrentMigrationState = (arrayValuesToCheck, location = null) => async (
dispatch,
getState,
) => {
await dispatch(waitForMigrationCreationPolling(location));
return pollApi(
() => dispatch(getMigrationsState()),
response => {
// This response will determine when the polling needs to stop
const arrayValues = !arrayValuesToCheck ? Object.values(MigrationState) : arrayValuesToCheck;
return arrayValues.includes(response?.data);
},
TIMES_INTERVAL_MS,
TIMES_IN_MIN,
)
.then(() => {
const migrationState = getState().migration?.migrationState;
switch (migrationState) {
case MigrationState.SECURITY_ANSWER_NOT_SET: {
const returnHost = getState().config.environmentConfig.web[WebHost.ACCOUNT].host;
window.location.href = `${returnHost}${Pages.MIGRATION_SET_SECURITY_QUESTION}`;
return true;
}
case MigrationState.SECURITY_ANSWER_SET: {
return dispatch(historyPush(Pages.MIGRATION_CONFIRM));
}
case MigrationState.LOCKING_LEGACY_MEMBER:
case MigrationState.LOADING_LEGACY_DATA: {
return dispatch(historyPush(Pages.MIGRATION_LOADING));
}
case MigrationState.LEGACY_DATA_LOADED: {
return dispatch(historyPush(Pages.MIGRATION_DATA_CHECK));
}
case MigrationState.IMPORTING:
case MigrationState.COMPLETING: {
return dispatch(historyPush(Pages.MIGRATION_TRANSFER));
}
case MigrationState.COMPLETED: {
return dispatch(historyPush(Pages.MIGRATION_COMPLETED));
}
case MigrationState.LEGACY_MEMBER_UNLOCKED_DUE_TO_FAILURE:
case MigrationState.LOCK_LEGACY_MEMBER_FAILED:
case MigrationState.UNLOCKING_LEGACY_MEMBER_FAILED:
case MigrationState.FAILED:
case MigrationState.ROLLING_BACK:
case MigrationState.IMPORT_FAILING: {
return dispatch(
historyPush(
createPath(Pages.MIGRATION_ERROR, { errorState: MigrationStaticError.DEFAULT }),
),
);
}
default: {
return dispatch(historyPush(Pages.MIGRATION_CONFIRM));
}
}
})
.catch(() => dispatch(redirectToMigrationErrorPage()));
};
export const refreshLogin = () => () => getValue(AUTHENTICATION_MANAGER).refreshLogin();
export const SET_RETURN_STATE_URL = 'migrationActions/SET_RETURN_STATE_URL';
export const setReturnStateUrl = createAction(SET_RETURN_STATE_URL, returnUrl => ({
returnUrl,
}));