diff --git a/docs/README.md b/docs/README.md
index 58755f7..6389dbe 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -309,6 +309,20 @@ mtLinkSdk.openService('myaccount', { view: 'settings/update-email' });
| serviceId | `myaccount` | true | | Open MyAccount |
| options.view | string | false | `settings` (for mobile view)
OR
`settings/update-email` (for desktop view) | Directly go to the chosen page. Currently supported locations include:`settings` - Main Moneytree account settings screen.`settings/authorized-applications` - List of apps currently connected to Moneytree.`settings/change-language` - Change Moneytree account language screen.`settings/email-preferences` - Change Moneytree email preferences screen`settings/delete-account` - Delete Moneytree account screen.`settings/update-email` - Change Moneytree account email screen.`settings/update-password` - Change Moneytree account password screen.
If no value is provided, it goes to the top page of MyAccount. |
+### openServiceUrl
+
+This method generates URLs to open various services provided by Moneytree, such as Moneytree Account Settings and Vault, etc. If you want to use the URLs for other purposes or load them in a different way, use this API instead of `openService`.
+
+NOTE: calling this API before calling `init` will open the services view without branding (company logo etc.)
+
+Usage:
+
+```javascript
+mtLinkSdk.openServiceUrl(serviceId, options);
+```
+
+This API has exactly the same parameters as `openService`, the only difference being that it returns an URL instead of opening immediately with `window.open`.
+
### requestLoginLink
Request for a password-less login link to be sent to the guest's email address. Clicking on the link in the email will log a guest in directly to the screen specified by the `loginLinkTo` parameter.
diff --git a/src/api/__tests__/open-service-url.test.ts b/src/api/__tests__/open-service-url.test.ts
new file mode 100644
index 0000000..59f57f1
--- /dev/null
+++ b/src/api/__tests__/open-service-url.test.ts
@@ -0,0 +1,183 @@
+import qs from 'qs';
+
+import { MY_ACCOUNT_DOMAINS, VAULT_DOMAINS, LINK_KIT_DOMAINS } from '../../server-paths';
+import { MtLinkSdk } from '../..';
+import openServiceUrl from '../open-service-url';
+import { generateConfigs } from '../../helper';
+
+describe('api', () => {
+ describe('open-service-url', () => {
+ const clientId = 'clientId';
+
+ test('myaccount', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'myaccount');
+
+ const query = qs.stringify({
+ configs: generateConfigs()
+ });
+
+ expect(url).toBe(`${MY_ACCOUNT_DOMAINS.production}/?${query}`);
+ });
+
+ test('myaccount/change-language', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'myaccount', {
+ view: 'settings/change-language'
+ });
+
+ const query = qs.stringify({
+ configs: generateConfigs()
+ });
+
+ expect(url).toBe(`${MY_ACCOUNT_DOMAINS.production}/settings/change-language?${query}`);
+ });
+
+ test('vault', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'vault', {
+ showRememberMe: false
+ });
+
+ const query = qs.stringify({
+ configs: generateConfigs({
+ showRememberMe: false
+ })
+ });
+
+ expect(url).toBe(`${VAULT_DOMAINS.production}?${query}`);
+ });
+
+ test('vault/services-list', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'vault', {
+ view: 'services-list',
+ type: 'bank',
+ group: 'grouping_testing',
+ search: 'vault',
+ showRememberMe: false
+ });
+
+ const query = qs.stringify({
+ configs: generateConfigs({
+ showRememberMe: false
+ }),
+ group: 'grouping_testing',
+ type: 'bank',
+ search: 'vault'
+ });
+
+ expect(url).toBe(`${VAULT_DOMAINS.production}/services?${query}`);
+ });
+
+ test('vault/service-connection', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'vault', {
+ view: 'service-connection',
+ entityKey: 'fauxbank_test_bank',
+ showRememberMe: false
+ });
+
+ const query = qs.stringify({
+ configs: generateConfigs({
+ showRememberMe: false
+ })
+ });
+
+ expect(url).toBe(`${VAULT_DOMAINS.production}/service/fauxbank_test_bank?${query}`);
+ });
+
+ test('vault/connection-setting', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'vault', {
+ view: 'connection-setting',
+ credentialId: '123',
+ showRememberMe: false
+ });
+
+ const query = qs.stringify({
+ configs: generateConfigs({
+ showRememberMe: false
+ })
+ });
+
+ expect(url).toBe(`${VAULT_DOMAINS.production}/connection/123?${query}`);
+ });
+
+ test('vault/customer-support', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'vault', {
+ view: 'customer-support',
+ showRememberMe: false
+ });
+
+ const query = qs.stringify({
+ configs: generateConfigs({
+ showRememberMe: false
+ })
+ });
+
+ expect(url).toBe(`${VAULT_DOMAINS.production}/customer-support?${query}`);
+ });
+
+ test('link-kit', () => {
+ const url = openServiceUrl(new MtLinkSdk().storedOptions, 'link-kit');
+
+ const query = qs.stringify({
+ configs: generateConfigs()
+ });
+
+ expect(url).toBe(`${LINK_KIT_DOMAINS.production}?${query}`);
+ });
+
+ test('calling after init will includes client id', () => {
+ const cobrandClientId = 'cobrandClientId';
+ const locale = 'locale';
+
+ const mtLinkSdk = new MtLinkSdk();
+ mtLinkSdk.init(clientId, {
+ locale,
+ cobrandClientId
+ });
+
+ const url = openServiceUrl(mtLinkSdk.storedOptions, 'myaccount');
+
+ const query = qs.stringify({
+ client_id: clientId,
+ cobrand_client_id: cobrandClientId,
+ locale,
+ configs: generateConfigs()
+ });
+
+ expect(url).toBe(`${MY_ACCOUNT_DOMAINS.production}/?${query}`);
+ });
+
+ test('invalid service id', () => {
+ expect(() => {
+ openServiceUrl(new MtLinkSdk().storedOptions, 'invalid');
+ }).toThrow('[mt-link-sdk] Invalid `serviceId` in `openServiceUrl/openService`, got: invalid');
+ });
+
+ test('saml_subject_id is passed when initialized', () => {
+ const instance = new MtLinkSdk();
+ instance.init('clientId', { samlSubjectId: 'samlSubjectId' });
+
+ const url = openServiceUrl(instance.storedOptions, 'myaccount');
+
+ const query = qs.stringify({
+ client_id: 'clientId',
+ saml_subject_id: 'samlSubjectId',
+ configs: generateConfigs()
+ });
+
+ expect(url).toBe(`${MY_ACCOUNT_DOMAINS.production}/?${query}`);
+ });
+
+ test('undefined saml_subject_id should not be passed down', () => {
+ const instance = new MtLinkSdk();
+ instance.init('clientId', { samlSubjectId: undefined });
+
+ const url = openServiceUrl(instance.storedOptions, 'myaccount');
+
+ const query = qs.stringify({
+ client_id: 'clientId',
+ configs: generateConfigs()
+ });
+
+ expect(url).toBe(`${MY_ACCOUNT_DOMAINS.production}/?${query}`);
+ });
+ });
+});
diff --git a/src/api/__tests__/open-service.test.ts b/src/api/__tests__/open-service.test.ts
index 2bac17e..43c036a 100644
--- a/src/api/__tests__/open-service.test.ts
+++ b/src/api/__tests__/open-service.test.ts
@@ -194,7 +194,7 @@ describe('api', () => {
test('invalid service id', () => {
expect(() => {
openService(new MtLinkSdk().storedOptions, 'invalid');
- }).toThrow('[mt-link-sdk] Invalid `serviceId` in `openService`, got: invalid');
+ }).toThrow('[mt-link-sdk] Invalid `serviceId` in `openServiceUrl/openService`, got: invalid');
});
test('saml_subject_id is passed when initialized', () => {
diff --git a/src/api/open-service-url.ts b/src/api/open-service-url.ts
new file mode 100644
index 0000000..e6f7998
--- /dev/null
+++ b/src/api/open-service-url.ts
@@ -0,0 +1,90 @@
+import { stringify } from 'qs';
+
+import { generateConfigs, mergeConfigs } from '../helper';
+import { MY_ACCOUNT_DOMAINS, VAULT_DOMAINS, LINK_KIT_DOMAINS } from '../server-paths';
+import {
+ StoredOptions,
+ ServiceId,
+ OpenServicesUrlConfigsOptions,
+ ConnectionSettingType,
+ ServiceConnectionType,
+ ServicesListType
+} from '../typings';
+
+interface QueryData {
+ client_id?: string;
+ cobrand_client_id?: string;
+ locale?: string;
+ configs: string;
+ saml_subject_id?: string;
+}
+
+export default function openServiceUrl(
+ storedOptions: StoredOptions,
+ serviceId: ServiceId,
+ options: OpenServicesUrlConfigsOptions = {}
+): string {
+ const { clientId, mode, cobrandClientId, locale, samlSubjectId } = storedOptions;
+ const { view = '', ...rest } = options;
+
+ const getQueryValue = (needStringify = true): string | QueryData => {
+ const query: QueryData = {
+ client_id: clientId,
+ cobrand_client_id: cobrandClientId,
+ locale,
+ saml_subject_id: samlSubjectId,
+ configs: generateConfigs(mergeConfigs(storedOptions, rest))
+ };
+
+ if (!needStringify) {
+ return query;
+ }
+
+ return stringify(query);
+ };
+
+ switch (serviceId) {
+ case 'vault':
+ if (!view) {
+ return `${VAULT_DOMAINS[mode]}?${getQueryValue()}`;
+ }
+
+ switch (view) {
+ case 'services-list':
+ // eslint-disable-next-line no-case-declarations
+ const { group, type, search } = options as ServicesListType;
+
+ return `${VAULT_DOMAINS[mode]}/services?${stringify({
+ ...(getQueryValue(false) as QueryData),
+ group,
+ type,
+ search
+ })}`;
+
+ case 'service-connection':
+ // eslint-disable-next-line no-case-declarations
+ const { entityKey } = options as ServiceConnectionType;
+
+ return `${VAULT_DOMAINS[mode]}/service/${entityKey}?${getQueryValue()}`;
+
+ case 'connection-setting':
+ // eslint-disable-next-line no-case-declarations
+ const { credentialId } = options as ConnectionSettingType;
+
+ return `${VAULT_DOMAINS[mode]}/connection/${credentialId}?${getQueryValue()}`;
+
+ case 'customer-support':
+ default:
+ return `${VAULT_DOMAINS[mode]}/customer-support?${getQueryValue()}`;
+ }
+
+ case 'myaccount':
+ return `${MY_ACCOUNT_DOMAINS[mode]}/${view}?${getQueryValue()}`;
+
+ case 'link-kit':
+ return `${LINK_KIT_DOMAINS[mode]}?${getQueryValue()}`;
+
+ default:
+ throw new Error(`[mt-link-sdk] Invalid \`serviceId\` in \`openServiceUrl/openService\`, got: ${serviceId}`);
+ }
+}
diff --git a/src/api/open-service.ts b/src/api/open-service.ts
index 99c1a66..1f5df25 100644
--- a/src/api/open-service.ts
+++ b/src/api/open-service.ts
@@ -1,23 +1,6 @@
-import { stringify } from 'qs';
-
-import { generateConfigs, mergeConfigs, getIsTabValue, openWindow } from '../helper';
-import { MY_ACCOUNT_DOMAINS, VAULT_DOMAINS, LINK_KIT_DOMAINS } from '../server-paths';
-import {
- StoredOptions,
- ServiceId,
- OpenServicesConfigsOptions,
- ConnectionSettingType,
- ServiceConnectionType,
- ServicesListType
-} from '../typings';
-
-interface QueryData {
- client_id?: string;
- cobrand_client_id?: string;
- locale?: string;
- configs: string;
- saml_subject_id?: string;
-}
+import { getIsTabValue, openWindow } from '../helper';
+import { StoredOptions, ServiceId, OpenServicesConfigsOptions } from '../typings';
+import openServiceUrl from './open-service-url';
export default function openService(
storedOptions: StoredOptions,
@@ -28,79 +11,7 @@ export default function openService(
throw new Error('[mt-link-sdk] `openService` only works in the browser.');
}
- const { clientId, mode, cobrandClientId, locale, samlSubjectId } = storedOptions;
- const { isNewTab, view = '', ...rest } = options;
-
- const getQueryValue = (needStringify = true): string | QueryData => {
- const query: QueryData = {
- client_id: clientId,
- cobrand_client_id: cobrandClientId,
- locale,
- saml_subject_id: samlSubjectId,
- configs: generateConfigs(mergeConfigs(storedOptions, rest))
- };
-
- if (!needStringify) {
- return query;
- }
-
- return stringify(query);
- };
-
- switch (serviceId) {
- case 'vault':
- if (!view) {
- openWindow(`${VAULT_DOMAINS[mode]}?${getQueryValue()}`, getIsTabValue(isNewTab));
- break;
- }
-
- switch (view) {
- case 'services-list':
- // eslint-disable-next-line no-case-declarations
- const { group, type, search } = options as ServicesListType;
-
- openWindow(
- `${VAULT_DOMAINS[mode]}/services?${stringify({
- ...(getQueryValue(false) as QueryData),
- group,
- type,
- search
- })}`,
- getIsTabValue(isNewTab)
- );
- break;
+ const { isNewTab, ...restOptions } = options;
- case 'service-connection':
- // eslint-disable-next-line no-case-declarations
- const { entityKey } = options as ServiceConnectionType;
-
- openWindow(`${VAULT_DOMAINS[mode]}/service/${entityKey}?${getQueryValue()}`, getIsTabValue(isNewTab));
- break;
-
- case 'connection-setting':
- // eslint-disable-next-line no-case-declarations
- const { credentialId } = options as ConnectionSettingType;
-
- openWindow(`${VAULT_DOMAINS[mode]}/connection/${credentialId}?${getQueryValue()}`, getIsTabValue(isNewTab));
- break;
-
- case 'customer-support':
- default:
- openWindow(`${VAULT_DOMAINS[mode]}/customer-support?${getQueryValue()}`, getIsTabValue(isNewTab));
- break;
- }
-
- break;
-
- case 'myaccount':
- openWindow(`${MY_ACCOUNT_DOMAINS[mode]}/${view}?${getQueryValue()}`, getIsTabValue(isNewTab));
- break;
-
- case 'link-kit':
- openWindow(`${LINK_KIT_DOMAINS[mode]}?${getQueryValue()}`, getIsTabValue(isNewTab));
- break;
-
- default:
- throw new Error(`[mt-link-sdk] Invalid \`serviceId\` in \`openService\`, got: ${serviceId}`);
- }
+ openWindow(openServiceUrl(storedOptions, serviceId, restOptions), getIsTabValue(isNewTab));
}
diff --git a/src/index.ts b/src/index.ts
index 9feac49..805be03 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,6 +2,7 @@ import authorize from './api/authorize';
import onboard from './api/onboard';
import logout from './api/logout';
import openService from './api/open-service';
+import openServiceUrl from './api/open-service-url';
import requestLoginLink from './api/request-login-link';
import exchangeToken from './api/exchange-token';
import tokenInfo from './api/token-info';
@@ -16,7 +17,8 @@ import {
ExchangeTokenOptions,
RequestLoginLinkOptions,
TokenInfo,
- Mode
+ Mode,
+ OpenServicesUrlConfigsOptions
} from './typings';
export * from './typings';
@@ -64,6 +66,10 @@ export class MtLinkSdk {
openService(this.storedOptions, serviceId, options);
}
+ public openServiceUrl(serviceId: ServiceId, options?: OpenServicesUrlConfigsOptions): string {
+ return openServiceUrl(this.storedOptions, serviceId, options);
+ }
+
public requestLoginLink(options?: RequestLoginLinkOptions): Promise {
return requestLoginLink(this.storedOptions, options);
}
diff --git a/src/typings.ts b/src/typings.ts
index d926b97..5f4def5 100644
--- a/src/typings.ts
+++ b/src/typings.ts
@@ -59,6 +59,9 @@ export type MyAccountPageType = { view?: LoginLinkTo };
export type OpenServicesConfigsOptions = ConfigsOptions &
(ServicesListType | ServiceConnectionType | ConnectionSettingType | CustomerSupportType | MyAccountPageType);
+export type OpenServicesUrlConfigsOptions = Omit &
+ (ServicesListType | ServiceConnectionType | ConnectionSettingType | CustomerSupportType | MyAccountPageType);
+
export type Scopes = string | string[];
interface AuthorizeConfigsOptions {