From 971a46d4c6ca47696aacd6ea19b375c4ac1881f0 Mon Sep 17 00:00:00 2001 From: Krishna Acondy Date: Mon, 1 Mar 2021 09:13:02 +0000 Subject: [PATCH 1/2] feat(*): add execute code button, add default targets --- package-lock.json | 6 +- package.json | 4 +- .../execute-code/ExecuteCodeCommand.ts | 108 ++++++++++++++ .../execute-code/internal/configuration.ts | 88 +++++++++++ src/commands/execute-code/internal/utils.ts | 7 + src/executeCode.ts | 138 ------------------ src/extension.ts | 12 +- src/utils/input.ts | 13 +- tsconfig.json | 1 + 9 files changed, 221 insertions(+), 156 deletions(-) create mode 100644 src/commands/execute-code/ExecuteCodeCommand.ts create mode 100644 src/commands/execute-code/internal/configuration.ts create mode 100644 src/commands/execute-code/internal/utils.ts delete mode 100644 src/executeCode.ts diff --git a/package-lock.json b/package-lock.json index 6558601..77f1827 100644 --- a/package-lock.json +++ b/package-lock.json @@ -240,9 +240,9 @@ } }, "@sasjs/utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.3.0.tgz", - "integrity": "sha512-jJgQup1v8cAJKtUOCkkKyOE+AYZhnWI9SneCM3PcckQ3IZu4Tq9+l5NHqNm4KdGLaOsJUPJuw+9JH267O4kmZg==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.6.1.tgz", + "integrity": "sha512-yyWGIb9ntR3Z4HoVkFo0WQzdsIhAsIL0mvQ5w12AHJ6UYTa6HHvC999KM+ZSENsRkPORp9alxOFquJ2NoI1Rxg==", "requires": { "@types/prompts": "^2.0.9", "consola": "^2.15.0", diff --git a/package.json b/package.json index 083cf7d..5533b14 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "Other" ], "activationEvents": [ - "onCommand:sasjs-for-vscode.executeCode" + "*" ], "main": "./out/extension.js", "contributes": { @@ -86,7 +86,7 @@ }, "dependencies": { "@sasjs/adapter": "^2.1.0", - "@sasjs/utils": "^2.3.0", + "@sasjs/utils": "^2.6.1", "valid-url": "^1.0.9" } } diff --git a/src/commands/execute-code/ExecuteCodeCommand.ts b/src/commands/execute-code/ExecuteCodeCommand.ts new file mode 100644 index 0000000..d249e9b --- /dev/null +++ b/src/commands/execute-code/ExecuteCodeCommand.ts @@ -0,0 +1,108 @@ +import SASjs from '@sasjs/adapter/node' +import * as os from 'os' +import * as path from 'path' +import { + window, + ExtensionContext, + commands, + StatusBarAlignment, + OutputChannel, + ViewColumn, + workspace, + StatusBarItem +} from 'vscode' +import { getEditorContent } from '../../utils/editor' +import { createFile } from '../../utils/file' +import { selectTarget, getAccessToken } from './internal/configuration' +import { getTimestamp } from './internal/utils' + +export class ExecuteCodeCommand { + private outputChannel: OutputChannel + private runSasCodeButton: StatusBarItem + + constructor(private context: ExtensionContext) { + this.outputChannel = window.createOutputChannel('SASjs') + } + + initialise = () => { + const executeCodeCommand = commands.registerCommand( + 'sasjs-for-vscode.executeCode', + () => this.executeCode() + ) + this.context.subscriptions.push(executeCodeCommand) + + this.initialiseRunSasCodeButton() + } + + private initialiseRunSasCodeButton = () => { + this.runSasCodeButton = window.createStatusBarItem( + StatusBarAlignment.Right, + 100 + ) + this.runSasCodeButton.text = '$(notebook-execute) Run SAS code' + this.runSasCodeButton.command = 'sasjs-for-vscode.executeCode' + this.runSasCodeButton.show() + this.context.subscriptions.push(this.runSasCodeButton) + } + + private executeCode = async () => { + this.outputChannel.appendLine('Initialising SASjs.') + const target = await selectTarget(this.outputChannel) + if (!target) { + return + } + const accessToken = await getAccessToken(target) + const currentFileContent = getEditorContent() + + const adapter = new SASjs({ + serverUrl: target.serverUrl, + serverType: target.serverType, + appLoc: target.appLoc, + contextName: target.contextName, + useComputeApi: true, + debug: true + }) + + this.runSasCodeButton.text = '$(sync~spin) Running your SAS code' + adapter + .executeScriptSASViya( + 'vscode-test-exec', + (currentFileContent || '').split('\n'), + '', + accessToken + ) + .then(async (res) => { + this.runSasCodeButton.text = + '$(notebook-state-success) SAS code executed successfully!' + + const timeout = setTimeout(() => { + this.runSasCodeButton.text = '$(notebook-execute) Run SAS code' + clearTimeout(timeout) + }, 3000) + const timestamp = getTimestamp() + const resultsPath = workspace.workspaceFolders?.length + ? path.join( + workspace.workspaceFolders![0].uri.fsPath, + 'sasjsresults', + `${timestamp}.log` + ) + : path.join(os.homedir(), 'sasjsresults', `${timestamp}.log`) + await createFile(resultsPath, res.log) + const document = await workspace.openTextDocument(resultsPath) + window.showTextDocument(document, { + viewColumn: ViewColumn.Beside + }) + + this.outputChannel.append(JSON.stringify(res, null, 2)) + }) + .catch((e) => { + this.runSasCodeButton.text = + '$(notebook-state-error) SAS code execution failed' + const timeout = setTimeout(() => { + this.runSasCodeButton.text = '$(notebook-execute) Run SAS code' + clearTimeout(timeout) + }, 3000) + this.outputChannel.append(JSON.stringify(e, null, 2)) + }) + } +} diff --git a/src/commands/execute-code/internal/configuration.ts b/src/commands/execute-code/internal/configuration.ts new file mode 100644 index 0000000..ced540e --- /dev/null +++ b/src/commands/execute-code/internal/configuration.ts @@ -0,0 +1,88 @@ +import { OutputChannel, env, Uri } from 'vscode' + +import SASjs from '@sasjs/adapter/node' +import { Target } from '@sasjs/utils/types' +import { + getAuthCode, + getChoiceInput, + getClientId, + getClientSecret, + getIsDefault, + getServerType, + getServerUrl, + getTargetName +} from '../../../utils/input' +import { getAuthUrl, getTokens } from '../../../utils/auth' +import { + getGlobalConfiguration, + saveToGlobalConfig +} from '../../../utils/config' + +export const selectTarget = async (outputChannel: OutputChannel) => { + const config = await getGlobalConfiguration(outputChannel) + + if (config?.targets?.length) { + let targetName = config.targets.find((t: Target) => t.isDefault)?.name + if (!targetName) { + const targetNames = (config?.targets || []).map((t: any) => t.name) + targetName = await getChoiceInput(targetNames, 'Please select a target') + } + + if (!targetName) { + return + } + + const selectedTarget = config.targets.find( + (t: any) => t.name === targetName + ) + return new Target(selectedTarget) + } else { + return await createTarget(outputChannel) + } +} + +export const createTarget = async (outputChannel: OutputChannel) => { + const name = await getTargetName() + const serverUrl = await getServerUrl() + const serverType = await getServerType() + const clientId = await getClientId() + + const clientSecret = await getClientSecret() + + env.openExternal(Uri.parse(getAuthUrl(serverUrl, clientId))) + + const authCode = await getAuthCode() + + const adapter = new SASjs({ + serverUrl: serverUrl, + serverType: serverType, + appLoc: '/Public/app', + useComputeApi: true, + debug: true + }) + + const authResponse = await getTokens( + adapter, + clientId, + clientSecret, + authCode + ) + + const isDefault = await getIsDefault() + const target = new Target({ + name, + serverUrl, + serverType, + appLoc: '/Public/app', + authConfig: authResponse, + isDefault + }) + + await saveToGlobalConfig(target, outputChannel) + + return target +} + +export const getAccessToken = async (target: Target) => { + return target.authConfig?.access_token +} diff --git a/src/commands/execute-code/internal/utils.ts b/src/commands/execute-code/internal/utils.ts new file mode 100644 index 0000000..5be73d3 --- /dev/null +++ b/src/commands/execute-code/internal/utils.ts @@ -0,0 +1,7 @@ +import { timestampToYYYYMMDDHHMMSS } from '@sasjs/utils/time' + +export const getTimestamp = () => + timestampToYYYYMMDDHHMMSS() + .replace(/ /g, '') + .replace(/\//g, '') + .replace(/:/g, '') diff --git a/src/executeCode.ts b/src/executeCode.ts deleted file mode 100644 index dbdd93f..0000000 --- a/src/executeCode.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { OutputChannel, window, workspace, env, Uri, ViewColumn } from 'vscode' -import * as path from 'path' - -import SASjs from '@sasjs/adapter/node' -import { Target } from '@sasjs/utils/types' -import { timestampToYYYYMMDDHHMMSS } from '@sasjs/utils/time' -import { createFile } from './utils/file' -import { - getAuthCode, - getChoiceInput, - getClientId, - getClientSecret, - getServerType, - getServerUrl, - getTargetName -} from './utils/input' -import { getAuthUrl, getTokens } from './utils/auth' -import { getEditorContent } from './utils/editor' -import { getGlobalConfiguration, saveToGlobalConfig } from './utils/config' - -export const executeCode = async (outputChannel: OutputChannel) => { - outputChannel.appendLine('Initialising SASjs.') - const target = await selectTarget(outputChannel) - const accessToken = await getAccessToken(target) - const currentFileContent = getEditorContent() - - const adapter = new SASjs({ - serverUrl: target.serverUrl, - serverType: target.serverType, - appLoc: target.appLoc, - contextName: target.contextName, - useComputeApi: true, - debug: true - }) - - adapter - .executeScriptSASViya( - 'vscode-test-exec', - (currentFileContent || '').split('\n'), - '', - accessToken - ) - .then(async (res) => { - const timestamp = timestampToYYYYMMDDHHMMSS() - .replace(/ /g, '') - .replace(/\//g, '') - .replace(/:/g, '') - const resultsPath = path.join( - workspace.workspaceFolders![0].uri.fsPath, - 'results', - `${timestamp}.log` - ) - await createFile(resultsPath, res.log) - const document = await workspace.openTextDocument(resultsPath) - window.showTextDocument(document, { - viewColumn: ViewColumn.Beside - }) - - outputChannel.append(JSON.stringify(res, null, 2)) - window.showInformationMessage( - `Your request has executed successfully! The log is available in ${ - workspace.workspaceFolders![0].uri.path - }/results/test.log`, - { modal: true } - ) - }) - .catch((e) => { - outputChannel.append(JSON.stringify(e, null, 2)) - }) -} - -const selectTarget = async (outputChannel: OutputChannel) => { - const config = await getGlobalConfiguration(outputChannel) - - if (config?.targets?.length) { - const targetNames = (config?.targets || []).map((t: any) => t.name) - const targetName = await getChoiceInput( - targetNames, - 'Please select a target' - ) - - const selectedTarget = config.targets.find( - (t: any) => t.name === targetName - ) - return new Target(selectedTarget) - } else { - return await createTarget(outputChannel) - } -} - -const createTarget = async (outputChannel: OutputChannel) => { - const name = await getTargetName() - const serverUrl = await getServerUrl() - const serverType = await getServerType() - const clientId = await getClientId() - - const clientSecret = await getClientSecret() - - env.openExternal(Uri.parse(getAuthUrl(serverUrl, clientId))) - - const authCode = await getAuthCode() - - const adapter = new SASjs({ - serverUrl: serverUrl, - serverType: serverType, - appLoc: '/Public/app', - useComputeApi: true, - debug: true - }) - - const authResponse = await getTokens( - adapter, - clientId, - clientSecret, - authCode - ) - - const target = new Target({ - name, - serverUrl, - serverType, - appLoc: '/Public/app', - authConfig: { - // eslint-disable-next-line @typescript-eslint/naming-convention - access_token: authResponse.access_token, - // eslint-disable-next-line @typescript-eslint/naming-convention - refresh_token: authResponse.refresh_token - } - }) - - await saveToGlobalConfig(target, outputChannel) - - return target -} - -const getAccessToken = async (target: Target) => { - return target.authConfig?.access_token -} diff --git a/src/extension.ts b/src/extension.ts index d64c7de..8139ae3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,15 +1,9 @@ import * as vscode from 'vscode' -import { executeCode } from './executeCode' +import { ExecuteCodeCommand } from './commands/execute-code/ExecuteCodeCommand' export function activate(context: vscode.ExtensionContext) { - const outputChannel = vscode.window.createOutputChannel('SASjs') - - const executeCodeCommand = vscode.commands.registerCommand( - 'sasjs-for-vscode.executeCode', - () => executeCode(outputChannel) - ) - - context.subscriptions.push(executeCodeCommand) + const executeCodeCommand = new ExecuteCodeCommand(context) + executeCodeCommand.initialise() } export function deactivate() {} diff --git a/src/utils/input.ts b/src/utils/input.ts index 3cec384..2ad72ee 100644 --- a/src/utils/input.ts +++ b/src/utils/input.ts @@ -96,9 +96,14 @@ export const getChoiceInput = async ( ignoreFocusOut: true }) - if (!input) { - throw new Error('Input is invalid.') - } - return input } + +export const getIsDefault = async () => { + const isDefault = await window.showQuickPick(['Yes', 'No'], { + placeHolder: 'Would you like to set this as the default target?', + ignoreFocusOut: true + }) + + return isDefault === 'Yes' +} diff --git a/tsconfig.json b/tsconfig.json index f7e64f5..01fe565 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "module": "commonjs", "target": "es6", "outDir": "out", + "strictPropertyInitialization": false, "lib": [ "ES2018", "DOM", From 50c2c26cae429372550c06ecae0f33fb8b7ab9c5 Mon Sep 17 00:00:00 2001 From: Krishna Acondy Date: Mon, 8 Mar 2021 10:53:06 +0000 Subject: [PATCH 2/2] fix(default-target): use defaultTarget property --- package-lock.json | 6 +++--- package.json | 2 +- .../execute-code/internal/configuration.ts | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 77f1827..cd4e5f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -240,9 +240,9 @@ } }, "@sasjs/utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.6.1.tgz", - "integrity": "sha512-yyWGIb9ntR3Z4HoVkFo0WQzdsIhAsIL0mvQ5w12AHJ6UYTa6HHvC999KM+ZSENsRkPORp9alxOFquJ2NoI1Rxg==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.6.3.tgz", + "integrity": "sha512-qfL61WW7LwdFrpLIt6Uyr3Eoh+HPwCG3gfhgq9E7XriLs7/YZL3WiUTasYadu8mDeXI/LhHwQh01o3bzFDCQfw==", "requires": { "@types/prompts": "^2.0.9", "consola": "^2.15.0", diff --git a/package.json b/package.json index 5533b14..da15226 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ }, "dependencies": { "@sasjs/adapter": "^2.1.0", - "@sasjs/utils": "^2.6.1", + "@sasjs/utils": "^2.6.3", "valid-url": "^1.0.9" } } diff --git a/src/commands/execute-code/internal/configuration.ts b/src/commands/execute-code/internal/configuration.ts index ced540e..5b848cc 100644 --- a/src/commands/execute-code/internal/configuration.ts +++ b/src/commands/execute-code/internal/configuration.ts @@ -1,7 +1,7 @@ import { OutputChannel, env, Uri } from 'vscode' import SASjs from '@sasjs/adapter/node' -import { Target } from '@sasjs/utils/types' +import { Target, Configuration } from '@sasjs/utils/types' import { getAuthCode, getChoiceInput, @@ -15,14 +15,15 @@ import { import { getAuthUrl, getTokens } from '../../../utils/auth' import { getGlobalConfiguration, + saveGlobalRcFile, saveToGlobalConfig } from '../../../utils/config' export const selectTarget = async (outputChannel: OutputChannel) => { - const config = await getGlobalConfiguration(outputChannel) + const config = await getGlobalConfiguration(outputChannel) as Configuration if (config?.targets?.length) { - let targetName = config.targets.find((t: Target) => t.isDefault)?.name + let targetName = config.targets.find((t) => t.name === config.defaultTarget)?.name if (!targetName) { const targetNames = (config?.targets || []).map((t: any) => t.name) targetName = await getChoiceInput(targetNames, 'Please select a target') @@ -80,9 +81,18 @@ export const createTarget = async (outputChannel: OutputChannel) => { await saveToGlobalConfig(target, outputChannel) + if (isDefault) { + await setTargetAsDefault(name, outputChannel) + } return target } export const getAccessToken = async (target: Target) => { return target.authConfig?.access_token } + +const setTargetAsDefault = async (targetName: string, outputChannel: OutputChannel) => { + const globalConfig = await getGlobalConfiguration(outputChannel) + globalConfig.defaultTarget = targetName + await saveGlobalRcFile(JSON.stringify(globalConfig, null, 2)) +}