-
Notifications
You must be signed in to change notification settings - Fork 29
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
Add Studio Token Auth Flow #4931
Conversation
jest.resetAllMocks() | ||
}) | ||
|
||
describe('openUrl', () => { |
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.
Went ahead and added a small test for openUrl
since we didn't seem to have one.
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.
'should handle a message from the webview to open Studio'
tested it indirectly (anything that used mockOpenExternal
tested the call to the same level as this). It seems like you've deleted all of those tests which means mockOpenExternal
can be removed from buildSetup
.
export const waitForUriResponse = (path: string, onResponse: () => unknown) => { | ||
window.registerUriHandler({ | ||
handleUri(uri: Uri): ProviderResult<void> { | ||
if (uri.path.startsWith(path)) { |
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.
Originally, I was going to check for uri.path === path
, but there's an issue with Studio redirecting an encoded version of our callback URL.
Instead of getting redirected to vscode://iterative.dvc/studio-complete-auth?windowId=2
, Studio will redirect the user to vscode://iterative.dvc/studio-complete-auth%253FwindowId=2
, which makes the path studio-complete-auth%253F
, failing an equals check.
I'm asking Thomas if Studio can redirect the user to an decoded version... Though now that I think about it, it should be a small change, I can probably open a pr myself 🤔
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.
For now, I'm using uri.path.startsWith
, but we can change it to an ===
check once Studio is updated.
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.
decodeURI()
does not work here?
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.
Yes, technically we could use decodeURI
here to check the path, but we need the user to be sent to the vscode URL with the windowId
query since I believe it helps VSCode to route the user correctly depending on certain circumstances.
We could use decodeURI
as a temporary solution instead of .startsWith()
though!
Looks good to me. @yathomasi @amritghimire we still need to review the messaging on the Studio side (e.g. Device Activation - etc - do we really need those low level terms?). Why do we need to click Continue, then Authorize - we don't need to buttons to my mind there ... Let's please one more time review it. @julieg18 we need to find a way to get rid of the pause at the end (feels like nothing is happening) or show some spinner (with some timeout). Otherwise it was a bit confusing. Good stuff. |
extension/src/setup/studio.test.ts
Outdated
import { Toast } from '../vscode/toast' | ||
|
||
const mockedFetch = jest.mocked(Fetch) | ||
const mockedFetchDefault = jest.fn() |
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.
Why not import fetch from 'node-fetch'
and
const mockedFetch = jest.mocked(fetch)
const mockedFetchDefault = jest.fn()
mockedFetch.mockIplementation(() => mockedFetchDefault)
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.
Hmmmm, this code gives me an error: mockedFetch.mockImplementation is not a function
. Maybe it's because because fetch is a function, not a source object?
const webview = await setup.showWebview() | ||
await webview.isReady() | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
stub(Setup.prototype as any, 'getCliCompatible').returns(true) |
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.
Why is this private in the first place?
private getCliCompatible() {
return this.cliCompatible
}
That does not seem to have any use since it's accessing a local variable. Is this code for tests only? Is there no other way to change the value? Why not set it public then? cc. @mattseddon (I think you're the one that added it)
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.
Is there no other way to change the value?
Yes, that is the reason that I remember... https://github.com/iterative/vscode-dvc/pull/3768/files#r1178629676
export const waitForUriResponse = (path: string, onResponse: () => unknown) => { | ||
window.registerUriHandler({ | ||
handleUri(uri: Uri): ProviderResult<void> { | ||
if (uri.path.startsWith(path)) { |
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.
decodeURI()
does not work here?
@@ -21,26 +18,13 @@ export const Connect: React.FC = () => { | |||
<a href="https://dvc.org/doc/studio/user-guide/projects-and-experiments/live-metrics-and-plots#set-up-an-access-token"> | |||
access token | |||
</a>{' '} | |||
generated from your Studio profile page. | |||
generated from your Studio profile page. Request a token below or{' '} | |||
<button className={styles.buttonAsLink} onClick={saveStudioToken}> |
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.
The style seems different than the previous link (buttonAsLink
might need an update, underline vs none). Also, it feels weird to have this link here (before the "Get Token" button that is mentioned before), I feel like it should be a button on the same level as the "Get Token" one.
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.
[Q] Why do we need this escape hatch? Will the endpoint always return a new token or does it fail if the user has one setup already?
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.
Also, it feels weird to have this link here (before the "Get Token" button that is mentioned before), I feel like it should be a button on the same level as the "Get Token" one.
Makes sense, I can move it to a secondary button.
[Q] Why do we need this escape hatch? Will the endpoint always return a new token or does it fail if the user has one setup already?
Yes, the endpoint always creates a new one (your studio profile can hold multiple now) which I why I wanted to leave the option to add an already created one. If preferred, I can remove the option though if we want to keep onboarding as simplified as possible.
extension/src/setup/studio.ts
Outdated
return fetchStudioToken(deviceCode, tokenUri, false) | ||
} | ||
return Toast.showError( | ||
'Unable to get token. Please try again later or add an already created token.' |
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.
[C] IMO, this should be a modal. The user will have no chance of missing it.
[Q] Is there a reason to retry this? Have you seen the process work on these second try? If we only try to contact the server once, we can (easily) provide details of the failure based on the response we get. If the call is unstable then we should be working with the Studio team, not trying to patch it up.
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.
[C] IMO, this should be a modal. The user will have no chance of missing it.
Good point!
[Q] Is there a reason to retry this? Have you seen the process work on these second try? If we only try to contact the server once, we can (easily) provide details of the failure based on the response we get. If the call is unstable then we should be working with the Studio team, not trying to patch it up.
Oh, you're right. When I first reviewed the Studio code I thought there was a chance of the token not being ready by the time the user is redirected, but I now see that this should work on the first try. I'll remove the retry attempt.
const webview = await setup.showWebview() | ||
await webview.isReady() | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
stub(Setup.prototype as any, 'getCliCompatible').returns(true) |
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.
Is there no other way to change the value?
Yes, that is the reason that I remember... https://github.com/iterative/vscode-dvc/pull/3768/files#r1178629676
jest.resetAllMocks() | ||
}) | ||
|
||
describe('openUrl', () => { |
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.
'should handle a message from the webview to open Studio'
tested it indirectly (anything that used mockOpenExternal
tested the call to the same level as this). It seems like you've deleted all of those tests which means mockOpenExternal
can be removed from buildSetup
.
@@ -21,26 +18,13 @@ export const Connect: React.FC = () => { | |||
<a href="https://dvc.org/doc/studio/user-guide/projects-and-experiments/live-metrics-and-plots#set-up-an-access-token"> | |||
access token | |||
</a>{' '} | |||
generated from your Studio profile page. | |||
generated from your Studio profile page. Request a token below or{' '} | |||
<button className={styles.buttonAsLink} onClick={saveStudioToken}> |
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.
[Q] Why do we need this escape hatch? Will the endpoint always return a new token or does it fail if the user has one setup already?
Alright, resolved/responded to all comments. What's left:
I'd like to do this one in a followup and merge this a first iteration on Monday :) |
Code Climate has analyzed commit a4e1baa and detected 0 issues on this pull request. The test coverage on the diff in this pull request is 100.0% (85% is the threshold). This pull request will bring the total coverage in the repository to 95.2% (0.0% change). View more on Code Climate. |
Demo
https://github.com/iterative/vscode-dvc/assets/43496356/c0b597c5-0a76-4481-94c3-fd356a124bd2Screen.Recording.2023-11-10.at.11.10.59.AM.mov
Part of #3864