Skip to content

Commit 483d582

Browse files
authored
Merge pull request #6 from GitGuardian/salomevoltz/scrt-4849-show-quota
Show Quota
2 parents fc605c7 + 899f821 commit 483d582

File tree

7 files changed

+173
-10
lines changed

7 files changed

+173
-10
lines changed

images/refresh-dark.svg

Lines changed: 1 addition & 0 deletions
Loading

images/refresh-light.svg

Lines changed: 1 addition & 0 deletions
Loading

package.json

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@
5656
{
5757
"command": "gitguardian.openProblems",
5858
"title": "Open Problems"
59+
},
60+
{
61+
"command": "gitguardian.refreshQuota",
62+
"title": "Refresh Quota",
63+
"icon": {
64+
"light": "images/refresh-light.svg",
65+
"dark": "images/refresh-dark.svg"
66+
}
5967
}
6068
],
6169
"viewsContainers": {
@@ -72,8 +80,13 @@
7280
{
7381
"type": "webview",
7482
"id": "gitguardianView",
75-
"name": "gitguardian",
76-
"when": "gitguardian.isAuthenticated == false"
83+
"name": "gitguardian"
84+
},
85+
{
86+
"type": "webview",
87+
"id": "gitguardianQuotaView",
88+
"name": "quota",
89+
"collapsed": true
7790
}
7891
]
7992
},
@@ -82,7 +95,16 @@
8295
"view": "gitguardianView",
8396
"contents": "To get started with GitGuardian, please authenticate.\n[Authenticate](command:gitguardian.authenticate)"
8497
}
85-
]
98+
],
99+
"menus": {
100+
"view/title": [
101+
{
102+
"command": "gitguardian.refreshQuota",
103+
"group": "navigation",
104+
"when": "view == gitguardianQuotaView"
105+
}
106+
]
107+
}
86108
},
87109
"scripts": {
88110
"vscode:prepublish": "yarn run compile",

src/extension.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
generateSecretName,
3232
GitGuardianSecretHoverProvider,
3333
} from "./gitguardian-hover-provider";
34+
import { GitGuardianQuotaWebviewProvider } from "./ggshield-webview/gitguardian-quota-webview";
3435

3536
/**
3637
* Extension diagnostic collection
@@ -110,6 +111,13 @@ function registerOpenViewsCommands(
110111
);
111112
}
112113

114+
function registerQuotaViewCommands(view: GitGuardianQuotaWebviewProvider) {
115+
commands.registerCommand(
116+
"gitguardian.refreshQuota",
117+
async () => await view.refresh()
118+
);
119+
}
120+
113121
export function activate(context: ExtensionContext) {
114122
// Check if ggshield if available
115123
const outputChannel = window.createOutputChannel("GGShield Resolver");
@@ -124,14 +132,24 @@ export function activate(context: ExtensionContext) {
124132
configuration,
125133
context.extensionUri
126134
);
135+
136+
const ggshieldQuotaViewProvider = new GitGuardianQuotaWebviewProvider(
137+
configuration,
138+
context.extensionUri
139+
);
127140
window.registerWebviewViewProvider("gitguardianView", ggshieldViewProvider);
128-
context.subscriptions.push(ggshieldViewProvider);
141+
window.registerWebviewViewProvider(
142+
"gitguardianQuotaView",
143+
ggshieldQuotaViewProvider
144+
);
145+
context.subscriptions.push(ggshieldViewProvider, ggshieldQuotaViewProvider);
129146

130147
statusBar = window.createStatusBarItem(StatusBarAlignment.Left, 0);
131148
updateStatusBarItem(StatusBarStatus.initialization, statusBar);
132149

133150
//generic commands to open correct view on status bar click
134151
registerOpenViewsCommands(context, outputChannel);
152+
registerQuotaViewCommands(ggshieldQuotaViewProvider);
135153
context.subscriptions.push(statusBar);
136154

137155
context.subscriptions.push(
@@ -212,6 +230,7 @@ export function activate(context: ExtensionContext) {
212230
authStatus = true;
213231
updateStatusBarItem(StatusBarStatus.ready, statusBar);
214232
ggshieldViewProvider.refresh();
233+
ggshieldQuotaViewProvider.refresh();
215234
} else {
216235
updateStatusBarItem(StatusBarStatus.unauthenticated, statusBar);
217236
}
@@ -222,7 +241,6 @@ export function activate(context: ExtensionContext) {
222241
outputChannel.appendLine(`Error: ${error.message}`);
223242
updateStatusBarItem(StatusBarStatus.error, statusBar);
224243
});
225-
outputChannel.show();
226244
}
227245

228246
export function deactivate() {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { getAPIquota, ggshieldAuthStatus } from "../lib/ggshield-api";
2+
import { GGShieldConfiguration } from "../lib/ggshield-configuration";
3+
import * as vscode from "vscode";
4+
5+
export class GitGuardianQuotaWebviewProvider
6+
implements vscode.WebviewViewProvider
7+
{
8+
public static readonly viewType = "gitguardian.gitguardianQuotaView";
9+
private _view?: vscode.WebviewView;
10+
private isAuthenticated: boolean = false;
11+
private quota: number = 0;
12+
private isLoading: boolean = false;
13+
14+
constructor(
15+
private ggshieldConfiguration: GGShieldConfiguration,
16+
private readonly _extensionUri: vscode.Uri
17+
) {
18+
this.checkAuthenticationStatus();
19+
this.updateQuota();
20+
}
21+
22+
public async resolveWebviewView(
23+
webviewView: vscode.WebviewView,
24+
context: vscode.WebviewViewResolveContext,
25+
_token: vscode.CancellationToken
26+
) {
27+
this._view = webviewView;
28+
this.refresh();
29+
30+
webviewView.onDidChangeVisibility(() => {
31+
if (webviewView.visible) {
32+
// Refresh the quota when the view becomes visible (e.g., after being collapsed and reopened)
33+
this.refresh();
34+
}
35+
});
36+
}
37+
38+
private async checkAuthenticationStatus() {
39+
this.isAuthenticated = ggshieldAuthStatus(this.ggshieldConfiguration);
40+
}
41+
42+
private async updateQuota() {
43+
if (this.isAuthenticated) {
44+
this.quota = await getAPIquota(this.ggshieldConfiguration);
45+
}
46+
}
47+
48+
private updateWebViewContent(webviewView?: vscode.WebviewView) {
49+
if (webviewView) {
50+
webviewView.webview.html = this.getHtmlForWebview();
51+
}
52+
}
53+
54+
private getHtmlForWebview(): string {
55+
if (this.isLoading) {
56+
return `
57+
<!DOCTYPE html>
58+
<html lang="en">
59+
<body>
60+
<p>Loading...</p>
61+
</body>
62+
</html>`;
63+
}
64+
65+
if (this.isAuthenticated) {
66+
return `
67+
<!DOCTYPE html>
68+
<html lang="en">
69+
<body>
70+
<p>Your current quota: ${this.quota}</p>
71+
</body>
72+
</html>`;
73+
} else {
74+
return `
75+
<!DOCTYPE html>
76+
<html lang="en">
77+
<body>
78+
<p>Please authenticate to see your quota.</p>
79+
</body>
80+
</html>`;
81+
}
82+
}
83+
84+
public async refresh() {
85+
this.isLoading = true;
86+
this.updateWebViewContent(this._view);
87+
88+
await this.checkAuthenticationStatus();
89+
await this.updateQuota();
90+
91+
this.isLoading = false;
92+
this.updateWebViewContent(this._view);
93+
}
94+
95+
dispose(): void {
96+
if (this._view) {
97+
this._view.webview.onDidReceiveMessage(() => {});
98+
this._view.webview.html = "";
99+
this._view = undefined;
100+
}
101+
}
102+
}

src/ggshield-webview/gitguardian-webview-view.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ export class GitGuardianWebviewProvider implements vscode.WebviewViewProvider {
3030

3131
webviewView.webview.options = {
3232
enableScripts: true,
33-
localResourceRoots: [vscode.Uri.joinPath(this._extensionUri, "media"), vscode.Uri.joinPath(this._extensionUri, "images")],
33+
localResourceRoots: [
34+
vscode.Uri.joinPath(this._extensionUri, "media"),
35+
vscode.Uri.joinPath(this._extensionUri, "images"),
36+
],
3437
};
3538

3639
this.updateWebViewContent(webviewView);
@@ -43,8 +46,7 @@ export class GitGuardianWebviewProvider implements vscode.WebviewViewProvider {
4346
}
4447

4548
private async checkAuthenticationStatus() {
46-
this.isAuthenticated = await ggshieldAuthStatus(this.ggshieldConfiguration);
47-
this.updateWebViewContent(this._view);
49+
this.isAuthenticated = ggshieldAuthStatus(this.ggshieldConfiguration);
4850
}
4951

5052
private updateWebViewContent(webviewView?: vscode.WebviewView) {
@@ -58,7 +60,11 @@ export class GitGuardianWebviewProvider implements vscode.WebviewViewProvider {
5860
vscode.Uri.joinPath(this._extensionUri, "media", "main.css")
5961
);
6062
const logoUri = webview.asWebviewUri(
61-
vscode.Uri.joinPath(this._extensionUri, "images", "gitguardian-icon-primary700-background.svg")
63+
vscode.Uri.joinPath(
64+
this._extensionUri,
65+
"images",
66+
"gitguardian-icon-primary700-background.svg"
67+
)
6268
);
6369

6470
if (this.isAuthenticated) {
@@ -111,11 +117,13 @@ export class GitGuardianWebviewProvider implements vscode.WebviewViewProvider {
111117

112118
public refresh() {
113119
this.checkAuthenticationStatus();
120+
121+
this.updateWebViewContent(this._view);
114122
}
115123

116124
dispose(): void {
117125
if (this._view) {
118-
this._view.webview.onDidReceiveMessage(() => { });
126+
this._view.webview.onDidReceiveMessage(() => {});
119127
this._view.webview.html = "";
120128
this._view = undefined;
121129
}

src/lib/ggshield-api.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ export function showAPIQuota(configuration: GGShieldConfiguration): undefined {
6767
}
6868
}
6969

70+
export async function getAPIquota(
71+
configuration: GGShieldConfiguration
72+
): Promise<number> {
73+
try {
74+
const proc = runGGShieldCommand(configuration, ["quota", "--json"]);
75+
return JSON.parse(proc.stdout).remaining;
76+
} catch (e) {
77+
return 0;
78+
}
79+
}
80+
7081
/**
7182
* Ignore last found secrets
7283
*

0 commit comments

Comments
 (0)