diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 40f15f8ac..eee29de86 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,6 +1,8 @@ # CodeQL for Visual Studio Code: Changelog -## 1.0.3 +## 1.0.4 + +## 1.0.3 - 13 January 2020 - Reduce the frequency of CodeQL CLI update checks to help avoid hitting GitHub API limits of 60 requests per hour for unauthenticated IPs. diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 3c2141fab..e5f88f353 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.0.3", + "version": "1.0.4", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index 683b1ee03..027dcfff3 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -140,7 +140,12 @@ export function getQueryName(info: EvaluationInfo) { * the last invocation of that function. */ export class InvocationRateLimiter { - constructor(extensionContext: ExtensionContext, funcIdentifier: string, func: () => Promise) { + constructor( + extensionContext: ExtensionContext, + funcIdentifier: string, + func: () => Promise, + createDate: (dateString?: string) => Date = s => s ? new Date(s) : new Date()) { + this._createDate = createDate; this._extensionContext = extensionContext; this._func = func; this._funcIdentifier = funcIdentifier; @@ -150,7 +155,7 @@ export class InvocationRateLimiter { * Invoke the function if `minSecondsSinceLastInvocation` seconds have elapsed since the last invocation. */ public async invokeFunctionIfIntervalElapsed(minSecondsSinceLastInvocation: number): Promise> { - const updateCheckStartDate = new Date(); + const updateCheckStartDate = this._createDate(); const lastInvocationDate = this.getLastInvocationDate(); if (minSecondsSinceLastInvocation && lastInvocationDate && lastInvocationDate <= updateCheckStartDate && lastInvocationDate.getTime() + minSecondsSinceLastInvocation * 1000 > updateCheckStartDate.getTime()) { @@ -162,15 +167,16 @@ export class InvocationRateLimiter { } private getLastInvocationDate(): Date | undefined { - const maybeDate: Date | undefined = + const maybeDateString: string | undefined = this._extensionContext.globalState.get(InvocationRateLimiter._invocationRateLimiterPrefix + this._funcIdentifier); - return maybeDate ? new Date(maybeDate) : undefined; + return maybeDateString ? this._createDate(maybeDateString) : undefined; } private async setLastInvocationDate(date: Date): Promise { return await this._extensionContext.globalState.update(InvocationRateLimiter._invocationRateLimiterPrefix + this._funcIdentifier, date); } + private readonly _createDate: (dateString?: string) => Date; private readonly _extensionContext: ExtensionContext; private readonly _func: () => Promise; private readonly _funcIdentifier: string; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts index 242a6201a..5b978b876 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts @@ -4,8 +4,19 @@ import { ExtensionContext, Memento } from "vscode"; import { InvocationRateLimiter } from "../../helpers"; describe("Invocation rate limiter", () => { + // 1 January 2020 + let currentUnixTime = 1577836800; + + function createDate(dateString?: string): Date { + if (dateString) { + return new Date(dateString); + } + const numMillisecondsPerSecond = 1000; + return new Date(currentUnixTime * numMillisecondsPerSecond); + } + function createInvocationRateLimiter(funcIdentifier: string, func: () => Promise): InvocationRateLimiter { - return new InvocationRateLimiter(new MockExtensionContext(), funcIdentifier, func); + return new InvocationRateLimiter(new MockExtensionContext(), funcIdentifier, func, s => createDate(s)); } it("initially invokes function", async () => { @@ -17,7 +28,7 @@ describe("Invocation rate limiter", () => { expect(numTimesFuncCalled).to.equal(1); }); - it("doesn't invoke function within time period", async () => { + it("doesn't invoke function again if no time has passed", async () => { let numTimesFuncCalled = 0; const invocationRateLimiter = createInvocationRateLimiter("funcid", async () => { numTimesFuncCalled++; @@ -27,7 +38,18 @@ describe("Invocation rate limiter", () => { expect(numTimesFuncCalled).to.equal(1); }); - it("invoke function again after 0s time period has elapsed", async () => { + it("doesn't invoke function again if requested time since last invocation hasn't passed", async () => { + let numTimesFuncCalled = 0; + const invocationRateLimiter = createInvocationRateLimiter("funcid", async () => { + numTimesFuncCalled++; + }); + await invocationRateLimiter.invokeFunctionIfIntervalElapsed(100); + currentUnixTime += 1; + await invocationRateLimiter.invokeFunctionIfIntervalElapsed(2); + expect(numTimesFuncCalled).to.equal(1); + }); + + it("invokes function again immediately if requested time since last invocation is 0 seconds", async () => { let numTimesFuncCalled = 0; const invocationRateLimiter = createInvocationRateLimiter("funcid", async () => { numTimesFuncCalled++; @@ -37,13 +59,13 @@ describe("Invocation rate limiter", () => { expect(numTimesFuncCalled).to.equal(2); }); - it("invoke function again after 1s time period has elapsed", async () => { + it("invokes function again after requested time since last invocation has elapsed", async () => { let numTimesFuncCalled = 0; const invocationRateLimiter = createInvocationRateLimiter("funcid", async () => { numTimesFuncCalled++; }); await invocationRateLimiter.invokeFunctionIfIntervalElapsed(1); - await new Promise((resolve, _reject) => setTimeout(() => resolve(), 1000)); + currentUnixTime += 1; await invocationRateLimiter.invokeFunctionIfIntervalElapsed(1); expect(numTimesFuncCalled).to.equal(2); });