Skip to content

Instantly share code, notes, and snippets.

@Bogidon
Created March 18, 2021 22:06
Show Gist options
  • Save Bogidon/f107df4e55636399038b0d7a02dba50f to your computer and use it in GitHub Desktop.
Save Bogidon/f107df4e55636399038b0d7a02dba50f to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const State = {
cancelMigrationConfirmation: 'cancelMigrationConfirmation',
done: 'done',
duplicatePortal: 'duplicatePortal',
initial: 'initial',
migrationAccountsMismatch: 'migrationAccountsMismatch',
migrationDisclosure: 'migrationDisclosure',
plaidFlow: 'plaidFlow',
portalError: 'portalError',
selectInstitution: 'selectInstitution',
smsVerification: 'smsVerification',
unsupportedInstitution: 'unsupportedInstitution',
yodleeFlow: 'yodleeFlow',
};
const Transition = {
BACK: 'BACK',
CONTINUE: 'CONTINUE',
SELECT_INSTITUTION: 'SELECT_INSTITUTION',
GO_TO_MIGRATION: 'GO_TO_MIGRATION',
};
const Action = {
BEGIN_MIGRATION: 'BEGIN_MIGRATION',
SELECT_INSTITUTION: 'SELECT_INSTITUTION',
UPDATE_PORTAL: 'UPDATE_PORTAL',
UPDATE_MIGRATION_DETAILS: 'UPDATE_MIGRATION_DETAILS',
};
const Condition = {
HAS_SELECTED_PLAID_PORTAL: 'HAS_SELECTED_PLAID_PORTAL',
HAS_SELECTED_YODLEE_PORTAL: 'HAS_SELECTED_YODLEE_PORTAL',
IS_ACCOUNTS_MISMATCH: 'IS_ACCOUNTS_MISMATCH',
IS_DUPLICATE_LINK: 'IS_DUPLICATE_LINK',
IS_FIXING_PORTAL: 'IS_FIXING_PORTAL',
IS_MIGRATION: 'IS_MIGRATION',
IS_PLAID_PORTAL: 'IS_PLAID_PORTAL',
IS_SELECTING_PLAID_PORTAL: 'IS_SELECTING_PLAID_PORTAL',
IS_SELECTING_YODLEE_PORTAL: 'IS_SELECTING_YODLEE_PORTAL',
IS_UNSUPPORTED_INSTITUTION: 'IS_UNSUPPORTED_INSTITUTION',
IS_YODLEE_MFA: 'IS_YODLEE_MFA',
IS_YODLEE_PORTAL: 'IS_YODLEE_PORTAL',
REQUIRES_SMS_VERIFICATION: 'REQUIRES_SMS_VERIFICATION',
};
const Context = {
INSTITUTION: 'institution',
MIGRATION_DETAILS: 'migrationDetails',
PORTAL: 'portal',
REQUIRES_SMS_VERIFICATION: 'requiresSmsVerification',
};
const YodleeLinkState = {
auth: 'yodleeAuth',
done: 'yodleeDone',
initial: 'yodleeInitial',
mfa: 'yodleeMfa',
};
const YodleeLinkTransition = {
BACK: 'yodleeBack',
CONTINUE: 'yodleeContinue',
UPDATE_PORTAL: 'yodleeUpdatePortal',
};
const yodleeStates = {
initial: YodleeLinkState.initial,
states: {
[YodleeLinkState.initial]: {
always: [
{
cond: Condition.IS_YODLEE_MFA,
target: YodleeLinkState.mfa,
},
{
target: YodleeLinkState.auth,
},
],
},
// todo: I'm probably missing complexity here
[YodleeLinkState.auth]: {
on: {
[YodleeLinkTransition.CONTINUE]: [
{
cond: Condition.IS_YODLEE_MFA,
target: YodleeLinkState.mfa,
actions: [Action.UPDATE_PORTAL],
},
{
target: YodleeLinkState.done,
actions: [Action.UPDATE_PORTAL],
},
],
[YodleeLinkTransition.BACK]: {
target: YodleeLinkState.done,
},
},
},
[YodleeLinkState.mfa]: {
on: {
[YodleeLinkTransition.CONTINUE]: {
target: YodleeLinkState.done,
},
[YodleeLinkTransition.BACK]: {
target: YodleeLinkState.back,
},
},
},
[YodleeLinkState.done]: {
type: 'final',
},
},
};
const PlaidLinkState = {
done: 'plaidDone',
initial: 'plaidInitial',
link: 'plaidLink',
relink: 'plaidRelink',
};
const PlaidLinkTransition = {
BACK: 'plaidBack',
CONTINUE: 'plaidContinue',
};
const plaidStates = {
initial: PlaidLinkState.initial,
states: {
[PlaidLinkState.initial]: {
always: [
{
cond: Condition.IS_PLAID_PORTAL,
target: PlaidLinkState.relink,
},
{
target: PlaidLinkState.link,
},
],
},
[PlaidLinkState.link]: {
on: {
[PlaidLinkTransition.BACK]: {
target: PlaidLinkState.done,
},
[PlaidLinkTransition.CONTINUE]: {
actions: [Action.UPDATE_MIGRATION_DETAILS],
target: PlaidLinkState.done,
},
},
},
[PlaidLinkState.relink]: {
on: {
[PlaidLinkTransition.BACK]: {
target: PlaidLinkState.done,
},
[PlaidLinkTransition.CONTINUE]: {
target: PlaidLinkState.done,
},
},
},
[PlaidLinkState.done]: {
type: 'final',
},
},
};
const isYodleePortalInMfaRequired = ({ portal } = {}) => {
return portal?.authState === 'USER_ACTION_REQUIRED' && portal?.primarySource === 'YODLEE';
};
const isAccountsMismatch = ({ migrationDetails } = {}) => {
return migrationDetails?.oldAccountIds?.length > migrationDetails?.newAccountIds?.length;
};
const accountAggregationMachine = Machine(
{
id: 'accountAggregation',
initial: State.initial,
context: {
// the currently selected institution
[Context.INSTITUTION]: undefined,
// if we are attempting to migrate the primary source of the portal
[Context.IS_MIGRATION]: undefined,
// the current portal being fixed or linked
// todo: how to differentiate if this a linked portal or not?
[Context.PORTAL]: undefined,
// whether the user requires SMS verification or not
[Context.REQUIRES_SMS_VERIFICATION]: undefined,
},
states: {
[State.initial]: {
always: [
{
target: [State.portalError],
cond: Condition.IS_FIXING_PORTAL,
},
{
target: [State.selectInstitution],
},
],
},
[State.selectInstitution]: {
on: {
[Transition.SELECT_INSTITUTION]: [
{
actions: [Action.SELECT_INSTITUTION],
cond: Condition.IS_DUPLICATE_LINK,
target: State.duplicatePortal,
},
{
actions: [Action.SELECT_INSTITUTION],
cond: Condition.IS_UNSUPPORTED_INSTITUTION,
target: State.unsupportedInstitution,
},
{
actions: [Action.SELECT_INSTITUTION],
cond: Condition.REQUIRES_SMS_VERIFICATION,
target: State.smsVerification,
},
{
actions: [Action.SELECT_INSTITUTION],
cond: Condition.IS_MIGRATION,
target: State.migrationDisclosure,
},
{
actions: [Action.SELECT_INSTITUTION],
cond: Condition.IS_SELECTING_PLAID_PORTAL,
target: State.plaidFlow,
},
{
actions: [Action.SELECT_INSTITUTION],
cond: Condition.IS_SELECTING_YODLEE_PORTAL,
target: State.yodleeFlow,
},
],
},
},
[State.duplicatePortal]: {
on: {
[Transition.BACK]: {
target: State.selectInstitution,
},
},
},
[State.unsupportedInstitution]: {
on: {
[Transition.BACK]: {
target: State.selectInstitution,
},
},
},
[State.smsVerification]: {
on: {
[Transition.BACK]: {
target: State.selectInstitution,
},
[Transition.CONTINUE]: [
{
cond: Condition.HAS_SELECTED_PLAID_PORTAL,
target: State.plaidFlow,
},
{
cond: Condition.HAS_SELECTED_YODLEE_PORTAL,
target: State.plaidFlow,
},
],
},
},
// todo: do we need separate error screens for Yodlee and Plaid? Logic today is pretty different
// but maybe can be simplified?
[State.portalError]: {
on: {
[Transition.BACK]: {
target: State.done,
},
[Transition.CONTINUE]: [
{
actions: [Action.UPDATE_PORTAL],
cond: Condition.IS_ACCOUNTS_MISMATCH,
target: State.migrationAccountsMismatch,
},
{
actions: [Action.UPDATE_PORTAL],
cond: Condition.IS_MIGRATION,
target: State.plaidFlow,
},
{
// actions: [Action.UPDATE_PORTAL],
cond: Condition.IS_PLAID_PORTAL,
target: State.plaidFlow,
},
{
// actions: [Action.UPDATE_PORTAL],
cond: Condition.IS_YODLEE_PORTAL,
target: State.yodleeFlow,
},
],
},
},
[State.migrationDisclosure]: {
on: {
[Transition.BACK]: {
target: State.portalError,
},
[Transition.CONTINUE]: {
target: State.plaidFlow,
},
},
},
[State.migrationAccountsMismatch]: {
on: {
[Transition.BACK]: {
target: State.portalError,
},
[Transition.CONTINUE]: {
target: State.done,
},
},
},
[State.cancelMigrationConfirmation]: {
on: {
[Transition.BACK]: {
target: State.migrationAccountsMismatch,
},
[Transition.CONTINUE]: {
target: State.done,
},
},
},
[State.plaidFlow]: {
on: {
[Transition.BACK]: [
{
cond: Condition.IS_FIXING_PORTAL,
target: State.portalError,
},
{
target: State.selectInstitution,
},
],
[Transition.CONTINUE]: [
{
cond: Condition.IS_ACCOUNTS_MISMATCH,
target: State.migrationAccountsMismatch,
},
{
target: State.done,
},
],
},
...plaidStates,
},
[State.yodleeFlow]: {
on: {
[Transition.BACK]: [
{
cond: Condition.IS_FIXING_PORTAL,
target: State.portalError,
},
],
[Transition.CONTINUE]: {
target: State.done,
},
[Transition.GO_TO_MIGRATION]: {
actions: [Action.BEGIN_MIGRATION],
target: State.plaidFlow,
},
},
...yodleeStates,
},
[State.done]: {
type: 'final',
},
},
},
{
actions: {
[Action.BEGIN_MIGRATION]: assign({
[Context.MIGRATION_DETAILS]: () => {},
}),
[Action.SELECT_INSTITUTION]: assign({
[Context.INSTITUTION]: (_context, event) => event.institution,
}),
[Action.UPDATE_MIGRATION_DETAILS]: assign({
[Context.MIGRATION_DETAILS]: (_context, event) => event.migrationDetails,
}),
[Action.UPDATE_PORTAL]: assign({
[Context.PORTAL]: (_context, event) => event.portal,
}),
},
guards: {
[Condition.IS_FIXING_PORTAL]: (context) => {
return !!context.portal?.id;
},
[Condition.IS_PLAID_PORTAL]: (context) => {
return context.portal?.primarySource === 'PLAID';
},
[Condition.IS_YODLEE_PORTAL]: (context) => {
return context.portal?.primarySource === 'YODLEE';
},
[Condition.IS_SELECTING_PLAID_PORTAL]: (_context, event) => {
return event?.institution?.aggregator === 'PLAID';
},
[Condition.IS_SELECTING_YODLEE_PORTAL]: (_context, event) => {
return event?.institution?.aggregator === 'YODLEE';
},
[Condition.IS_DUPLICATE_LINK]: (_context, event) => {
// todo: update check of action against context
// will context know which institutions have been linked already?
return !!event.isDuplicate;
},
[Condition.IS_UNSUPPORTED_INSTITUTION]: (_context, event) => {
// todo: update check of action
return !!event.isUnsupported;
},
[Condition.IS_YODLEE_MFA]: (context, event) => {
return isYodleePortalInMfaRequired(event) || isYodleePortalInMfaRequired(context);
},
[Condition.REQUIRES_SMS_VERIFICATION]: (context) => {
return !!context.requiresSmsVerification;
},
[Condition.HAS_SELECTED_PLAID_PORTAL]: (context) => {
return context?.institution?.aggregator === 'PLAID';
},
[Condition.HAS_SELECTED_YODLEE_PORTAL]: (context) => {
return context?.institution?.aggregator === 'YODLEE';
},
[Condition.IS_MIGRATION]: (context, event) => {
return !!context?.migrationDetails || !!event?.migrationDetails;
},
[Condition.IS_ACCOUNTS_MISMATCH]: (context, event) => {
return isAccountsMismatch(event) || isAccountsMismatch(context);
},
},
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment