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

Reverted to vs-code-dev and added export button #629

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions extensions/api_history/history.msgpack
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
‘ƒ¦method£GET¤nameÙ http://dummy.restapiexample.com/¨lastUsed¸2024-10-23T18:28:39.910Z
File renamed without changes.
File renamed without changes.
File renamed without changes
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,31 @@ body {
font-weight: bold;
}

#export-history-btn {
background-color: white;
color: black;
border: none;
border-radius: 3px;
padding: 6px 10px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s ease;
}

#export-history-btn:hover {
background-color: rgb(206, 206, 206);
}

#export-history-btn::before {
margin-right: 5px;
font-size: 14px;
font-weight: bold;
}


#filter-collections {
margin: 10px 16px; /* Increased margin */
padding: 6px 10px; /* Increased padding */
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion extensions/vscode/package.json → extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"publisher": "CloudCodeAI",
"repository": {
"type": "git",
"url": "https://github.com/Cloud-Code-AI/kaizen.git"
"url": "https://github.com/Clouod-Code-AI/kaizen.git"
},
"engines": {
"vscode": "^1.93.0"
Expand Down
315 changes: 315 additions & 0 deletions extensions/src/SidebarProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
import * as fs from 'fs';
import * as path from 'path';
import * as msgpack from 'msgpack-lite';
import * as vscode from 'vscode';
import { ApiRequestProvider } from './apiRequest/apiRequestProvider';
import { log } from './extension';
import { ApiEndpoint } from './types';
import { TestManagementProvider } from './testManagement/testManagementProvider';

export class SidebarProvider implements vscode.WebviewViewProvider {
_view?: vscode.WebviewView;
_doc?: vscode.TextDocument;
private apiRequestProvider: ApiRequestProvider;
private apiHistory: ApiEndpoint[] = [];
private showHistory: boolean = false;
private context: vscode.ExtensionContext;
private testManagementProvider: TestManagementProvider;


constructor(private readonly _extensionUri: vscode.Uri, context: vscode.ExtensionContext) {
this.apiRequestProvider = new ApiRequestProvider(context);
this.context = context;
this.loadApiHistory();
this.testManagementProvider = new TestManagementProvider(context);



// Register command to update API history
context.subscriptions.push(
vscode.commands.registerCommand('vscode-api-client.updateApiHistory', (endpoint: ApiEndpoint) => {
this.updateApiHistory(endpoint);
})
);
}
private loadApiHistory() {
try {
const history = this.context.globalState.get<ApiEndpoint[]>('apiHistory', []);
this.apiHistory = history;
} catch (error) {
console.error('Error loading API history:', error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: Potential exposure of sensitive data in console logs.

Solution: Avoid logging sensitive data or implement a logging framework with configurable log levels.
!! Make sure the following suggestion is correct before committing it !!

Suggested change
console.error('Error loading API history:', error);
console.error('Error loading API history');

this.apiHistory = [];
}
}

private saveApiHistory() {
try {
this.context.globalState.update('apiHistory', this.apiHistory);
} catch (error) {
console.error('Error saving API history:', error);
vscode.window.showErrorMessage('Failed to save API history. Please try again.');
Comment on lines +54 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: Potential exposure of sensitive data in console logs.

Solution: Avoid logging sensitive information or ensure it is sanitized before logging.
!! Make sure the following suggestion is correct before committing it !!

Suggested change
console.error('Error saving API history:', error);
vscode.window.showErrorMessage('Failed to save API history. Please try again.');
console.error('Error saving API history.'); // Avoid logging the error object directly.

}
}
private async exportApiHistory() {
try {
console.log("Exporting API history...");

// Define file path for saving the API history
const folderPath = path.join(this.context.extensionPath, 'api_history');

// Create directory if it doesn't exist
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath);
console.log(`Created folder at ${folderPath}`);
}

// Define file path for the MessagePack file
const filePath = path.join(folderPath, 'history.msgpack');

// Write API history to MessagePack file
const packedData = msgpack.encode(this.apiHistory); // Encode data using MessagePack
fs.writeFileSync(filePath, packedData); // Write packed data to file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: Synchronous file system operations may block the event loop.

Solution: Use asynchronous file operations instead to improve performance.
!! Make sure the following suggestion is correct before committing it !!

Suggested change
fs.writeFileSync(filePath, packedData); // Write packed data to file
await fs.promises.writeFile(filePath, packedData);


vscode.window.showInformationMessage('API History Exported to api_history folder!');
} catch (error) {
console.error('Error saving API history:', error); // Log any errors encountered
vscode.window.showErrorMessage(`Failed to save API history: ${error.message}`);
}
}

public refresh() {
if (this._view) {
console.log('Refreshing webview content');
this._view.webview.html = this._getHtmlForWebview(this._view.webview);
}
}

public resolveWebviewView(webviewView: vscode.WebviewView) {
this._view = webviewView;
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this._extensionUri],
};

webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);

webviewView.webview.onDidReceiveMessage(async (data) => {
log("Received message on sidebar");
switch (data.type) {
case "onInfo": {
if (!data.value) {
return;
}
vscode.window.showInformationMessage(data.value);
break;
}
case "onError": {
if (!data.value) {
return;
}
vscode.window.showErrorMessage(data.value);
break;
}
case "openApiManagement": {
if (!data.value) {
return;
}
this.openWebview(data.value);
break;
}
case "backButton": {
this.showHistory = false;
this.refresh();
break;
}
case "newRequest": {
this.openApiRequestView();
break;
}
case "deleteEndpoint": {
this.deleteEndpoint(data.name, data.method);
break;
}
case "exportApiHistory":{
this.exportApiHistory();
break;
}
}
});
}

public revive(panel: vscode.WebviewView) {
this._view = panel;
}

private _getHtmlForWebview(webview: vscode.Webview) {
log("HTML Web View Loaded");

const scriptUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "out", "sidebar.js")
);

const styleSidebarUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "media", "sidebar.css")
);


const nonce = getNonce();

const csp = `
default-src 'none';
script-src ${webview.cspSource} 'nonce-${nonce}';
style-src ${webview.cspSource} 'unsafe-inline';
img-src ${webview.cspSource} https:;
font-src ${webview.cspSource};
`;

return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="${csp}">
<link href="${styleSidebarUri}" rel="stylesheet">
</head>
<body>
${this.showHistory
? `<button id="back-button">Back</button>${this.getHistoryHtml()}`
: `<div id="buttons">
<button class="webview-button" data-webview="apiManagement">API Management</button>
<button class="webview-button" data-webview="apiRequest">API Documentation [Coming Soon]</button>
<button class="webview-button" data-webview="documentation">Documentation [Coming Soon]</button>
<button class="webview-button" data-webview="testManagement">Test Management [Coming Soon]</button>
<button class="webview-button" data-webview="codeReview">Code Review [Coming Soon]</button>
</div>`
}
<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}

private getHistoryHtml() {
return `
<div id="sidebar-container">
<div id="history-header">
<h3>API History</h3>
<button id="new-request-btn">New Request</button>
<button id="export-history-btn">Export History</button>
</div>
<input type="text" id="filter-history" placeholder="Filter history">
<ul id="api-history">
${this.apiHistory.map(endpoint => `
<li class="api-endpoint ${endpoint.method.toLowerCase()}">
<span class="method">${endpoint.method}</span>
<span class="name">${endpoint.name}</span>
<span class="last-used">${new Date(endpoint.lastUsed).toLocaleString()}</span>
<button class="delete-btn" data-name="${endpoint.name}" data-method="${endpoint.method}">X</button>
</li>
`).join('')}
</ul>
</div>
`;
}

private async openWebview(webviewType: string) {
if (webviewType === 'apiManagement') {
if (this.apiRequestProvider) {
console.log("Opening API Request View");
this.apiRequestProvider.openApiRequestView();
this.showHistory = true;
this.refresh();
} else {
console.error("apiRequestProvider is not initialized");
vscode.window.showErrorMessage("API Management is not available");
}
} else if (webviewType === 'testManagement') {
if (this.testManagementProvider) {
console.log("Opening Test Management View");
await this.testManagementProvider.openTestManagementView();
} else {
console.error("testManagementProvider is not initialized");
vscode.window.showErrorMessage("Test Management is not available");
}
} else {
const panel = vscode.window.createWebviewPanel(
webviewType,
this.getWebviewTitle(webviewType),
vscode.ViewColumn.One,
{
enableScripts: true,
localResourceRoots: [this._extensionUri],
}
);

panel.webview.html = await this.getWebviewContent(webviewType);
}
}

private openApiRequestView() {
if (this.apiRequestProvider) {
console.log("Opening API Request View");
this.apiRequestProvider.openApiRequestView();
} else {
console.error("apiRequestProvider is not initialized");
vscode.window.showErrorMessage("API Request is not available");
}
}

private getWebviewTitle(webviewType: string): string {
switch (webviewType) {
case 'apiManagement':
return 'API Management';
case 'apiRequest':
return 'API Request';
case 'chatRepo':
return 'Chat Repo';
case 'documentation':
return 'Documentation';
case 'codeReview':
return 'Code Review';
case 'testManagement':
return 'Test Case';
default:
return 'Webview';
}
}

private async getWebviewContent(webviewType: string): Promise<string> {
const filePath = vscode.Uri.joinPath(this._extensionUri, webviewType, 'index.html');
const fileContent = await vscode.workspace.fs.readFile(filePath);
return fileContent.toString();
}
private updateApiHistory(endpoint: ApiEndpoint) {
const existingIndex = this.apiHistory.findIndex(
e => e.name === endpoint.name && e.method === endpoint.method
);

if (existingIndex !== -1) {
this.apiHistory[existingIndex].lastUsed = endpoint.lastUsed;
const [updatedEndpoint] = this.apiHistory.splice(existingIndex, 1);
this.apiHistory.unshift(updatedEndpoint);
} else {
this.apiHistory.unshift(endpoint);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: Potential exposure of sensitive data in API history.

Solution: Implement data sanitization or encryption for sensitive fields before storing or displaying them.
!! Make sure the following suggestion is correct before committing it !!

Suggested change
this.apiHistory.unshift(endpoint);
this.apiHistory.unshift(sanitizeEndpoint(endpoint));

this.apiHistory = this.apiHistory.slice(0, 10);
}

this.saveApiHistory();
this.refresh();
}

private deleteEndpoint(name: string, method: string) {
this.apiHistory = this.apiHistory.filter(
endpoint => !(endpoint.name === name && endpoint.method === method)
);
this.saveApiHistory();
this.refresh();
}
}
function getNonce() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
Loading
Loading