diff --git a/docs/usage/utils.md b/docs/usage/utils.md index 46cb4efbc..3b7fff120 100644 --- a/docs/usage/utils.md +++ b/docs/usage/utils.md @@ -1,12 +1,14 @@ # utils -## Get the HOST application URL using `getHostAppUrl` +## Get the application URL for embedded apps using `getEmbeddedAppUrl` -If you need to redirect a request to your app inside the user's admin you can use `getHostAppUrl`. +If you need to redirect a request to your embedded app URL you can use `getEmbeddedAppUrl` ```ts -const redirectURL = getHostAppUrl(request); +const redirectURL = getEmbeddedAppUrl(request); res.redirect(redirectURL); ``` +Using this utility ensures that embedded app URL is properly constructed and brings the merchant to the right place. It is more reliable than using the shop param. + This utility relies on the host query param being a Base 64 encoded string. All requests from Shopify should include this param in the correct format. diff --git a/src/utils/__tests__/get-embedded-app-url.test.ts b/src/utils/__tests__/get-embedded-app-url.test.ts new file mode 100644 index 000000000..7009aa358 --- /dev/null +++ b/src/utils/__tests__/get-embedded-app-url.test.ts @@ -0,0 +1,51 @@ +import http from 'http'; + +import getEmbeddedAppUrl from '../get-embedded-app-url'; +import * as ShopifyErrors from '../../error'; +import {Context} from '../../context'; + +describe('getEmbeddedAppUrl', () => { + beforeEach(() => { + Context.API_KEY = 'my-api-key'; + }); + + test('throws an error when no request is passed', () => { + // @ts-expect-error: For JS users test it throws when no request is passed + expect(() => getEmbeddedAppUrl()).toThrow( + ShopifyErrors.MissingRequiredArgument, + ); + }); + + test('throws an error when the request has no URL', () => { + const req = { + url: undefined, + } as http.IncomingMessage; + + expect(() => getEmbeddedAppUrl(req)).toThrow( + ShopifyErrors.InvalidRequestError, + ); + }); + + test('throws an error when the request has no host query param', () => { + const req = { + url: 'http://www.example.com', + } as http.IncomingMessage; + + expect(() => getEmbeddedAppUrl(req)).toThrow( + ShopifyErrors.InvalidRequestError, + ); + }); + + test('returns the host app url', () => { + const host = 'my-store.shopify.com/admin'; + const base64Host = Buffer.from(host, 'utf-8').toString('base64'); + + const req = { + url: `http://www.example.com?host=${base64Host}`, + } as http.IncomingMessage; + + expect(getEmbeddedAppUrl(req)).toBe( + 'https://my-store.shopify.com/admin/apps/my-api-key', + ); + }); +}); diff --git a/src/utils/__tests__/get-host-app-url.test.ts b/src/utils/__tests__/get-host-app-url.test.ts deleted file mode 100644 index fe651127c..000000000 --- a/src/utils/__tests__/get-host-app-url.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import http from 'http'; - -import getHostAppUrl from '../get-host-app-url'; -import * as ShopifyErrors from '../../error'; -import {Context} from '../../context'; - -describe('getHostAppUrl', () => { - beforeEach(() => { - Context.API_KEY = 'my-api-key'; - }); - - test('thows an error when no request is passed', () => { - // @ts-expect-error: For JS users test it throws when no request is passed - expect(() => getHostAppUrl()).toThrow( - ShopifyErrors.MissingRequiredArgument, - ); - }); - - test('thows an error when the request has no URL', () => { - const req = { - url: undefined, - } as http.IncomingMessage; - - expect(() => getHostAppUrl(req)).toThrow(ShopifyErrors.InvalidRequestError); - }); - - test('thows an error when the request has no host query param', () => { - const req = { - url: 'www.example.com', - } as http.IncomingMessage; - - expect(() => getHostAppUrl(req)).toThrow(ShopifyErrors.InvalidRequestError); - }); - - test('returns the host app url', () => { - const host = 'my-store.shopify.com'; - const base64Host = Buffer.from(host, 'utf-8').toString('base64'); - - const req = { - url: `www.example.com?host=${base64Host}`, - } as http.IncomingMessage; - - expect(getHostAppUrl(req)).toBe( - 'https://my-store.shopify.com/apps/my-api-key', - ); - }); -}); diff --git a/src/utils/get-host-app-url.ts b/src/utils/get-embedded-app-url.ts similarity index 83% rename from src/utils/get-host-app-url.ts rename to src/utils/get-embedded-app-url.ts index 87dfa1c0f..fa4c48ce4 100644 --- a/src/utils/get-host-app-url.ts +++ b/src/utils/get-embedded-app-url.ts @@ -9,10 +9,12 @@ import {Context} from '../context'; * * @param request Current HTTP request */ -export default function getHostAppUrl(request: http.IncomingMessage): string { +export default function getEmbeddedAppUrl( + request: http.IncomingMessage, +): string { if (!request) { throw new ShopifyErrors.MissingRequiredArgument( - 'getHostAppUrl requires a request object argument', + 'getEmbeddedAppUrl requires a request object argument', ); } diff --git a/src/utils/index.ts b/src/utils/index.ts index 2432952a5..583904efc 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -11,7 +11,7 @@ import validateHmac from './hmac-validator'; import validateShop from './shop-validator'; import versionCompatible from './version-compatible'; import withSession from './with-session'; -import getHostAppUrl from './get-host-app-url'; +import getEmbeddedAppUrl from './get-embedded-app-url'; const ShopifyUtils = { decodeSessionToken, @@ -27,7 +27,7 @@ const ShopifyUtils = { validateShop, versionCompatible, withSession, - getHostAppUrl, + getEmbeddedAppUrl, }; export default ShopifyUtils;