Skip to content
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

#6656: Refresh partner token on failed requests #8466

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

johnnymetz
Copy link
Collaborator

@johnnymetz johnnymetz commented May 15, 2024

What does this PR do?

Discussion

Demo

https://www.loom.com/share/372cd8d5bacc4fa18ae2e5d847363204

Team Coordination

  • This PR introduces a new library: double check it's MIT/Apache2/permissively licensed

Checklist

  • Add jest or playwright tests and/or storybook stories
  • Designate a primary reviewer
  • Fix circular import

@johnnymetz johnnymetz self-assigned this May 15, 2024
Copy link

github-actions bot commented May 15, 2024

Playwright test results

failed  16 failed
passed  3 passed
flaky  2 flaky
skipped  37 skipped

Details

report  Open report ↗︎
stats  58 tests across 19 suites
duration  19 minutes, 56 seconds
commit  7f57274

Failed tests

chrome › tests/bricks/sidebarEffects.spec.ts › sidebar effect bricks › toggle sidebar brick
chrome › tests/extensionConsoleActivation.spec.ts › can activate a mod with built-in integration
chrome › tests/extensionConsoleActivation.spec.ts › can activate a mod with a database
chrome › tests/extensionConsoleActivation.spec.ts › activating a mod when the quickbar shortcut is not configured
chrome › tests/extensionConsoleActivation.spec.ts › can activate a mod via url
chrome › tests/pageEditor/saveMod.spec.ts › can save a standalone trigger mod
chrome › tests/pageEditor/saveMod.spec.ts › shows error notification when updating a public mod without incrementing the version
chrome › tests/regressions/doNotCloseSidebarOnPageEditorSave.spec.ts › #8104: Do not automatically close the sidebar when saving in the Page Editor
chrome › tests/regressions/formFlicker.spec.ts › forms flickering due to components unexpectedly unmounting/remounting › #8320: Search field loses focus while typing on snippet mod
chrome › tests/regressions/sidebarLinks.spec.ts › #8206: clicking links from the sidebar doesn't crash browser
chrome › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8157: can insert at cursor from side bar
chrome › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8154: can insert at cursor after opening sidebar from selection menu
chrome › tests/runtime/setInputValue.spec.ts › can set input value
chrome › tests/runtime/sidebarController.spec.ts › sidebar controller › can open sidebar immediately from iframe without focus dialog
chrome › tests/runtime/sidebarController.spec.ts › sidebar controller › shows focus dialog in top-level frame
chrome › tests/runtime/sidebarController.spec.ts › sidebar controller › prevents host page styles from leaking into dialog

Flaky tests

edgeSetup › auth.setup.ts › authenticate
chromeSetup › auth.setup.ts › authenticate

Skipped tests

edge › tests/bricks/sidebarEffects.spec.ts › sidebar effect bricks › toggle sidebar brick
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod with no config options
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod with built-in integration
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod with a database
edge › tests/extensionConsoleActivation.spec.ts › activating a mod when the quickbar shortcut is not configured
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod via url
edge › tests/pageEditor/saveMod.spec.ts › can save a standalone trigger mod
edge › tests/pageEditor/saveMod.spec.ts › shows error notification when updating a public mod without incrementing the version
edge › tests/regressions/doNotCloseSidebarOnPageEditorSave.spec.ts › #8104: Do not automatically close the sidebar when saving in the Page Editor
edge › tests/regressions/formFlicker.spec.ts › forms flickering due to components unexpectedly unmounting/remounting › #8320: Search field loses focus while typing on snippet mod
edge › tests/regressions/sidebarLinks.spec.ts › #8206: clicking links from the sidebar doesn't crash browser
edge › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8157: can insert at cursor from side bar
edge › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8154: can insert at cursor after opening sidebar from selection menu
edge › tests/runtime/localIntegrations.spec.ts › Local Integrations Page › can create a new integration
edge › tests/runtime/localIntegrations.spec.ts › Local Integrations Page › #8067: blank numeric text integration configuration field validated on save
edge › tests/runtime/setInputValue.spec.ts › can set input value
edge › tests/runtime/sidebarController.spec.ts › sidebar controller › can open sidebar immediately from iframe without focus dialog
edge › tests/runtime/sidebarController.spec.ts › sidebar controller › shows focus dialog in top-level frame
edge › tests/runtime/sidebarController.spec.ts › sidebar controller › prevents host page styles from leaking into dialog
chrome › tests/runtime/sidebarNavigation.spec.ts › sidebar mod panels are persistent during navigation
chrome › tests/runtime/sidebarNavigation.spec.ts › sidebar form and temporary panels are unavailable after navigation
edge › tests/runtime/sidebarNavigation.spec.ts › sidebar mod panels are persistent during navigation
edge › tests/runtime/sidebarNavigation.spec.ts › sidebar form and temporary panels are unavailable after navigation
chrome › tests/runtime/sidebarPanelTheme.spec.ts › custom sidebar theme css file is applied to all levels of sidebar document
edge › tests/runtime/sidebarPanelTheme.spec.ts › custom sidebar theme css file is applied to all levels of sidebar document
chrome › tests/runtime/srcdocFrames.spec.ts › 8143: mods can run in srcdoc iframes
edge › tests/runtime/srcdocFrames.spec.ts › 8143: mods can run in srcdoc iframes
chrome › tests/smoke/modsPage.spec.ts › extension console mods page smoke test › can view available mods
edge › tests/smoke/modsPage.spec.ts › extension console mods page smoke test › can view available mods
chrome › tests/smoke/pageEditor.spec.ts › page editor smoke test › can open the page editor and connect to an open tab
edge › tests/smoke/pageEditor.spec.ts › page editor smoke test › can open the page editor and connect to an open tab
chrome › tests/smoke/sidebar.spec.ts › sidebar page smoke test › can open the sidebar from selection menu action and view the related mod's sidebar panel
edge › tests/smoke/sidebar.spec.ts › sidebar page smoke test › can open the sidebar from selection menu action and view the related mod's sidebar panel
chrome › tests/smoke/workshopPage.spec.ts › extension console workshop smoke test › can navigate to workshop page without a username
edge › tests/smoke/workshopPage.spec.ts › extension console workshop smoke test › can navigate to workshop page without a username
chrome › tests/telemetry/errors.spec.ts › can report application error to telemetry service
edge › tests/telemetry/errors.spec.ts › can report application error to telemetry service

Copy link

github-actions bot commented May 15, 2024

Playwright test results - MV2

failed  18 failed
passed  9 passed
skipped  29 skipped

Details

report  Open report ↗︎
stats  56 tests across 19 suites
duration  19 minutes, 53 seconds
commit  8243a1b

Failed tests

chrome › tests/bricks/sidebarEffects.spec.ts › sidebar effect bricks › toggle sidebar brick
edge › tests/bricks/sidebarEffects.spec.ts › sidebar effect bricks › toggle sidebar brick
chrome › tests/extensionConsoleActivation.spec.ts › can activate a mod with a database
chrome › tests/extensionConsoleActivation.spec.ts › activating a mod when the quickbar shortcut is not configured
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod with a database
edge › tests/extensionConsoleActivation.spec.ts › activating a mod when the quickbar shortcut is not configured
chrome › tests/pageEditor/saveMod.spec.ts › can save a standalone trigger mod
chrome › tests/pageEditor/saveMod.spec.ts › shows error notification when updating a public mod without incrementing the version
chrome › tests/regressions/doNotCloseSidebarOnPageEditorSave.spec.ts › #8104: Do not automatically close the sidebar when saving in the Page Editor
chrome › tests/regressions/formFlicker.spec.ts › forms flickering due to components unexpectedly unmounting/remounting › #8320: Search field loses focus while typing on snippet mod
chrome › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8157: can insert at cursor from side bar
chrome › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8154: can insert at cursor after opening sidebar from selection menu
chrome › tests/runtime/setInputValue.spec.ts › can set input value
chrome › tests/runtime/sidebarController.spec.ts › sidebar controller › can open sidebar immediately from iframe without focus dialog
chrome › tests/runtime/sidebarPanelTheme.spec.ts › custom sidebar theme css file is applied to all levels of sidebar document
chrome › tests/runtime/srcdocFrames.spec.ts › 8143: mods can run in srcdoc iframes
chrome › tests/smoke/pageEditor.spec.ts › page editor smoke test › can open the page editor and connect to an open tab
chrome › tests/smoke/sidebar.spec.ts › sidebar page smoke test › can open the sidebar from selection menu action and view the related mod's sidebar panel

Skipped tests

chrome › tests/extensionConsoleActivation.spec.ts › can activate a mod with built-in integration
chrome › tests/extensionConsoleActivation.spec.ts › can activate a mod via url
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod with built-in integration
edge › tests/extensionConsoleActivation.spec.ts › can activate a mod via url
edge › tests/pageEditor/saveMod.spec.ts › can save a standalone trigger mod
edge › tests/pageEditor/saveMod.spec.ts › shows error notification when updating a public mod without incrementing the version
edge › tests/regressions/doNotCloseSidebarOnPageEditorSave.spec.ts › #8104: Do not automatically close the sidebar when saving in the Page Editor
edge › tests/regressions/formFlicker.spec.ts › forms flickering due to components unexpectedly unmounting/remounting › #8320: Search field loses focus while typing on snippet mod
chrome › tests/regressions/sidebarLinks.spec.ts › #8206: clicking links from the sidebar doesn't crash browser
edge › tests/regressions/sidebarLinks.spec.ts › #8206: clicking links from the sidebar doesn't crash browser
edge › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8157: can insert at cursor from side bar
edge › tests/runtime/insertAtCursor.spec.ts › Insert at Cursor › 8154: can insert at cursor after opening sidebar from selection menu
edge › tests/runtime/localIntegrations.spec.ts › Local Integrations Page › can create a new integration
edge › tests/runtime/localIntegrations.spec.ts › Local Integrations Page › #8067: blank numeric text integration configuration field validated on save
edge › tests/runtime/setInputValue.spec.ts › can set input value
chrome › tests/runtime/sidebarController.spec.ts › sidebar controller › shows focus dialog in top-level frame
edge › tests/runtime/sidebarController.spec.ts › sidebar controller › can open sidebar immediately from iframe without focus dialog
edge › tests/runtime/sidebarController.spec.ts › sidebar controller › shows focus dialog in top-level frame
chrome › tests/runtime/sidebarNavigation.spec.ts › sidebar mod panels are persistent during navigation
chrome › tests/runtime/sidebarNavigation.spec.ts › sidebar form and temporary panels are unavailable after navigation
edge › tests/runtime/sidebarNavigation.spec.ts › sidebar mod panels are persistent during navigation
edge › tests/runtime/sidebarNavigation.spec.ts › sidebar form and temporary panels are unavailable after navigation
edge › tests/runtime/sidebarPanelTheme.spec.ts › custom sidebar theme css file is applied to all levels of sidebar document
edge › tests/runtime/srcdocFrames.spec.ts › 8143: mods can run in srcdoc iframes
edge › tests/smoke/modsPage.spec.ts › extension console mods page smoke test › can view available mods
edge › tests/smoke/pageEditor.spec.ts › page editor smoke test › can open the page editor and connect to an open tab
edge › tests/smoke/sidebar.spec.ts › sidebar page smoke test › can open the sidebar from selection menu action and view the related mod's sidebar panel
edge › tests/smoke/workshopPage.spec.ts › extension console workshop smoke test › can navigate to workshop page without a username
edge › tests/telemetry/errors.spec.ts › can report application error to telemetry service

/**
* Refresh an Automation Anywhere JWT. NOOP if a JWT refresh token is not available.
*/
export async function _refreshPartnerToken(): Promise<void> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to a new file to prevent a circular import

@@ -253,31 +254,6 @@ async function proxyRequest<T>(
};
}

function isAuthenticationError(error: Pick<AxiosError, "response">): boolean {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to apiClient.ts

@@ -60,15 +93,7 @@ export async function getLinkedApiClient(): Promise<AxiosInstance> {
throw new ExtensionNotLinkedError();
}

return axios.create({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was exactly the same as the code in getApiClient so I'm using that to DRY out the code. And so we don't have to create the interceptor multiple times, see createAuthRefreshInterceptor.

We may actually want to merge getApiClient and getLinkedApiClient into a single function (and introduce a flag for throwing an error if the extension is not linked) because they're the same besides that check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may actually want to merge getApiClient and getLinkedApiClient into a single function (and introduce a flag for throwing an error if the extension is not linked) because they're the same besides that check.

I had them as two separate methods so the intent is explicit. I'll defer to engineering on their preference, but my preference would be to keep them separate. (If we do use a single method, I'd want the flag to be required)

@johnnymetz johnnymetz force-pushed the feature/6656-refresh-partner-token-on-failed-requests branch from a8d0ba6 to fbbcfbe Compare May 16, 2024 00:26
@twschiller twschiller added enhancement New feature or request user experience Improve the user experience (UX) labels May 16, 2024
@BLoe
Copy link
Collaborator

BLoe commented May 17, 2024

There's a fundamental architecture problem here that we probably need to re-think. The api client auth interceptor is trying to use the partner integration to refresh it's auth token, but that calls the integrations registry, which tries to refresh the remote registry, which tries to use the same api client (the same code, different call to getApiClient(), so new instance/copy) to fetch from /api/services/shared/ to refresh the registry... But, the client instance has the auth interceptor code in the first place, so we're stuck in a bad import loop here.

I'm also pretty concerned that we're configuring and instantiating a new instance of api client on every call to getApiClient(), why are we doing that? Shouldn't the API client instance be a long-lived singleton in the background script?

@twschiller
Copy link
Contributor

twschiller commented May 19, 2024

There's a fundamental architecture problem here that we probably need to re-think

That's a good callout. Some thoughts:

  • If the partner token is bad, the user must have configured it, which means the integration definition is most likely on their device. We could only retry with whatever definition is on their device (and provide a param to not allow remote fetching)
  • Even remote fetching "might" be OK. The integration definition is publicly visible, so it would come back from an unauthenticated request. However, by making that unauthenticated request, the user would lose any of their team bricks due to the syncing. But, given that in this scenario assumes the integration definition is missing, their other team bricks would most likely be missing as well

I'm also pretty concerned that we're configuring and instantiating a new instance of api client on every call to getApiClient()

Could you clarify your concern - is it performance? There should be negligible overhead for creating new instances. Whether or not it's a single seems to be an implementation detail to me. The caller doesn't care, it just needs to be able to get a client that meets its requirements (i.e., whether it needs to make an authenticated call)

@johnnymetz @grahamlangford let's make sure engineering is aligned on the architecture concerns before going too far down the particular implementation

@BLoe
Copy link
Collaborator

BLoe commented May 20, 2024

Could you clarify your concern

My concern is just that it feels like a very non-standard way to use an API client. I would have to look into it, but my instinct is that there may be some sort of caching happening in the axios instance that we're losing by doing this. I'm also concerned that, although it may seem negligible now, the more api calls our application makes the more impact this will have will creating all the new objects every time there's an api call. I'm concerned that this is a "foot-gun" that we're just leaving in the product because it doesn't seem like it makes a difference one way or another right now. Is there a chance this would be a memory leak with all the instances if you use the extension over a long period of time on one session? I'm not sure I see what the difference is with just changing this function into a module constant in the same place? If there's a reason we should leave it like this I'm open to discussion though. Can also be addressed separate from this refresh token PR for sure.

@BLoe
Copy link
Collaborator

BLoe commented May 20, 2024

If the partner token is bad, the user must have configured it, which means the integration definition is most likely on their device. We could only retry with whatever definition is on their device (and provide a param to not allow remote fetching)

Yeah, I think this is probably what we'll have to do for now, unless we want to save the entire config somewhere that's associated with the refresh token. That, to me, would be in the spirit of the concept of a "refresh token" where it's something that can be used to start a new auth session. But it sort of goes against our registry concept, and creates a place where the stored config could get out of sync with the registry possibly.

We'll probably need to split out a separate locator for "local integration config locator" or something so that we can decouple it from anything that uses getApiClient().

@twschiller
Copy link
Contributor

twschiller commented May 21, 2024

My concern is just that it feels like a very non-standard way to use an API client. I would have to look into it, but my instinct is that there may be some sort of caching happening in the axios instance that we're losing by doing this.

Someone had asked in the axios project and there's no mention of performance considerations: axios/axios#3365

Agree it makes sense to understand if there's any tradeoff. My instinct is that caching is done by the browser, not axios (fwiw, there is a separate caching adapter https://www.npmjs.com/package/axios-cache-adapter)

Is there a chance this would be a memory leak with all the instances if you use the extension over a long period of time on one session?

I wouldn't bet on it currently. We've been using the existing approach for 2+ years. But yes, there's always a chance we might introduce something that keeps a reference to the client instance

I'm not sure I see what the difference is with just changing this function into a module constant in the same place?

As mentioned, I'm fine using a singleton. I just don't see any urgency around it (engineering can prioritize for DevEx). At the end of the day, the caller cares about:

  • Getting a client to make a call with
  • Currently, the caller also asserts whether the client can authenticate the request (vs. making the call and solely depending on a 401/403)

@BLoe
Copy link
Collaborator

BLoe commented May 21, 2024

As mentioned, I'm fine using a singleton. I just don't see any urgency around it (engineering can prioritize for DevEx).

Sure, probably not any urgency related to anything broken, I agree

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request user experience Improve the user experience (UX)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update partner refresh token logic to fetch new access token when expired
3 participants