-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable saving of Studio access token (#3235)
* add prototype for very simple setup of a Studio token * wire up new webview * wire up new actions * validate studio token input * switch to settings page if studio access token is saved * validate dvc.studioAccessToken via package json properties * add context value which hides view from tree when token is added * hide show webview command when studio is connected * switch from setting to using secrets store * revert changes in config key * fix enum * fix story * restructure connect class * add connect app tests * add integration tests for Connect * disallow direct access to secrets * create add studio access token command * apply review feedback
- Loading branch information
1 parent
716f569
commit a68ceba
Showing
33 changed files
with
639 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
"featurize", | ||
"hardlinks", | ||
"Interactors", | ||
"isat", | ||
"isdir", | ||
"isempty", | ||
"isequal", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { commands, ExtensionContext, SecretStorage } from 'vscode' | ||
import { validateTokenInput } from './inputBox' | ||
import { STUDIO_ACCESS_TOKEN_KEY, isStudioAccessToken } from './token' | ||
import { STUDIO_URL } from './webview/contract' | ||
import { Resource } from '../resourceLocator' | ||
import { ViewKey } from '../webview/constants' | ||
import { MessageFromWebview, MessageFromWebviewType } from '../webview/contract' | ||
import { BaseRepository } from '../webview/repository' | ||
import { Logger } from '../common/logger' | ||
import { getInput, getValidInput } from '../vscode/inputBox' | ||
import { Title } from '../vscode/title' | ||
import { openUrl } from '../vscode/external' | ||
import { ContextKey, setContextValue } from '../vscode/context' | ||
import { RegisteredCommands } from '../commands/external' | ||
import { showInformation } from '../vscode/modal' | ||
|
||
export class Connect extends BaseRepository<undefined> { | ||
public readonly viewKey = ViewKey.CONNECT | ||
|
||
private readonly secrets: SecretStorage | ||
|
||
constructor(context: ExtensionContext, webviewIcon: Resource) { | ||
super('', webviewIcon) | ||
|
||
this.secrets = context.secrets | ||
|
||
this.dispose.track( | ||
this.onDidReceivedWebviewMessage(message => | ||
this.handleMessageFromWebview(message) | ||
) | ||
) | ||
|
||
void this.setContext().then(() => this.deferred.resolve()) | ||
|
||
this.dispose.track( | ||
context.secrets.onDidChange(e => { | ||
if (e.key !== STUDIO_ACCESS_TOKEN_KEY) { | ||
return | ||
} | ||
return this.setContext() | ||
}) | ||
) | ||
} | ||
|
||
public sendInitialWebviewData(): void {} | ||
|
||
public removeStudioAccessToken() { | ||
return this.removeSecret(STUDIO_ACCESS_TOKEN_KEY) | ||
} | ||
|
||
public async saveStudioAccessToken() { | ||
const token = await getValidInput( | ||
Title.ENTER_STUDIO_TOKEN, | ||
validateTokenInput, | ||
{ password: true } | ||
) | ||
if (!token) { | ||
return | ||
} | ||
|
||
return this.storeSecret(STUDIO_ACCESS_TOKEN_KEY, token) | ||
} | ||
|
||
private handleMessageFromWebview(message: MessageFromWebview) { | ||
switch (message.type) { | ||
case MessageFromWebviewType.OPEN_STUDIO: | ||
return this.openStudio() | ||
case MessageFromWebviewType.OPEN_STUDIO_PROFILE: | ||
return this.openStudioProfile() | ||
case MessageFromWebviewType.SAVE_STUDIO_TOKEN: | ||
return commands.executeCommand( | ||
RegisteredCommands.ADD_STUDIO_ACCESS_TOKEN | ||
) | ||
default: | ||
Logger.error(`Unexpected message: ${JSON.stringify(message)}`) | ||
} | ||
} | ||
|
||
private openStudio() { | ||
return openUrl(STUDIO_URL) | ||
} | ||
|
||
private async openStudioProfile() { | ||
const username = await getInput(Title.ENTER_STUDIO_USERNAME) | ||
if (!username) { | ||
return | ||
} | ||
return openUrl(`${STUDIO_URL}/user/${username}/profile`) | ||
} | ||
|
||
private async setContext() { | ||
const storedToken = await this.getSecret(STUDIO_ACCESS_TOKEN_KEY) | ||
if (isStudioAccessToken(storedToken)) { | ||
if (this.deferred.state === 'resolved') { | ||
void showInformation( | ||
'Studio is now connected. Use the "Share to Studio" command from an experiment\'s context menu to share experiments.' | ||
) | ||
} | ||
this.webview?.dispose() | ||
return setContextValue(ContextKey.STUDIO_CONNECTED, true) | ||
} | ||
|
||
return setContextValue(ContextKey.STUDIO_CONNECTED, false) | ||
} | ||
|
||
private getSecret(key: string) { | ||
const secrets = this.getSecrets() | ||
return secrets.get(key) | ||
} | ||
|
||
private storeSecret(key: string, value: string) { | ||
const secrets = this.getSecrets() | ||
return secrets.store(key, value) | ||
} | ||
|
||
private removeSecret(key: string) { | ||
const secrets = this.getSecrets() | ||
return secrets.delete(key) | ||
} | ||
|
||
private getSecrets() { | ||
return this.secrets | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { validateTokenInput } from './inputBox' | ||
|
||
describe('validateTokenInput', () => { | ||
const mockedStudioAccessToken = | ||
'isat_1Z4T0zVHvq9Cu03XEe9Zjvx2vkBihfGPdY7FfmEMAagOXfQx' | ||
it('should return the warning if the input is not valid', () => { | ||
expect( | ||
validateTokenInput(mockedStudioAccessToken.slice(0, -1)) | ||
).not.toBeNull() | ||
expect( | ||
validateTokenInput( | ||
mockedStudioAccessToken.slice(1, mockedStudioAccessToken.length) | ||
) | ||
).not.toBeNull() | ||
}) | ||
|
||
it('should return null if the input is valid', () => { | ||
expect(validateTokenInput(mockedStudioAccessToken)).toBeNull() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { isStudioAccessToken } from './token' | ||
|
||
export const validateTokenInput = (input: string | undefined) => { | ||
if (!isStudioAccessToken(input)) { | ||
return 'please enter a valid Studio access token' | ||
} | ||
return null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Connect } from '.' | ||
import { RegisteredCommands } from '../commands/external' | ||
import { InternalCommands } from '../commands/internal' | ||
|
||
export const registerConnectCommands = ( | ||
connect: Connect, | ||
internalCommands: InternalCommands | ||
): void => { | ||
internalCommands.registerExternalCommand( | ||
RegisteredCommands.CONNECT_SHOW, | ||
() => connect.showWebview() | ||
) | ||
|
||
internalCommands.registerExternalCommand( | ||
RegisteredCommands.ADD_STUDIO_ACCESS_TOKEN, | ||
() => connect.saveStudioAccessToken() | ||
) | ||
|
||
internalCommands.registerExternalCommand( | ||
RegisteredCommands.REMOVE_STUDIO_ACCESS_TOKEN, | ||
() => connect.removeStudioAccessToken() | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const STUDIO_ACCESS_TOKEN_KEY = 'dvc.studioAccessToken' | ||
|
||
export const isStudioAccessToken = (text?: string): boolean => { | ||
if (!text) { | ||
return false | ||
} | ||
return text.startsWith('isat_') && text.length >= 53 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const STUDIO_URL = 'https://studio.iterative.ai' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.