Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(views): toggle visibility #135 #136

Merged
merged 5 commits into from
Oct 21, 2023
Merged
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
1 change: 0 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"isDefault": true
},
"dependsOn": [
"Frontend Build",
]
}
]
Expand Down
27 changes: 22 additions & 5 deletions backend/__mocks__/azdata.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@


const connection = {}, ConnectionProvider = {}, dataprotocol = {}, DataProviderType = {}, QueryProvider = {}, SimpleExecuteResult = {};
const connection = {
getUriForConnection: jest.fn(),
},
ConnectionProvider = {
changeDatabase: jest.fn(),
connect: jest.fn(),
disconnect: jest.fn(),
getConnection: jest.fn(),
},
dataprotocol = {
getProvider: jest.fn(),
},
DataProviderType = {},
QueryProvider = {},
SimpleExecuteResult = {};

module.exports = {
connection, ConnectionProvider, dataprotocol, DataProviderType, QueryProvider, SimpleExecuteResult
};
connection,
ConnectionProvider,
dataprotocol,
DataProviderType,
QueryProvider,
SimpleExecuteResult,
};
3 changes: 1 addition & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "schema-visualization",
"displayName": "Schema Visualization",
"description": "Erd, Er-diagram. Visualization of databases",
"version": "0.9.2",
"version": "0.9.3",
"publisher": "R0tenur",
"license": "MIT",
"icon": "logo.png",
Expand Down Expand Up @@ -40,7 +40,6 @@
"vscode:prepublish": "yarn run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"proposedapi": "node installTypings.js",
"pack": "vsce package",
"test": "jest --config=jest.config.js"
},
Expand Down
76 changes: 17 additions & 59 deletions backend/src/controllers/visualization.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,29 @@
import { dashboard, DashboardWebview } from "azdata";
import { loadWebView } from "../web.loader";
import { visualizationPanelName } from "../constants";
import { getMssqlDbSchema } from "../repositories/mssql.repository";
import { chartBuilder } from "../services/builder.service";
import { Database } from "../models/database.model";
import { Status } from "../models/status.enum";
import { messageHandler } from "../message.handler";
import { getMermaidForDb, messageHandler } from "../message.handler";

let view: DashboardWebview;
export const VisualizationController = () => {
let counterHtml = loadWebView();
let counterHtml = loadWebView();

dashboard.registerWebviewProvider(
visualizationPanelName,
async (webview: DashboardWebview) => {
webview.html = counterHtml;
webview.onMessage(messageHandler);
dashboard.registerWebviewProvider(
visualizationPanelName,
async (webview: DashboardWebview) => {
view = webview;
view.html = counterHtml;
view.onMessage((m) => messageHandler(view, m));

if (webview.connection.options.database) {
await getMermaidForDb(webview);
} else {
webview.postMessage({
status: Status.NoDatabase,
});
}
}
);
};

const getMermaidForDb = async (webview: DashboardWebview) => {
webview.postMessage({
status: Status.GettingTableData,
});
try {
const database = await getMssqlDbSchema(
webview.connection.connectionId,
webview.connection.options.database);
webview.postMessage({
status: Status.BuildingChart,
databaseRaw: database.tables
});
const chart = chartBuilder(database.tables);

showResult(webview, chart, database);
} catch (error) {
showError(webview, error);
}
};

const showResult = (webview: DashboardWebview, chart: string, database: Database) => {
if (chart) {
webview.postMessage({
status: Status.Complete,
databaseName: webview.connection.options.database,
chart,
errors: database.errors,
tables: database.tables,
if (view.connection.options.database) {
await getMermaidForDb(view, {
options: { showTables: true, showViews: true },
});
} else {
webview.postMessage({
status: Status.Error,
errors: database.errors,
databaseRaw: database.tables
} else {
view.postMessage({
status: Status.NoDatabase,
});
}
}
);
};
const showError = (webview: DashboardWebview, error: Error) => webview.postMessage({
status: Status.Error,
errors: [error.message],
rawData: error.stack
});
1 change: 1 addition & 0 deletions backend/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* istanbul ignore file */
import { ExtensionContext } from "vscode";
import { VisualizationController } from "./controllers/visualization.controller";

Check warning on line 3 in backend/src/extension.ts

View check run for this annotation

Codecov / codecov/patch

backend/src/extension.ts#L3

Added line #L3 was not covered by tests

export const activate = (context: ExtensionContext) =>
VisualizationController();

Check warning on line 6 in backend/src/extension.ts

View check run for this annotation

Codecov / codecov/patch

backend/src/extension.ts#L5-L6

Added lines #L5 - L6 were not covered by tests

export const deactivate = () => {}; // NOSONAR Needed for Azure Data Studio

Check warning on line 8 in backend/src/extension.ts

View check run for this annotation

Codecov / codecov/patch

backend/src/extension.ts#L8

Added line #L8 was not covered by tests
36 changes: 36 additions & 0 deletions backend/src/message.function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { DashboardWebview } from "azdata";
import { Status } from "./models/status.enum";
import { Database } from "./models/database.model";

export const showError = (webview: DashboardWebview, error: Error) =>
webview.postMessage({

Check warning on line 6 in backend/src/message.function.ts

View check run for this annotation

Codecov / codecov/patch

backend/src/message.function.ts#L6

Added line #L6 was not covered by tests
status: Status.Error,
errors: [error.message],
rawData: error.stack,
});

export const showStatus = (webview: DashboardWebview, status: string) =>
webview.postMessage({
status,
});
export const showResult = (
webview: DashboardWebview,
chart: string,
database: Database
) => {
if (chart) {
webview.postMessage({

Check warning on line 22 in backend/src/message.function.ts

View check run for this annotation

Codecov / codecov/patch

backend/src/message.function.ts#L22

Added line #L22 was not covered by tests
status: Status.Complete,
databaseName: webview.connection.options.database,
chart,
errors: database.errors,
tables: database.tables,
});
} else {
webview.postMessage({

Check warning on line 30 in backend/src/message.function.ts

View check run for this annotation

Codecov / codecov/patch

backend/src/message.function.ts#L30

Added line #L30 was not covered by tests
status: Status.Error,
errors: database.errors,
databaseRaw: database.tables,
});
}
};
140 changes: 107 additions & 33 deletions backend/src/message.handler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,120 @@
import { DashboardWebview } from "azdata";
import { messageHandler } from "./message.handler";
import * as exporter from "./services/export.service";
import * as messageFunction from "./message.function";
import * as repository from "./repositories/mssql.repository";
import { Status } from "./models/status.enum";
const vscode = require("../__mocks__/vscode");

const vscode = require('../__mocks__/vscode');
describe("messageHandler", () => {
const view = {
connection: {
connectionId: "id",
options: {
database: "database",
},
},
postMessage: jest.fn(),
} as unknown as DashboardWebview;
let getMssqlDbSchemaSpy;
let showErrorSpy;
let exportSpy;
let showResultSpy;
let chartBuilderSpy;

beforeEach(() => {
getMssqlDbSchemaSpy = spyOn(repository, "getMssqlDbSchema");
showErrorSpy = spyOn(messageFunction, "showError");
exportSpy = spyOn(exporter, "exportService");
showResultSpy = spyOn(messageFunction, "showResult");
chartBuilderSpy = spyOn(
require("./services/builder.service"),
"chartBuilder"
).and.returnValue("chart");
});

describe("load", () => {
it("shows error when connectionError", async () => {
// Arrange
getMssqlDbSchemaSpy.and.throwError("connectionError");

describe('exportService', () => {
// Act
await messageHandler(view, {
command: "load",
options: {},
});

it('selects value from quick pick', async () => {
// Arrange
const data = {
chart: 'svg'
};
const exportSpy = spyOn(exporter, 'exportService').and.returnValue(Promise.resolve());
spyOn(vscode.window, 'showQuickPick').and.returnValue(Promise.resolve('svg'));
// Assert
expect(showErrorSpy).toHaveBeenCalledWith(
view,
new Error("connectionError")
);
});
it("updates status when fetched tables", async () => {
// Arrange
getMssqlDbSchemaSpy.and.returnValue(Promise.resolve({ tables: [] }));

// Act
await messageHandler({
data
});
// Act
await messageHandler(view, {
command: "load",
options: {},
});

// Assert
expect(vscode.window.showQuickPick).toHaveBeenCalledWith(['svg', 'md']);
expect(exportSpy).toHaveBeenCalledWith(`chart.svg`, data, 'svg');
// Assert
expect(view.postMessage).toHaveBeenCalledWith({
status: Status.BuildingChart,
databaseRaw: [],
});
expect(chartBuilderSpy).toHaveBeenCalledWith([]);
expect(showResultSpy).toHaveBeenCalledWith(view, "chart", { tables: [] });
});
});
describe("save", () => {
it("selects value from quick pick", async () => {
// Arrange
const data = {
chart: "svg",
};
exportSpy.and.returnValue(Promise.resolve());
spyOn(vscode.window, "showQuickPick").and.returnValue(
Promise.resolve("svg")
);

// Act
await messageHandler(
{} as DashboardWebview,

{
command: "save",

it('only md aviable when svg not present', async () => {
// Arrange
const data = {
mermaid: 'dummymermaid'
};
const exportSpy = spyOn(exporter, 'exportService').and.returnValue(Promise.resolve());
spyOn(vscode.window, 'showQuickPick').and.returnValue(Promise.resolve('md'));

// Act
await messageHandler({
data
});

// Assert
expect(vscode.window.showQuickPick).toHaveBeenCalledWith(['md']);
expect(exportSpy).toHaveBeenCalledWith(`chart.md`, data, 'md');
data,
}
);

// Assert
expect(vscode.window.showQuickPick).toHaveBeenCalledWith(["svg", "md"]);
expect(exportSpy).toHaveBeenCalledWith(`chart.svg`, data, "svg");
});
});

it("only md aviable when svg not present", async () => {
// Arrange
const data = {
mermaid: "dummymermaid",
};
exportSpy.and.returnValue(Promise.resolve());
spyOn(vscode.window, "showQuickPick").and.returnValue(
Promise.resolve("md")
);

// Act
await messageHandler(view, {
command: "save",
data,
});

// Assert
expect(vscode.window.showQuickPick).toHaveBeenCalledWith(["md"]);
expect(exportSpy).toHaveBeenCalledWith(`chart.md`, data, "md");
});
});
});
53 changes: 47 additions & 6 deletions backend/src/message.handler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,51 @@
import { window } from "vscode";
import { exportService } from "./services/export.service";
import { DashboardWebview } from "azdata";
import { Status } from "./models/status.enum";
import { getMssqlDbSchema } from "./repositories/mssql.repository";
import { chartBuilder } from "./services/builder.service";
import { showError, showResult, showStatus } from "./message.function";

export const messageHandler = async (e: any) => {
const selectable = !e.data.chart ? ['md'] : ['svg', 'md'];
await new Promise(resolve => setTimeout(resolve, 100));
const selected = await window.showQuickPick([...selectable]);
let data = e.data;
await exportService(`chart.${selected}`, data, selected);
export const messageHandler = async (view: DashboardWebview, e: any) => {
const handlers = {
save: saveHandler,
load: getMermaidForDb,
};
const handler = handlers[e.command];

if (handler) {
await handler(view, e);
}
};

export const getMermaidForDb = async (
webview: DashboardWebview,
message: any
) => {
showStatus(webview, Status.GettingTableData);

try {
const database = await getMssqlDbSchema(
webview.connection.connectionId,
webview.connection.options.database,
message.options
);
webview.postMessage({
status: Status.BuildingChart,
databaseRaw: database.tables,
});
const chart = chartBuilder(database.tables);

showResult(webview, chart, database);
} catch (error) {
showError(webview, error);
}
};

const saveHandler = async (_: DashboardWebview, e: any) => {
const selectable = !e.data.chart ? ["md"] : ["svg", "md"];
await new Promise((resolve) => setTimeout(resolve, 100));
const selected = await window.showQuickPick([...selectable]);
let data = e.data;
await exportService(`chart.${selected}`, data, selected);
};
Loading
Loading