Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"private": true,
"scripts": {
"prepare": "lerna run prepare",
"prepare": "yarn build",
"build": "yarn -s tsref && yarn -s tsbuild",
"tsref": "node scripts/typescript-references.js",
"tsbuild": "tsc -b",
"watch": "lerna exec --stream --parallel -- \"yarn run watch\"",
"clean": "lerna run clean",
"vsce:package": "lerna run vsce:package",
Expand Down Expand Up @@ -35,11 +38,16 @@
"webpack-cli": "^4.5.0"
},
"workspaces": [
"packages/base",
"packages/react-components",
"vscode-trace-common",
"vscode-trace-webviews",
"vscode-trace-extension"

],
"resolutions": {
"@vscode/vsce": "2.25.0"
"@vscode/vsce": "2.25.0",
"@types/react": "18.3.8",
"@types/lodash": "4.17.14"
}
}
26 changes: 26 additions & 0 deletions packages/base/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
root: true,
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
parserOptions: {
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: "module", // Allows for the use of imports
tsconfigRootDir: __dirname,
project: 'tsconfig.json',
projectFolderIgnoreList: [
'/lib/'
]
},
extends: [
'plugin:@typescript-eslint/recommended',
'../../configs/base.eslintrc.json',
'../../configs/warnings.eslintrc.json',
'../../configs/errors.eslintrc.json'
],
ignorePatterns: [
'node_modules',
'lib',
'.eslintrc.js',
'plugins'
]
};
19 changes: 19 additions & 0 deletions packages/base/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2019, 2021 Ericsson and others

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
9 changes: 9 additions & 0 deletions packages/base/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Description

The Trace Viewer base package contains trace management utilities for managing traces using Trace Server applications that implement the Trace Server Protocol (TSP). While being initially used within the Theia Trace Viewer extension, its code base is independent to any Theia APIs and hence can be integrated in other web applications.

## Additional Information

- [Theia Trace Viewer Extension git repository](https://github.com/eclipse-cdt-cloud/theia-trace-extension)
- [Trace Server Protocol git repository](https://github.com/eclipse-cdt-cloud/trace-server-protocol)
- [Reference Trace Server - Download (Eclipse Trace Compass)](https://download.eclipse.org/tracecompass.incubator/trace-server/rcp/)
41 changes: 41 additions & 0 deletions packages/base/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "traceviewer-base",
"version": "0.7.2",
"description": "Trace Viewer base package, contains trace management utilities",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/eclipse-cdt-cloud/theia-trace-extension"
},
"bugs": {
"url": "https://github.com/eclipse-cdt-cloud/theia-trace-extension/issues"
},
"homepage": "https://github.com/eclipse-cdt-cloud/theia-trace-extension",
"files": [
"lib",
"src"
],
"dependencies": {
"tsp-typescript-client": "^0.6.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^3.4.0",
"@typescript-eslint/parser": "^3.4.0",
"eslint": "^7.3.0",
"eslint-plugin-import": "^2.21.2",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-react": "^7.20.0",
"rimraf": "^5.0.0",
"typescript": "4.9.5"
},
"scripts": {
"build": "tsc -b",
"clean": "rimraf lib *.tsbuildinfo",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "echo 'test'",
"watch": "tsc -w",
"format:write": "prettier --write ./src",
"format:check": "prettier --check ./src"
}
}
160 changes: 160 additions & 0 deletions packages/base/src/experiment-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { Trace } from 'tsp-typescript-client/lib/models/trace';
import { ITspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
import { Query } from 'tsp-typescript-client/lib/models/query/query';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { TraceManager } from './trace-manager';
import { TspClientResponse } from 'tsp-typescript-client/lib/protocol/tsp-client-response';
import { signalManager } from './signals/signal-manager';

export class ExperimentManager {
private fOpenExperiments: Map<string, Experiment> = new Map();
private fTspClient: ITspClient;
private fTraceManager: TraceManager;

constructor(tspClient: ITspClient, traceManager: TraceManager) {
this.fTspClient = tspClient;
this.fTraceManager = traceManager;
signalManager().on('EXPERIMENT_DELETED', (experiment: Experiment) => this.onExperimentDeleted(experiment));
}

/**
* Get an array of opened experiments
* @returns Array of experiment
*/
async getOpenedExperiments(): Promise<Experiment[]> {
const openedExperiments: Array<Experiment> = [];
// Look on the server for opened experiments
const experimentsResponse = await this.fTspClient.fetchExperiments();
const experiments = experimentsResponse.getModel();
if (experimentsResponse.isOk() && experiments) {
openedExperiments.push(...experiments);
}
return openedExperiments;
}

/**
* Get a specific experiment information
* @param experimentUUID experiment UUID
*/
async getExperiment(experimentUUID: string): Promise<Experiment | undefined> {
// Check if the experiment is in "cache"
let experiment = this.fOpenExperiments.get(experimentUUID);

// If the experiment is undefined, check on the server
if (!experiment) {
const experimentResponse = await this.fTspClient.fetchExperiment(experimentUUID);
if (experimentResponse.isOk()) {
experiment = experimentResponse.getModel();
}
}
return experiment;
}

/**
* Get an array of OutputDescriptor for a given experiment
* @param experimentUUID experiment UUID
*/
async getAvailableOutputs(experimentUUID: string): Promise<OutputDescriptor[] | undefined> {
const outputsResponse = await this.fTspClient.experimentOutputs(experimentUUID);
if (outputsResponse && outputsResponse.isOk()) {
return outputsResponse.getModel();
}
return undefined;
}

/**
* Open a given experiment on the server
* @param experimentURI experiment URI to open
* @param experimentName Optional name for the experiment. If not specified the URI name is used
* @returns The opened experiment
*/
async openExperiment(experimentName: string, traces: Array<Trace>): Promise<Experiment | undefined> {
const name = experimentName;

const traceURIs = new Array<string>();
for (let i = 0; i < traces.length; i++) {
traceURIs.push(traces[i].UUID);
}

const tryCreate = async function (
tspClient: ITspClient,
retry: number
): Promise<TspClientResponse<Experiment>> {
return tspClient.createExperiment(
new Query({
name: retry === 0 ? name : name + '(' + retry + ')',
traces: traceURIs
})
);
};
let tryNb = 0;
let experimentResponse: TspClientResponse<Experiment> | undefined;
while (experimentResponse === undefined || experimentResponse.getStatusCode() === 409) {
experimentResponse = await tryCreate(this.fTspClient, tryNb);
tryNb++;
}
const experiment = experimentResponse.getModel();
if (experimentResponse.isOk() && experiment) {
this.addExperiment(experiment);
signalManager().emit('EXPERIMENT_OPENED', experiment);
return experiment;
}
// TODO Handle any other experiment open errors
return undefined;
}

/**
* Update the experiment with the latest info from the server.
* @param experimentUUID experiment UUID
* @returns The updated experiment or undefined if the experiment failed to update
*/
async updateExperiment(experimentUUID: string): Promise<Experiment | undefined> {
const experimentResponse = await this.fTspClient.fetchExperiment(experimentUUID);
const experiment = experimentResponse.getModel();
if (experiment && experimentResponse.isOk()) {
this.fOpenExperiments.set(experimentUUID, experiment);
return experiment;
}
return undefined;
}

/**
* Delete the given experiment from the server
* @param experimentUUID experiment UUID
*/
async deleteExperiment(experimentUUID: string): Promise<void> {
const experimentToDelete = this.fOpenExperiments.get(experimentUUID);
if (experimentToDelete) {
await this.fTspClient.deleteExperiment(experimentUUID);
const deletedExperiment = this.removeExperiment(experimentUUID);
if (deletedExperiment) {
signalManager().emit('EXPERIMENT_DELETED', deletedExperiment);
}
}
}

private onExperimentDeleted(experiment: Experiment) {
/*
* TODO: Do not close traces used by another experiment
*/
// Close each trace
const traces = experiment.traces;
for (let i = 0; i < traces.length; i++) {
this.fTraceManager.deleteTrace(traces[i].UUID);
}
}

public addExperiment(experiment: Experiment): void {
this.fOpenExperiments.set(experiment.UUID, experiment);
experiment.traces.forEach(trace => {
this.fTraceManager.addTrace(trace);
});
}

private removeExperiment(experimentUUID: string): Experiment | undefined {
const deletedExperiment = this.fOpenExperiments.get(experimentUUID);
this.fOpenExperiments.delete(experimentUUID);
return deletedExperiment;
}
}
47 changes: 47 additions & 0 deletions packages/base/src/lazy-tsp-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { ITspClient } from 'tsp-typescript-client';
import { HttpTspClient } from 'tsp-typescript-client/lib/protocol/http-tsp-client';

/**
* Hack!
* The `LazyTspClient` replaces _every_ method with an asynchronous one.
* Only keep methods, discard properties.
*/
export type LazyTspClient = {
[K in keyof ITspClient]: ITspClient[K] extends (...args: infer A) => infer R | Promise<infer R>
? (...args: A) => Promise<R>
: never; // Discard property.
};

export type LazyTspClientFactory = typeof LazyTspClientFactory;
export function LazyTspClientFactory(provider: () => Promise<string>): ITspClient {
// All methods from the `HttpTspClient` are asynchronous. The `LazyTspClient`
// will just delay each call to its methods by first awaiting for the
// asynchronous `baseUrl` resolution to then get a valid `HttpTspClient`.

// Save the current HttpTspClient and the URL used for it.
let tspClient: HttpTspClient;
let lastUrl: string;
// eslint-disable-next-line no-null/no-null
return new Proxy(Object.create(null), {
get(target, property, _receiver) {
let method = target[property];
if (!method) {
target[property] = method = async (...args: any[]) => {
tspClient = await provider().then(baseUrl => {
// If the url has not been updated keep the same client.
if (lastUrl === baseUrl) {
return tspClient;
}
// If the url has changed save it and create a new client.
lastUrl = baseUrl;
return new HttpTspClient(baseUrl);
});
return (tspClient as any)[property](...args);
};
}
return method;
}
}) as LazyTspClient as ITspClient;
}
36 changes: 36 additions & 0 deletions packages/base/src/message-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export enum MessageCategory {
TRACE_CONTEXT,
SERVER_MESSAGE,
SERVER_STATUS
}

export enum MessageSeverity {
ERROR,
WARNING,
INFO,
DEBUG
}

export interface StatusMessage {
text: string;
category?: MessageCategory;
severity?: MessageSeverity;
}

export declare interface MessageManager {
addStatusMessage(messageKey: string, message: StatusMessage): void;
removeStatusMessage(messageKey: string): void;
}

export class MessageManager implements MessageManager {
addStatusMessage(
messageKey: string,
{ text, category = MessageCategory.SERVER_MESSAGE, severity = MessageSeverity.INFO }: StatusMessage
): void {
console.log('New status message', messageKey, text, category, severity);
}

removeStatusMessage(messageKey: string): void {
console.log('Removing status message status message', messageKey);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';

export class AvailableViewsChangedSignalPayload {
private _availableOutputDescriptors: OutputDescriptor[];
private _experiment: Experiment;

constructor(availableOutputDescriptors: OutputDescriptor[], experiment: Experiment) {
this._availableOutputDescriptors = availableOutputDescriptors;
this._experiment = experiment;
}

public getAvailableOutputDescriptors(): OutputDescriptor[] {
return this._availableOutputDescriptors;
}

public getExperiment(): Experiment {
return this._experiment;
}
}
Loading
Loading