From 3a9b965c91112431a1fb702568062b361520b14d Mon Sep 17 00:00:00 2001 From: Matt Blewitt <118200221+mble-sfdc@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:56:44 +0100 Subject: [PATCH 1/5] fix: ensure x-app-uuid is present when using token The AppLink control plane requires this header to be present as part of validations, so lets ensure it is present. Additionally, lets set a valid User-Agent header to indicate this is from the SDK. Ref: W-18723611 --- src/add-ons/heroku-applink.ts | 1 + src/utils/addon-config.ts | 17 +++++++++++++++++ src/utils/request.ts | 8 +++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/add-ons/heroku-applink.ts b/src/add-ons/heroku-applink.ts index 1ac91c3..a8277b9 100644 --- a/src/add-ons/heroku-applink.ts +++ b/src/add-ons/heroku-applink.ts @@ -47,6 +47,7 @@ export async function getAuthorization( method: "GET", headers: { Authorization: `Bearer ${config.token}`, + "X-App-UUID": config.appUuid, "Content-Type": "application/json", }, retry: { diff --git a/src/utils/addon-config.ts b/src/utils/addon-config.ts index d7e60eb..a3558b6 100644 --- a/src/utils/addon-config.ts +++ b/src/utils/addon-config.ts @@ -5,14 +5,23 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +type AppUuid = string; + interface AddonConfig { apiUrl: string; token: string; + appUuid: AppUuid; } export function resolveAddonConfigByAttachmentOrColor( attachmentOrColor: string ): AddonConfig { + const appUuid = process.env.HEROKU_APP_ID || process.env.APPLINK_APP_ID; + + if (!appUuid) { + throw Error(`Heroku Applink app UUID not found`); + } + const addon = process.env.HEROKU_APPLINK_ADDON_NAME || "HEROKU_APPLINK"; let apiUrl; @@ -37,10 +46,17 @@ export function resolveAddonConfigByAttachmentOrColor( return { apiUrl, token, + appUuid, }; } export function resolveAddonConfigByUrl(url: string): AddonConfig { + const appUuid = process.env.HEROKU_APP_ID || process.env.APPLINK_APP_ID; + + if (!appUuid) { + throw Error(`Heroku Applink app UUID not found`); + } + // Find the environment variable ending with _API_URL that matches the given URL const envVarEntries = Object.entries(process.env); const matchingApiUrlEntry = envVarEntries.find( @@ -65,5 +81,6 @@ export function resolveAddonConfigByUrl(url: string): AddonConfig { return { apiUrl: matchingApiUrlEntry[1], token, + appUuid, }; } diff --git a/src/utils/request.ts b/src/utils/request.ts index 9509923..03f9ea3 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -19,7 +19,13 @@ export class HTTPResponseError extends Error { */ export class HttpRequestUtil { async request(url: string, opts: any, json = true) { - const response = await fetch(url, opts); + const defaultOpts = { + headers: { + "User-Agent": `heroku-applink-node-sdk/1.0`, + }, + }; + + const response = await fetch(url, { ...defaultOpts, ...opts }); if (!response.ok) { throw new HTTPResponseError(response); From ad9214a6ed28984fd6a822836d66a68c71872ea7 Mon Sep 17 00:00:00 2001 From: Matt Blewitt <118200221+mble-sfdc@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:00:28 +0100 Subject: [PATCH 2/5] remove APPLINK_APP_ID --- src/utils/addon-config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/addon-config.ts b/src/utils/addon-config.ts index a3558b6..d71719e 100644 --- a/src/utils/addon-config.ts +++ b/src/utils/addon-config.ts @@ -16,7 +16,7 @@ interface AddonConfig { export function resolveAddonConfigByAttachmentOrColor( attachmentOrColor: string ): AddonConfig { - const appUuid = process.env.HEROKU_APP_ID || process.env.APPLINK_APP_ID; + const appUuid = process.env.HEROKU_APP_ID if (!appUuid) { throw Error(`Heroku Applink app UUID not found`); @@ -51,7 +51,7 @@ export function resolveAddonConfigByAttachmentOrColor( } export function resolveAddonConfigByUrl(url: string): AddonConfig { - const appUuid = process.env.HEROKU_APP_ID || process.env.APPLINK_APP_ID; + const appUuid = process.env.HEROKU_APP_ID if (!appUuid) { throw Error(`Heroku Applink app UUID not found`); From 09e7e640d13a679dbf9f0a93700478af55e31357 Mon Sep 17 00:00:00 2001 From: Matt Blewitt <118200221+mble-sfdc@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:02:02 +0100 Subject: [PATCH 3/5] touch changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b27d240..bf298fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,3 +14,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `getAuthorization` to use the correct API URL. - Rename `getConnection(name: string)` -> `getAuthorization(developerName: string, attachmentNameOrColorUrl = "HEROKU_APPLINK")`, accepting a new attachmentNameOrColorOrUrl to use a specific Applink addon's config. - Remove node-fetch in favor of native fetch, add `HTTPResponseError` + +## Unreleased +- Add `X-App-UUID` header, `heroku-applink-node-sdk` UA. From bac92e5c5a3d3588f200e7cf349251b682447e19 Mon Sep 17 00:00:00 2001 From: Matt Blewitt <118200221+mble-sfdc@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:21:40 +0100 Subject: [PATCH 4/5] fix up tests --- src/utils/addon-config.ts | 4 ++-- test/add-ons/heroku-applink.test.ts | 1 + test/utils/addon-config.test.ts | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/utils/addon-config.ts b/src/utils/addon-config.ts index d71719e..c5c9783 100644 --- a/src/utils/addon-config.ts +++ b/src/utils/addon-config.ts @@ -16,7 +16,7 @@ interface AddonConfig { export function resolveAddonConfigByAttachmentOrColor( attachmentOrColor: string ): AddonConfig { - const appUuid = process.env.HEROKU_APP_ID + const appUuid = process.env.HEROKU_APP_ID; if (!appUuid) { throw Error(`Heroku Applink app UUID not found`); @@ -51,7 +51,7 @@ export function resolveAddonConfigByAttachmentOrColor( } export function resolveAddonConfigByUrl(url: string): AddonConfig { - const appUuid = process.env.HEROKU_APP_ID + const appUuid = process.env.HEROKU_APP_ID; if (!appUuid) { throw Error(`Heroku Applink app UUID not found`); diff --git a/test/add-ons/heroku-applink.test.ts b/test/add-ons/heroku-applink.test.ts index c2f7cb2..729d8fe 100644 --- a/test/add-ons/heroku-applink.test.ts +++ b/test/add-ons/heroku-applink.test.ts @@ -45,6 +45,7 @@ describe("getAuthorization", () => { process.env.HEROKU_APPLINK_PURPLE_API_URL = "https://applink.staging.herokudev.com/addons/536c15d8-c2c1-47f7-a582-76f5aae385e0"; process.env.HEROKU_APPLINK_PURPLE_TOKEN = "purple-token"; + process.env.HEROKU_APP_ID = "d52a726b-11a4-47a1-a4b6-2e18a771c2ac"; httpRequestStub = sinon.stub(HttpRequestUtil.prototype, "request"); }); diff --git a/test/utils/addon-config.test.ts b/test/utils/addon-config.test.ts index 2cf9dd1..36f26f3 100644 --- a/test/utils/addon-config.test.ts +++ b/test/utils/addon-config.test.ts @@ -18,6 +18,7 @@ describe("resolveAddonConfigByAttachmentOrColor", () => { beforeEach(() => { originalEnv = { ...process.env }; + process.env.HEROKU_APP_ID = "d52a726b-11a4-47a1-a4b6-2e18a771c2ac"; }); afterEach(() => { @@ -81,6 +82,14 @@ describe("resolveAddonConfigByAttachmentOrColor", () => { "Heroku Applink config not found under attachment or color HEROKU_APPLINK" ); }); + + it("throws if HEROKU_APP_ID is not set", () => { + delete process.env.HEROKU_APP_ID; + + expect(() => resolveAddonConfigByAttachmentOrColor(ATTACHMENT)).to.throw( + "Heroku Applink app UUID not found" + ); + }); }); describe("resolveAddonConfigByUrl", () => { @@ -88,6 +97,7 @@ describe("resolveAddonConfigByUrl", () => { beforeEach(() => { originalEnv = { ...process.env }; + process.env.HEROKU_APP_ID = "d52a726b-11a4-47a1-a4b6-2e18a771c2ac"; }); afterEach(() => { @@ -131,4 +141,12 @@ describe("resolveAddonConfigByUrl", () => { `Heroku Applink token not found for API URL: ${testUrl}` ); }); + + it("throws if HEROKU_APP_ID is not set", () => { + delete process.env.HEROKU_APP_ID; + + expect(() => resolveAddonConfigByUrl("https://api.example.com")).to.throw( + "Heroku Applink app UUID not found" + ); + }); }); From 651e498db6bbd3736db44bbcad63c3895f8701d1 Mon Sep 17 00:00:00 2001 From: Matt Blewitt <118200221+mble-sfdc@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:40:39 +0100 Subject: [PATCH 5/5] get version from package.json --- src/utils/request.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils/request.ts b/src/utils/request.ts index 03f9ea3..942a060 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -5,6 +5,9 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import fs from "fs"; +import path from "path"; + /** Error thrown by the SDK when receiving non-2xx responses on HTTP requests. */ export class HTTPResponseError extends Error { response: any; @@ -19,9 +22,15 @@ export class HTTPResponseError extends Error { */ export class HttpRequestUtil { async request(url: string, opts: any, json = true) { + const pjson = fs.readFileSync( + path.join(__dirname, "..", "package.json"), + "utf8" + ); + const pkg = JSON.parse(pjson); + const defaultOpts = { headers: { - "User-Agent": `heroku-applink-node-sdk/1.0`, + "User-Agent": `heroku-applink-node-sdk/${pkg.version}`, }, };