-
-
Notifications
You must be signed in to change notification settings - Fork 37
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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); | ||||||||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Suggested change
|
||||||||
} | ||||||||
} | ||||||||
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 | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Suggested change
|
||||||||
|
||||||||
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); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Suggested change
|
||||||||
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; | ||||||||
} |
There was a problem hiding this comment.
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 !!