add extension APIs to get live data

Signed-off-by: Yan Zhang <yanzh@microsoft.com>
This commit is contained in:
Yan Zhang
2022-04-02 18:38:13 +08:00
parent e66303e28e
commit f332ae4e69
6 changed files with 139 additions and 4 deletions

View File

@@ -38,6 +38,7 @@ public class SpringProcessCommandHandler {
private static final String COMMAND_CONNECT = "sts/livedata/connect";
private static final String COMMAND_REFRESH = "sts/livedata/refresh";
private static final String COMMAND_DISCONNECT = "sts/livedata/disconnect";
private static final String COMMAND_GET = "sts/livedata/get";
private final SpringProcessConnectorService connectorService;
private final SpringProcessConnectorLocal localProcessConnector;
@@ -68,6 +69,11 @@ public class SpringProcessCommandHandler {
return disconnect(params);
});
log.info("Registered command handler: {}",COMMAND_DISCONNECT);
server.onCommand(COMMAND_GET, (params) -> {
return get(params);
});
log.info("Registered command handler: {}",COMMAND_GET);
}
private CompletableFuture<Object> connect(ExecuteCommandParams params) {
@@ -174,16 +180,20 @@ public class SpringProcessCommandHandler {
}
private String getProcessKey(ExecuteCommandParams params) {
return getArgumentByKey(params, "processKey");
}
private String getArgumentByKey(ExecuteCommandParams params, String name) {
List<Object> arguments = params.getArguments();
for (Object arg : arguments) {
if (arg instanceof Map<?, ?>) {
Object value = ((Map<?, ?>) arg).get("processKey");
Object value = ((Map<?, ?>) arg).get(name);
if (value != null) {
return value.toString();
}
}
else if (arg instanceof JsonObject) {
JsonElement element = ((JsonObject) arg).get("processKey");
JsonElement element = ((JsonObject) arg).get(name);
if (element != null) {
return element.getAsString();
}
@@ -192,5 +202,42 @@ public class SpringProcessCommandHandler {
return null;
}
private CompletableFuture<Object> get(ExecuteCommandParams params) {
String processKey = getProcessKey(params);
String dataKey = getArgumentByKey(params, "dataKey");
if (processKey != null) {
SpringProcessLiveData data = connectorService.getLiveData(processKey);
switch(dataKey) {
case "properties": {
return CompletableFuture.completedFuture(data.getLiveProperties());
}
case "beans": {
String beanName = getArgumentByKey(params, "beanName");
if (beanName != null) {
return CompletableFuture.completedFuture(data.getBeans().getBeansOfName(beanName));
}
String dependingOn = getArgumentByKey(params, "dependingOn");
if (dependingOn != null) {
return CompletableFuture.completedFuture(data.getBeans().getBeansDependingOn(dependingOn));
}
return CompletableFuture.completedFuture(data.getBeans().getBeanNames());
}
case "mappings": {
return CompletableFuture.completedFuture(data.getRequestMappings());
}
case "contextPath": {
return CompletableFuture.completedFuture(data.getContextPath());
}
case "port": {
return CompletableFuture.completedFuture(data.getPort());
}
default: {}
}
}
return CompletableFuture.completedFuture(null);
}
}

View File

@@ -110,6 +110,10 @@ public class SpringProcessConnectorService {
}
}
public SpringProcessLiveData getLiveData(String processKey) {
return this.liveDataProvider.getCurrent(processKey);
}
public void disconnectProcess(String processKey) {
log.info("disconnect from process: " + processKey);

View File

@@ -9,6 +9,8 @@ import * as liveHoverUi from './live-hover-connect-ui';
import {LanguageClient} from "vscode-languageclient/node";
import { startDebugSupport } from './debug-config-provider';
import { ApiManager } from "./apiManager";
import { ExtensionAPI } from "./api";
const PROPERTIES_LANGUAGE_ID = "spring-boot-properties";
const YAML_LANGUAGE_ID = "spring-boot-properties-yaml";
@@ -18,7 +20,7 @@ const XML_LANGUAGE_ID = "xml";
const NEVER_SHOW_AGAIN = "Do not show again";
/** Called when extension is activated */
export function activate(context: VSCode.ExtensionContext): Thenable<LanguageClient> {
export function activate(context: VSCode.ExtensionContext): Thenable<ExtensionAPI> {
// registerPipelineGenerator(context);
let options : commons.ActivatorOptions = {
@@ -109,6 +111,6 @@ export function activate(context: VSCode.ExtensionContext): Thenable<LanguageCli
return commons.activate(options, context).then(client => {
liveHoverUi.activate(client, options, context);
return client;
return new ApiManager(client).api;
});
}

View File

@@ -0,0 +1,42 @@
import { Event } from "vscode";
import { LanguageClient } from "vscode-languageclient/node";
export interface ExtensionAPI {
readonly client: LanguageClient;
/**
* An event which fires on live process is connected. Payload is processKey.
*/
readonly onDidLiveProcessConnect: Event<string>
/**
* An event which fires on live process is disconnected. Payload is processKey.
*/
readonly onDidLiveProcessDisconnect: Event<string>
/**
* A command to get live process data.
*/
readonly getLiveProcessData: (query: SimpleQuery | BeansQuery) => Promise<any>
}
interface LiveProcessDataQuery {
/**
* unique identifier of a connected live process.
*/
processKey: string;
}
interface SimpleQuery extends LiveProcessDataQuery {
endpoint: "mappings" | "contextPath" | "port" | "properties";
}
interface BeansQuery extends LiveProcessDataQuery {
endpoint: "beans";
/**
* if provided, return corresponding beans via name.
*/
beanName?: string;
dependingOn?: string;
}

View File

@@ -0,0 +1,31 @@
import { commands, Uri } from "vscode";
import { Emitter, LanguageClient } from "vscode-languageclient/node";
import { ExtensionAPI } from "./api";
import { LiveProcessConnectedNotification, LiveProcessDisconnectedNotification } from "./notification";
export class ApiManager {
public api: ExtensionAPI;
private onDidLiveProcessConnectEmitter: Emitter<string> = new Emitter<string>();
private onDidLiveProcessDisconnectEmitter: Emitter<string> = new Emitter<string>();
public constructor(private client: LanguageClient) {
const onDidLiveProcessConnect = this.onDidLiveProcessConnectEmitter.event;
const onDidLiveProcessDisconnect = this.onDidLiveProcessDisconnectEmitter.event;
const COMMAND_LIVEDATA_GET = "sts/livedata/get";
const getLiveProcessData = async (query) => {
await commands.executeCommand(COMMAND_LIVEDATA_GET, query);
}
// TODO: STS server should send corresponding notification back.
client.onNotification(LiveProcessConnectedNotification.type, (processKey: string) => this.onDidLiveProcessConnectEmitter.fire(processKey));
client.onNotification(LiveProcessDisconnectedNotification.type, (processKey: string) => this.onDidLiveProcessDisconnectEmitter.fire(processKey));
this.api = {
client,
onDidLiveProcessConnect,
onDidLiveProcessDisconnect,
getLiveProcessData
};
}
}

View File

@@ -0,0 +1,9 @@
import { NotificationType } from "vscode-languageclient";
export namespace LiveProcessConnectedNotification {
export const type = new NotificationType<string>('sts/liveprocess/connected');
}
export namespace LiveProcessDisconnectedNotification {
export const type = new NotificationType<string>('sts/liveprocess/disconnected');
}