-
Notifications
You must be signed in to change notification settings - Fork 0
accept attachment name or url to get addon config #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,44 +8,49 @@ | |
import { HttpRequestUtil } from "../utils/request"; | ||
import { OrgImpl } from "../sdk/org"; | ||
import { Org } from "../index"; | ||
import { | ||
resolveAddonConfigByAttachment, | ||
resolveAddonConfigByUrl, | ||
} from "~/utils/addon-config"; | ||
|
||
const HTTP_REQUEST = new HttpRequestUtil(); | ||
|
||
/** | ||
* Get stored Salesforce or Data Cloud org user credentials for given name or alias. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* @param name or alias | ||
* @param attachmentNameOrUrl Either an attachment name (e.g. "APPLINK") or a full URL | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this should be HEROKU_APPLINK since that's the default attachment iirc. |
||
* @returns Org | ||
*/ | ||
export async function getConnection(name: string): Promise<Org> { | ||
export async function getAuthorization( | ||
name: string, | ||
attachmentNameOrUrl = "APPLINK" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
): Promise<Org> { | ||
if (!name) { | ||
throw Error(`Connection name not provided`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
const addonEndpoint = | ||
process.env.HEROKU_APPLINK_API_URL || | ||
process.env.HEROKU_APPLINK_STAGING_API_URL; | ||
if (!addonEndpoint) { | ||
throw Error( | ||
`Heroku Applink add-on not provisioned on app or endpoint not provided` | ||
); | ||
// Check if the attachmentNameOrUrl is a URL by attempting to parse it | ||
let resolveConfigByUrl = false; | ||
try { | ||
new URL(attachmentNameOrUrl); | ||
resolveConfigByUrl = true; | ||
} catch { | ||
resolveConfigByUrl = false; | ||
} | ||
|
||
const addonAuthToken = process.env.HEROKU_APPLINK_TOKEN; | ||
if (!addonAuthToken) { | ||
throw Error( | ||
`Heroku Applink add-on not provisioned on app or authorization token not found` | ||
); | ||
} | ||
const config = resolveConfigByUrl | ||
? resolveAddonConfigByUrl(attachmentNameOrUrl) | ||
: resolveAddonConfigByAttachment(attachmentNameOrUrl); | ||
|
||
const authUrl = `${addonEndpoint}/invocations/authorization`; | ||
const authUrl = `${config.apiUrl}/invocations/authorization`; | ||
const opts = { | ||
method: "POST", | ||
headers: { | ||
Authorization: `Bearer ${addonAuthToken}`, | ||
Authorization: `Bearer ${config.token}`, | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
org_name: name, | ||
developer_name: name, | ||
}), | ||
retry: { | ||
limit: 1, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I'll handle that in a separate pr to avoid noise. |
||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
interface AddonConfig { | ||
apiUrl: string; | ||
token: string; | ||
} | ||
|
||
export function resolveAddonConfigByAttachment( | ||
attachment = "HEROKU_APPLINK" | ||
): AddonConfig { | ||
const apiUrl = process.env[`${attachment}_API_URL`]; | ||
const token = process.env[`${attachment}_TOKEN`]; | ||
|
||
if (!apiUrl || !token) { | ||
throw Error( | ||
`Heroku Applink config not found under attachment ${attachment}` | ||
); | ||
} | ||
|
||
return { | ||
apiUrl, | ||
token, | ||
}; | ||
} | ||
|
||
export function resolveAddonConfigByUrl(url: string): AddonConfig { | ||
// Find the environment variable ending with _API_URL that matches the given URL | ||
const envVarEntries = Object.entries(process.env); | ||
const matchingApiUrlEntry = envVarEntries.find( | ||
([key, value]) => key.endsWith("_API_URL") && value === url | ||
); | ||
|
||
if (!matchingApiUrlEntry) { | ||
throw Error(`Heroku Applink config not found for API URL: ${url}`); | ||
} | ||
|
||
// Extract the prefix from the API_URL environment variable name | ||
const [envVarName] = matchingApiUrlEntry; | ||
const prefix = envVarName.slice(0, -"_API_URL".length); // Remove '_API_URL' suffix | ||
|
||
// Look for corresponding token | ||
const token = process.env[`${prefix}_TOKEN`]; | ||
if (!token) { | ||
throw Error(`Heroku Applink config not found for API URL: ${url}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Token not found? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No strong feelings here right now. I can change to reference the missing token config var explicitly. |
||
} | ||
|
||
return { | ||
apiUrl: url, | ||
token, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import { expect } from "chai"; | ||
import { | ||
resolveAddonConfigByAttachment, | ||
resolveAddonConfigByUrl, | ||
} from "../../src/utils/addon-config"; | ||
|
||
describe("resolveAddonConfigByAttachment", () => { | ||
let originalEnv: NodeJS.ProcessEnv; | ||
|
||
beforeEach(() => { | ||
originalEnv = { ...process.env }; | ||
}); | ||
|
||
afterEach(() => { | ||
process.env = originalEnv; | ||
}); | ||
|
||
it("gets config for default HEROKU_APPLINK attachment", () => { | ||
process.env.HEROKU_APPLINK_API_URL = "https://api.example.com"; | ||
process.env.HEROKU_APPLINK_TOKEN = "default-token"; | ||
|
||
const config = resolveAddonConfigByAttachment(); | ||
expect(config.apiUrl).to.equal("https://api.example.com"); | ||
expect(config.token).to.equal("default-token"); | ||
}); | ||
|
||
it("gets config for specified attachment", () => { | ||
process.env.CUSTOM_API_URL = "https://custom.example.com"; | ||
process.env.CUSTOM_TOKEN = "custom-token"; | ||
|
||
const config = resolveAddonConfigByAttachment("CUSTOM"); | ||
expect(config.apiUrl).to.equal("https://custom.example.com"); | ||
expect(config.token).to.equal("custom-token"); | ||
}); | ||
|
||
it("throws if API_URL config not found", () => { | ||
process.env.APPLINK_TOKEN = "token"; | ||
// APPLINK_API_URL intentionally not set | ||
|
||
expect(() => resolveAddonConfigByAttachment()).to.throw( | ||
"Heroku Applink config not found under attachment HEROKU_APPLINK" | ||
); | ||
}); | ||
|
||
it("throws if TOKEN config not found", () => { | ||
process.env.HEROKU_APPLINK_API_URL = "https://api.example.com"; | ||
// APPLINK_TOKEN intentionally not set | ||
|
||
expect(() => resolveAddonConfigByAttachment()).to.throw( | ||
"Heroku Applink config not found under attachment HEROKU_APPLINK" | ||
); | ||
}); | ||
}); | ||
|
||
describe("resolveAddonConfigByUrl", () => { | ||
let originalEnv: NodeJS.ProcessEnv; | ||
|
||
beforeEach(() => { | ||
originalEnv = { ...process.env }; | ||
}); | ||
|
||
afterEach(() => { | ||
process.env = originalEnv; | ||
}); | ||
|
||
it("finds config for matching URL", () => { | ||
const testUrl = "https://api.example.com"; | ||
process.env.HEROKU_APPLINK_API_URL = testUrl; | ||
process.env.HEROKU_APPLINK_TOKEN = "test-token"; | ||
|
||
const config = resolveAddonConfigByUrl(testUrl); | ||
expect(config.apiUrl).to.equal(testUrl); | ||
expect(config.token).to.equal("test-token"); | ||
}); | ||
|
||
it("finds config for matching URL with custom prefix", () => { | ||
const testUrl = "https://custom.example.com"; | ||
process.env.CUSTOM_ATTACHMENT_API_URL = testUrl; | ||
process.env.CUSTOM_ATTACHMENT_TOKEN = "custom-token"; | ||
|
||
const config = resolveAddonConfigByUrl(testUrl); | ||
expect(config.apiUrl).to.equal(testUrl); | ||
expect(config.token).to.equal("custom-token"); | ||
}); | ||
|
||
it("throws if no matching URL is found", () => { | ||
const testUrl = "https://nonexistent.example.com"; | ||
|
||
expect(() => resolveAddonConfigByUrl(testUrl)).to.throw( | ||
`Heroku Applink config not found for API URL: ${testUrl}` | ||
); | ||
}); | ||
|
||
it("throws if matching URL found but no corresponding token", () => { | ||
const testUrl = "https://api.example.com"; | ||
process.env.SOME_API_URL = testUrl; | ||
// SOME_TOKEN intentionally not set | ||
|
||
expect(() => resolveAddonConfigByUrl(testUrl)).to.throw( | ||
`Heroku Applink config not found for API URL: ${testUrl}` | ||
); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might need to rethink the example a bit here given the change to use a developer name instead of org name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, agree. Best to be consistent.