dreams-web-sdk
Version:
324 lines (319 loc) • 12.2 kB
JavaScript
var partnerEvents;
(function (partnerEvents) {
// It's really unfortunate that we call this event "accountProvisioned",
// when what we mean is "accountProvisionInitiated". But that's the current
// name that is being used by the dreams backend
partnerEvents["accountProvisionInitiated"] = "accountProvisioned";
partnerEvents["investmentAccountProvisionInitiated"] = "investmentAccountProvisionInitiated";
partnerEvents["updateToken"] = "updateToken";
partnerEvents["navigateTo"] = "navigateTo";
partnerEvents["transferConsentSucceeded"] = "onTransferConsentSucceeded";
partnerEvents["transferConsentCancelled"] = "onTransferConsentCancelled";
partnerEvents["accountRequestedFailed"] = "onAccountRequestedFailed";
partnerEvents["accountRequestedSucceeded"] = "onAccountRequestedSucceeded";
})(partnerEvents || (partnerEvents = {}));
var partnerEvents$1 = partnerEvents;
class MessageHandler {
constructor(iframe, apiUrl, callbacks) {
this.listen = () => window.addEventListener('message', this.onMessage);
/*
* This method handles all possible messages coming from dreams app.
* For each type of message an appropriate function defined in the callbacks has to be added.
* Check {@linkcode ClientCallbacks}.
*/
this.onMessage = async message => {
console.debug('onMessage: ', message);
const event = this.parseEvent(message);
if (!event) return;
switch (event.event) {
case 'onIdTokenDidExpire':
this.onIdTokenDidExpire(event);
break;
case 'onAccountProvisionRequested':
this.onAccountProvisionRequested(event);
break;
case 'onInvestmentAccountProvisionRequested':
this.onInvestmentAccountProvisionRequested(event);
break;
case 'onInvestmentSellRequested':
this.onInvestmentSellRequested(event);
break;
case 'onExitRequested':
await this.callbacks.onExitRequested(event);
break;
case 'onShare':
this.onShare(event);
break;
case 'onTransferConsentRequested':
this.onTransferConsentRequested(event);
break;
case 'onAccountRequested':
this.onAccountRequested(event);
break;
default:
console.warn('Unknown event type:', event);
}
};
/**
* You can use this method if you need to manually update the token.
*/
this.postUpdateToken = message => {
const event = {
event: partnerEvents$1.updateToken,
message
};
this.postMessage(event);
};
/**
* You can use this method if you need to manually inform the dreams app that account provision has been initiated.
*/
this.postAccountProvisionInitiated = message => {
const event = {
event: partnerEvents$1.accountProvisionInitiated,
message
};
this.postMessage(event);
};
/**
* You can use this method if you need to manually inform the dreams app that investment account provision has been initiated.
* AccountId is a shared id of a newly provisioned account. Whenever dreams will make a request to transfer money
* to/from an account it will use this value to refer to that account.
*/
this.postInvestmentAccountProvisionInitiated = message => {
const event = {
event: partnerEvents$1.investmentAccountProvisionInitiated,
message
};
this.postMessage(event);
};
this.postTransferConsentRequestSucceeded = message => {
const event = {
event: partnerEvents$1.transferConsentSucceeded,
message
};
this.postMessage(event);
};
this.postTransferConsentRequestCancelled = message => {
const event = {
event: partnerEvents$1.transferConsentCancelled,
message
};
this.postMessage(event);
};
this.postAccountRequestFailed = message => {
const event = {
event: partnerEvents$1.accountRequestedFailed,
message
};
this.postMessage(event);
};
this.postAccountRequestSucceeded = message => {
const event = {
event: partnerEvents$1.accountRequestedSucceeded,
message
};
this.postMessage(event);
};
/**
* @param location the part of the dreams app where you want to take the user to. You have to only pass the path.
*/
this.navigateTo = location => {
const event = {
event: partnerEvents$1.navigateTo,
message: {
location
}
};
this.postMessage(event);
};
this.onIdTokenDidExpire = async event => {
if (!this.callbacks.onIdTokenDidExpire) return;
try {
const token = await this.callbacks.onIdTokenDidExpire(event);
const msg = {
requestId: event.message.requestId,
idToken: token
};
this.postUpdateToken(msg);
} catch (err) {
console.error('onIdTokenDidExpire error: ', err);
}
};
this.onAccountProvisionRequested = async event => {
if (!this.callbacks.onAccountProvisionRequested) return;
try {
await this.callbacks.onAccountProvisionRequested(event);
this.postAccountProvisionInitiated(event.message);
} catch (err) {
console.error('onAccountProvisionRequested error: ', err);
}
};
this.onInvestmentAccountProvisionRequested = async event => {
if (!this.callbacks.onInvestmentAccountProvisionRequested) return;
try {
await this.callbacks.onInvestmentAccountProvisionRequested(event);
this.postInvestmentAccountProvisionInitiated(event.message);
} catch (err) {
console.error('onInvestmentAccountProvisionRequested error: ', err);
}
};
this.onInvestmentSellRequested = async event => {
if (!this.callbacks.onInvestmentSellRequested) return;
try {
await this.callbacks.onInvestmentSellRequested(event);
} catch (err) {
console.error('onInvestmentSellRequested error: ', err);
}
};
this.onShare = async event => {
if (this.callbacks.onShare) await this.callbacks.onShare(event);
};
this.onTransferConsentRequested = async event => {
if (!this.callbacks.onTransferConsentRequested) {
return;
}
try {
const transferConsentData = await this.callbacks.onTransferConsentRequested(event);
if (transferConsentData) {
this.postTransferConsentRequestSucceeded(transferConsentData);
}
} catch (err) {
this.postTransferConsentRequestCancelled(err);
console.error('onTransferConsentRequested error', err);
}
};
this.postMessage = message => {
console.debug('postMessage', message);
if (this.iframe.contentWindow) {
this.iframe.contentWindow.postMessage(JSON.stringify(message), this.apiUrl);
} else {
console.error('iframe has no content window!', this.iframe);
}
};
this.parseEvent = message => {
try {
return JSON.parse(message.data);
} catch (error) {
console.error(error);
return null;
}
};
this.validateParams = apiUrl => {
if (!apiUrl) throw Error('Invalid parameters: dreamsApiEndpoint must be specified');
};
this.validateParams(apiUrl);
this.iframe = iframe;
this.apiUrl = apiUrl;
this.callbacks = callbacks;
}
async onAccountRequested(event) {
if (!this.callbacks.onAccountRequested) {
return;
}
try {
const accountRequestedResult = await this.callbacks.onAccountRequested(event);
this.postAccountRequestSucceeded(accountRequestedResult);
} catch (err) {
this.postAccountRequestFailed(err);
console.error('onAccountRequested error: ', err);
}
}
}
const iframeName = 'dreams-web-sdk-iframe';
const createForm = (endpoint, tokenProps = {
type: 'hidden',
name: 'token',
value: ''
}, localeProps = {
type: 'hidden',
name: 'locale',
value: 'en'
}) => {
const form = document.createElement('form');
form.setAttribute('target', iframeName);
form.setAttribute('method', 'POST');
form.setAttribute('action', endpoint);
form.setAttribute('class', 'hidden');
const formInputLocale = document.createElement('input');
formInputLocale.setAttribute('type', localeProps.type);
formInputLocale.setAttribute('name', localeProps.name);
formInputLocale.setAttribute('value', localeProps.value);
const formInputToken = document.createElement('input');
formInputToken.setAttribute('type', tokenProps.type);
formInputToken.setAttribute('name', tokenProps.name);
formInputToken.setAttribute('value', tokenProps.value);
const formInputLocation = document.createElement('input');
formInputLocation.setAttribute('type', 'hidden');
formInputLocation.setAttribute('name', 'location');
formInputLocation.setAttribute('value', '');
const formInputTheme = document.createElement('input');
formInputTheme.setAttribute('type', 'hidden');
formInputTheme.setAttribute('name', 'theme');
formInputTheme.setAttribute('value', '');
form.appendChild(formInputLocale);
form.appendChild(formInputToken);
form.appendChild(formInputLocation);
form.appendChild(formInputTheme);
return form;
};
const createIFrame = (className = iframeName) => {
const iframe = document.createElement('iframe');
iframe.setAttribute('name', iframeName);
iframe.setAttribute('class', className);
return iframe;
};
/**
* DreamSDK is an utility class responsible for setting up and listening
* to messages being exchanged between the your context and Dreams iframe.
*
* ```typescript
* const sdk = new DreamsSDK('https://dreams.api.endpoint');
* sdk.setup(callbacks);
* sdk.start(jwk_token, locale);
* ```
*/
class DreamsSDK {
constructor(apiUrl) {
this.apiUrl = apiUrl;
}
/**
* @param callbacks as time goes this object might contain more keys. Think about that when writing your code.
* @param containerId you are free to specify your value if that's needed. Otherwise, leave the default.
* @param iframeClassName if you want the iframe to have a specific class, you can do it via this param.
*/
setup(callbacks, containerId = 'dreams-web-sdk-container', iframeClassName = iframeName) {
if (!this.apiUrl) throw Error('there is no api url specified!');
const dreamDiv = document.getElementById(containerId);
if (!dreamDiv) throw Error("can't find dreams web sdk container");
const formTargetUrl = `${this.apiUrl}/users/verify_token`;
this.form = createForm(formTargetUrl);
this.iframe = createIFrame(iframeClassName);
dreamDiv.appendChild(this.form);
dreamDiv.appendChild(this.iframe);
this.messageHandler = new MessageHandler(this.iframe, this.apiUrl, callbacks);
return this.messageHandler;
}
/**
* @param token jwk token for the user
* @param locale determines the localization configuration that will be applied.
* @param location path to which the user will be redirected to after the token is verified
* @param theme determines the color theme that will be applied to the app
*/
start(token, locale, location, theme) {
if (!this.iframe) throw Error('there is no iframe specified!');
if (!this.form) throw Error('there is no form specified!');
if (!this.messageHandler) throw Error('there is no message handler specified!');
const tokenInput = this.form.querySelector("input[name='token']");
if (tokenInput) tokenInput.setAttribute('value', token);
const localeInput = this.form.querySelector("input[name='locale']");
if (localeInput) localeInput.setAttribute('value', locale);
const locationInput = this.form.querySelector("input[name='location']");
if (location) locationInput.setAttribute('value', location);
const themeInput = this.form.querySelector("input[name='theme']");
if (theme) themeInput.setAttribute('value', theme);
this.messageHandler.listen();
this.form.submit();
}
}
export { MessageHandler, DreamsSDK as default };
//# sourceMappingURL=dreams-web-sdk.js.map