From bdacd431c8c7a97d014891a1dfa279c715715473 Mon Sep 17 00:00:00 2001 From: Arunprasad Rajkumar Date: Wed, 13 Jan 2021 18:44:48 +0530 Subject: [PATCH] feat: Add dependency analytics extension recommendation Signed-off-by: Arunprasad Rajkumar --- src/extension.ts | 3 ++ src/recommendation/dependencyAnalytics.ts | 30 +++++++++++ src/recommendation/handler.ts | 6 +++ src/recommendation/handlerImpl.ts | 64 +++++++++++++++++++++++ src/recommendation/index.ts | 12 +++++ 5 files changed, 115 insertions(+) create mode 100644 src/recommendation/dependencyAnalytics.ts create mode 100644 src/recommendation/handler.ts create mode 100644 src/recommendation/handlerImpl.ts create mode 100644 src/recommendation/index.ts diff --git a/src/extension.ts b/src/extension.ts index f38fa837d..be2097bf3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,6 +9,7 @@ import { ExecuteCommandParams, ExecuteCommandRequest, LanguageClient, LanguageCl import { collectJavaExtensions } from './plugin'; import { prepareExecutable } from './javaServerStarter'; import * as requirements from './requirements'; +import { initialize as initializeRecommendation } from './recommendation'; import { Commands } from './commands'; import { ExtensionAPI, ClientStatus } from './extension.api'; import { getJavaConfiguration, deleteDirectory, getBuildFilePatterns, getInclusionPatternsFromNegatedExclusion, convertToGlob, getExclusionBlob } from './utils'; @@ -127,6 +128,8 @@ export function activate(context: ExtensionContext): Promise { enableJavadocSymbols(); + initializeRecommendation(context); + return requirements.resolveRequirements(context).catch(error => { // show error window.showErrorMessage(error.message, error.label).then((selection) => { diff --git a/src/recommendation/dependencyAnalytics.ts b/src/recommendation/dependencyAnalytics.ts new file mode 100644 index 000000000..1af038aee --- /dev/null +++ b/src/recommendation/dependencyAnalytics.ts @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +'use strict'; + +import * as vscode from "vscode"; +import { IHandler } from "./handler"; + +const EXTENSION_NAME = "redhat.fabric8-analytics"; +const GH_ORG_URL = `https://github.com/fabric8-analytics`; +const RECOMMENDATION_MESSAGE = `[Dependency Analytics](${GH_ORG_URL}) extension is recommended to get security insights about pom.xml.`; + +function isPomDotXml(uri: vscode.Uri) { + return !!uri.path && uri.path.toLowerCase().endsWith("pom.xml"); +} + +export function initialize (context: vscode.ExtensionContext, handler: IHandler): void { + if (!handler.canRecommendExtension(EXTENSION_NAME)) { + return; + } + context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => { + if (isPomDotXml(e.uri)) { + handler.handle(EXTENSION_NAME, RECOMMENDATION_MESSAGE); + } + })); + + const isPomDotXmlOpened = vscode.workspace.textDocuments.findIndex(doc => isPomDotXml(doc.uri)) !== -1; + if (isPomDotXmlOpened) { + handler.handle(EXTENSION_NAME, RECOMMENDATION_MESSAGE); + } +} diff --git a/src/recommendation/handler.ts b/src/recommendation/handler.ts new file mode 100644 index 000000000..25a66139a --- /dev/null +++ b/src/recommendation/handler.ts @@ -0,0 +1,6 @@ +'use strict'; + +export interface IHandler { + handle(extName: string, message: string): Promise; + canRecommendExtension(extName: string): boolean; +} diff --git a/src/recommendation/handlerImpl.ts b/src/recommendation/handlerImpl.ts new file mode 100644 index 000000000..bb57c9d45 --- /dev/null +++ b/src/recommendation/handlerImpl.ts @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +'use strict'; + +import * as vscode from "vscode"; +import { IHandler } from "./handler"; + +const KEY_RECOMMENDATION_USER_CHOICE_MAP = "recommendationUserChoice"; + +async function installExtensionCmdHandler(extensionName: string, displayName: string) { + return vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: `Installing ${displayName||extensionName}...`}, progress => { + return vscode.commands.executeCommand("workbench.extensions.installExtension", extensionName); + }).then(() => { + vscode.window.showInformationMessage(`Successfully installed ${displayName||extensionName}.`); + }); +} + +enum UserChoice { + Install = "Install", + Never = "Never", + Later = "Later", +} + +export class HandlerImpl implements IHandler { + userChoice: any; + storeUserChoice: any; + constructor(context: vscode.ExtensionContext) { + this.userChoice = () => { + return context.globalState.get(KEY_RECOMMENDATION_USER_CHOICE_MAP, {}); + }; + + this.storeUserChoice = (choice: object) => { + context.globalState.update(KEY_RECOMMENDATION_USER_CHOICE_MAP, choice); + }; + } + + isExtensionInstalled(extName: string): boolean { + return !!vscode.extensions.getExtension(extName); + } + + canRecommendExtension(extName: string): boolean { + return this.userChoice()[extName] !== UserChoice.Never && !this.isExtensionInstalled(extName); + } + + async handle(extName: string, message: string): Promise { + if (this.isExtensionInstalled(extName)) { + return; + } + + const choice = this.userChoice(); + if (choice[extName] === UserChoice.Never) { + return; + } + + const actions: Array = Object.keys(UserChoice); + const answer = await vscode.window.showInformationMessage(message, ...actions); + if (answer === UserChoice.Install) { + await installExtensionCmdHandler(extName, extName); + } + + choice[extName] = answer; + this.storeUserChoice(choice); + } +} diff --git a/src/recommendation/index.ts b/src/recommendation/index.ts new file mode 100644 index 000000000..7a46fb630 --- /dev/null +++ b/src/recommendation/index.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +'use strict'; + +import * as vscode from "vscode"; +import { HandlerImpl } from "./handlerImpl"; +import { initialize as initDependencyAnalytics } from "./dependencyAnalytics"; + +export function initialize (context: vscode.ExtensionContext) { + const handler = new HandlerImpl(context); + initDependencyAnalytics(context, handler); +}