From 251dd1b689a4adef1cfb811697c1ee3917052842 Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Tue, 8 Oct 2024 21:37:28 +0200 Subject: [PATCH] feat: customize mfe links --- packages/drop-in/package.json | 1 + .../drop-in/src/apis/commercelayer/account.ts | 12 ++++-- .../drop-in/src/apis/commercelayer/cart.ts | 16 ++++---- .../drop-in/src/apis/commercelayer/config.ts | 39 +++++++++++++++++++ .../cl-cart-link/cl-cart-link.spec.tsx | 5 +++ .../cl-identity-link.spec.tsx | 3 ++ .../cl-my-account-link.spec.tsx | 9 +++++ pnpm-lock.yaml | 30 ++++++++++++++ 8 files changed, 105 insertions(+), 10 deletions(-) diff --git a/packages/drop-in/package.json b/packages/drop-in/package.json index 033c3b32..c58f4396 100644 --- a/packages/drop-in/package.json +++ b/packages/drop-in/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "@commercelayer/js-auth": "^6.3.1", + "@commercelayer/organization-config": "^1.4.9", "@commercelayer/sdk": "^6.14.1", "@types/lodash": "^4.17.7", "iframe-resizer": "4.4.2", diff --git a/packages/drop-in/src/apis/commercelayer/account.ts b/packages/drop-in/src/apis/commercelayer/account.ts index 028f06ae..d7c0674d 100644 --- a/packages/drop-in/src/apis/commercelayer/account.ts +++ b/packages/drop-in/src/apis/commercelayer/account.ts @@ -1,5 +1,5 @@ import { getAccessToken } from '#apis/commercelayer/client' -import { getConfig } from '#apis/commercelayer/config' +import { getConfig, getOrganizationConfig } from '#apis/commercelayer/config' import { getClosestLocationHref } from '#utils/url' export async function getMyAccountUrl(): Promise { @@ -10,7 +10,10 @@ export async function getMyAccountUrl(): Promise { return undefined } - return `${config.appEndpoint}/my-account?accessToken=${accessToken}` + const organizationConfig = await getOrganizationConfig() + const hostedUrl = `${config.appEndpoint}/my-account` + + return `${organizationConfig?.links?.my_account ?? hostedUrl}?accessToken=${accessToken}` } export async function getIdentityUrl( @@ -22,7 +25,10 @@ export async function getIdentityUrl( return '#' } - return `${config.appEndpoint}/identity/${type}?clientId=${ + const organizationConfig = await getOrganizationConfig() + const hostedUrl = `${config.appEndpoint}/identity` + + return `${organizationConfig?.links?.identity ?? hostedUrl}/${type}?clientId=${ config.clientId }&scope=${config.scope}&returnUrl=${getClosestLocationHref()}` } diff --git a/packages/drop-in/src/apis/commercelayer/cart.ts b/packages/drop-in/src/apis/commercelayer/cart.ts index 23145f31..231c042e 100644 --- a/packages/drop-in/src/apis/commercelayer/cart.ts +++ b/packages/drop-in/src/apis/commercelayer/cart.ts @@ -1,5 +1,5 @@ import { createClient, getAccessToken } from '#apis/commercelayer/client' -import { getConfig } from '#apis/commercelayer/config' +import { getConfig, getOrganizationConfig } from '#apis/commercelayer/config' import { fireEvent } from '#apis/event' import { getKeyForCart } from '#apis/storage' import type { @@ -86,9 +86,10 @@ export async function getCartUrl( cartId = cart.id } - return `${config.appEndpoint}/cart/${ - cartId ?? 'null' - }?accessToken=${accessToken}` + const organizationConfig = await getOrganizationConfig() + const hostedUrl = `${config.appEndpoint}/cart/${cartId ?? 'null'}` + + return `${organizationConfig?.links?.cart ?? hostedUrl}?accessToken=${accessToken}` } export async function getCheckoutUrl(): Promise { @@ -100,9 +101,10 @@ export async function getCheckoutUrl(): Promise { return undefined } - return `${config.appEndpoint}/checkout/${ - cart.id ?? 'null' - }?accessToken=${accessToken}` + const organizationConfig = await getOrganizationConfig() + const hostedUrl = `${config.appEndpoint}/checkout/${cart.id ?? 'null'}` + + return `${organizationConfig?.links?.checkout ?? hostedUrl}?accessToken=${accessToken}` } export async function _getCart(): Promise { diff --git a/packages/drop-in/src/apis/commercelayer/config.ts b/packages/drop-in/src/apis/commercelayer/config.ts index b81f5bf8..e09499d3 100644 --- a/packages/drop-in/src/apis/commercelayer/config.ts +++ b/packages/drop-in/src/apis/commercelayer/config.ts @@ -1,3 +1,9 @@ +import { jwtDecode, jwtIsSalesChannel } from '@commercelayer/js-auth' +import { createClient, getAccessToken } from './client' +import { getConfig as mergeConfig } from '@commercelayer/organization-config' +import { getCart } from './cart' +import { memoize } from '#utils/utils' + export interface CommerceLayerConfig { /** * Client ID is the application unique identifier. You can find it in your dashboard. @@ -107,3 +113,36 @@ export function getConfig(): Config { appEndpoint } } + +const getOrganization = memoize(async () => { + const config = getConfig() + const client = await createClient(config) + return await client.organization.retrieve({ + fields: ['config'] + }) +}) + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export async function getOrganizationConfig() { + const config = getConfig() + const { accessToken } = await getAccessToken(config) + const jwt = jwtDecode(accessToken) + const cart = await getCart() + + const organization = await getOrganization() + + const mergeConfigOptions: Parameters[0] = { + jsonConfig: organization.config ?? {}, + market: + jwtIsSalesChannel(jwt.payload) && jwt.payload.market?.id[0] != null + ? `market:id:${jwt.payload.market.id[0]}` + : undefined, + params: { + accessToken, + orderId: cart?.id, + lang: 'en' + } + } + + return mergeConfig(mergeConfigOptions) +} diff --git a/packages/drop-in/src/components/cl-cart-link/cl-cart-link.spec.tsx b/packages/drop-in/src/components/cl-cart-link/cl-cart-link.spec.tsx index 00c0748b..30cb5a12 100644 --- a/packages/drop-in/src/components/cl-cart-link/cl-cart-link.spec.tsx +++ b/packages/drop-in/src/components/cl-cart-link/cl-cart-link.spec.tsx @@ -1,4 +1,5 @@ import * as cart from '#apis/commercelayer/cart' +import * as config from '#apis/commercelayer/config' import * as client from '#apis/commercelayer/client' import type { CommerceLayerClient } from '@commercelayer/sdk' import { newSpecPage } from '@stencil/core/testing' @@ -17,6 +18,8 @@ describe('cl-cart-link.spec', () => { scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) + const { root, waitForChanges } = await newSpecPage({ components: [CLCartLink], html: 'Cart' @@ -45,6 +48,7 @@ describe('cl-cart-link.spec', () => { accessToken: 'token-123', scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) jest.spyOn(cart, 'getCart').mockResolvedValue({ type: 'orders', id: 'order-123', @@ -86,6 +90,7 @@ describe('cl-cart-link.spec', () => { accessToken: 'token-123', scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) jest.spyOn(cart, 'getCart').mockResolvedValue(null) jest .spyOn(client, 'createClient') diff --git a/packages/drop-in/src/components/cl-identity-link/cl-identity-link.spec.tsx b/packages/drop-in/src/components/cl-identity-link/cl-identity-link.spec.tsx index 5410473e..a2f2bcfc 100644 --- a/packages/drop-in/src/components/cl-identity-link/cl-identity-link.spec.tsx +++ b/packages/drop-in/src/components/cl-identity-link/cl-identity-link.spec.tsx @@ -1,4 +1,5 @@ import * as client from '#apis/commercelayer/client' +import * as config from '#apis/commercelayer/config' import { newSpecPage } from '@stencil/core/testing' import { ClIdentityLink } from './cl-identity-link' import * as logger from '#utils/logger' @@ -14,6 +15,8 @@ beforeEach(() => { scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) + log = jest.spyOn(logger, 'log') }) diff --git a/packages/drop-in/src/components/cl-my-account-link/cl-my-account-link.spec.tsx b/packages/drop-in/src/components/cl-my-account-link/cl-my-account-link.spec.tsx index 2dfb39a5..ebc10961 100644 --- a/packages/drop-in/src/components/cl-my-account-link/cl-my-account-link.spec.tsx +++ b/packages/drop-in/src/components/cl-my-account-link/cl-my-account-link.spec.tsx @@ -1,4 +1,5 @@ import * as client from '#apis/commercelayer/client' +import * as config from '#apis/commercelayer/config' import { fireEvent } from '#apis/event' import { newSpecPage } from '@stencil/core/testing' import { ClMyAccountLink } from './cl-my-account-link' @@ -15,6 +16,8 @@ describe('cl-my-account-link.spec', () => { scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) + const { root, waitForChanges } = await newSpecPage({ components: [ClMyAccountLink], html: 'My Account' @@ -38,6 +41,8 @@ describe('cl-my-account-link.spec', () => { scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) + const { root, waitForChanges } = await newSpecPage({ components: [ClMyAccountLink], html: 'My Account' @@ -62,6 +67,8 @@ describe('cl-my-account-link.spec', () => { scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) + const { root, waitForChanges } = await newSpecPage({ components: [ClMyAccountLink], html: 'My Account' @@ -86,6 +93,8 @@ describe('cl-my-account-link.spec', () => { scope: 'market:code:usa' }) + jest.spyOn(config, 'getOrganizationConfig').mockResolvedValue({}) + const { root, waitForChanges } = await newSpecPage({ components: [ClMyAccountLink], html: 'My Account' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8a4f052..5508b621 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,6 +135,9 @@ importers: '@commercelayer/js-auth': specifier: ^6.3.1 version: 6.3.1 + '@commercelayer/organization-config': + specifier: ^1.4.9 + version: 1.4.9(eslint@8.57.0)(typescript@5.5.4) '@commercelayer/sdk': specifier: ^6.14.1 version: 6.14.1 @@ -982,6 +985,13 @@ packages: resolution: {integrity: sha512-f+SrlerLE+fUtyGeSe90y1BIuekF962aDPA6JCsBFm34S7nfWp8VW0IUvPAiZJGNVtlDFuWrW0VB5qN9z49bKQ==} engines: {node: '>=18.0.0'} + '@commercelayer/organization-config@1.4.9': + resolution: {integrity: sha512-gVQoIMVqBo88xkLn5E/OUDTY21Ac45Nrcq6jkiMeKen5NHGWVFDne7qEl70ySxEqACOczw1VZZPVX8XUyN8NOw==} + engines: {node: '>=18', pnpm: '>=7'} + peerDependencies: + eslint: '>=8.0' + typescript: '>=5.0' + '@commercelayer/sdk@6.14.1': resolution: {integrity: sha512-iByH/4fM2++s9urlxQJWk8mtNmLzWUTtpflA+54uTPJgFRRU+UlS5Q4n0QwZEfcEfg00HU0RNUo0s8o/jDEOBA==} engines: {node: '>=20'} @@ -4138,6 +4148,10 @@ packages: is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -4645,6 +4659,10 @@ packages: resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} engines: {node: '>=10'} + merge-anything@5.1.7: + resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} + engines: {node: '>=12.13'} + merge-descriptors@1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} @@ -7702,6 +7720,12 @@ snapshots: '@commercelayer/js-auth@6.3.1': {} + '@commercelayer/organization-config@1.4.9(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + eslint: 8.57.0 + merge-anything: 5.1.7 + typescript: 5.5.4 + '@commercelayer/sdk@6.14.1': {} '@esbuild/aix-ppc64@0.20.2': @@ -11511,6 +11535,8 @@ snapshots: dependencies: call-bind: 1.0.7 + is-what@4.1.16: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -12400,6 +12426,10 @@ snapshots: type-fest: 0.18.1 yargs-parser: 20.2.9 + merge-anything@5.1.7: + dependencies: + is-what: 4.1.16 + merge-descriptors@1.0.1: {} merge-stream@2.0.0: {}