Skip to content

Commit b9c0f2b

Browse files
Move InvocationRateLimiter to a separate file
1 parent 81fb126 commit b9c0f2b

File tree

5 files changed

+332
-351
lines changed

5 files changed

+332
-351
lines changed

extensions/ql-vscode/src/codeql-cli/distribution.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ import * as semver from "semver";
66
import { URL } from "url";
77
import { ExtensionContext, Event } from "vscode";
88
import { DistributionConfig } from "../config";
9-
import {
10-
InvocationRateLimiter,
11-
InvocationRateLimiterResultKind,
12-
showAndLogErrorMessage,
13-
showAndLogWarningMessage,
14-
} from "../helpers";
9+
import { showAndLogErrorMessage, showAndLogWarningMessage } from "../helpers";
1510
import { extLogger } from "../common";
1611
import { getCodeQlCliVersion } from "./cli-version";
1712
import {
@@ -24,6 +19,10 @@ import {
2419
extractZipArchive,
2520
getRequiredAssetName,
2621
} from "../pure/distribution";
22+
import {
23+
InvocationRateLimiter,
24+
InvocationRateLimiterResultKind,
25+
} from "../invocation-rate-limiter";
2726

2827
/**
2928
* distribution.ts

extensions/ql-vscode/src/helpers.ts

Lines changed: 1 addition & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,7 @@ import { glob } from "glob";
1010
import { load } from "js-yaml";
1111
import { join, basename, dirname } from "path";
1212
import { dirSync } from "tmp-promise";
13-
import {
14-
ExtensionContext,
15-
Uri,
16-
window as Window,
17-
workspace,
18-
env,
19-
WorkspaceFolder,
20-
} from "vscode";
13+
import { Uri, window as Window, workspace, env, WorkspaceFolder } from "vscode";
2114
import { CodeQLCliServer, QlpacksInfo } from "./codeql-cli/cli";
2215
import { UserCancellationException } from "./common/vscode/progress";
2316
import { extLogger, OutputChannelLogger } from "./common";
@@ -363,106 +356,6 @@ export async function prepareCodeTour(
363356
}
364357
}
365358

366-
/**
367-
* Provides a utility method to invoke a function only if a minimum time interval has elapsed since
368-
* the last invocation of that function.
369-
*/
370-
export class InvocationRateLimiter<T> {
371-
constructor(
372-
extensionContext: ExtensionContext,
373-
funcIdentifier: string,
374-
func: () => Promise<T>,
375-
createDate: (dateString?: string) => Date = (s) =>
376-
s ? new Date(s) : new Date(),
377-
) {
378-
this._createDate = createDate;
379-
this._extensionContext = extensionContext;
380-
this._func = func;
381-
this._funcIdentifier = funcIdentifier;
382-
}
383-
384-
/**
385-
* Invoke the function if `minSecondsSinceLastInvocation` seconds have elapsed since the last invocation.
386-
*/
387-
public async invokeFunctionIfIntervalElapsed(
388-
minSecondsSinceLastInvocation: number,
389-
): Promise<InvocationRateLimiterResult<T>> {
390-
const updateCheckStartDate = this._createDate();
391-
const lastInvocationDate = this.getLastInvocationDate();
392-
if (
393-
minSecondsSinceLastInvocation &&
394-
lastInvocationDate &&
395-
lastInvocationDate <= updateCheckStartDate &&
396-
lastInvocationDate.getTime() + minSecondsSinceLastInvocation * 1000 >
397-
updateCheckStartDate.getTime()
398-
) {
399-
return createRateLimitedResult();
400-
}
401-
const result = await this._func();
402-
await this.setLastInvocationDate(updateCheckStartDate);
403-
return createInvokedResult(result);
404-
}
405-
406-
private getLastInvocationDate(): Date | undefined {
407-
const maybeDateString: string | undefined =
408-
this._extensionContext.globalState.get(
409-
InvocationRateLimiter._invocationRateLimiterPrefix +
410-
this._funcIdentifier,
411-
);
412-
return maybeDateString ? this._createDate(maybeDateString) : undefined;
413-
}
414-
415-
private async setLastInvocationDate(date: Date): Promise<void> {
416-
return await this._extensionContext.globalState.update(
417-
InvocationRateLimiter._invocationRateLimiterPrefix + this._funcIdentifier,
418-
date,
419-
);
420-
}
421-
422-
private readonly _createDate: (dateString?: string) => Date;
423-
private readonly _extensionContext: ExtensionContext;
424-
private readonly _func: () => Promise<T>;
425-
private readonly _funcIdentifier: string;
426-
427-
private static readonly _invocationRateLimiterPrefix =
428-
"invocationRateLimiter_lastInvocationDate_";
429-
}
430-
431-
export enum InvocationRateLimiterResultKind {
432-
Invoked,
433-
RateLimited,
434-
}
435-
436-
/**
437-
* The function was invoked and returned the value `result`.
438-
*/
439-
interface InvokedResult<T> {
440-
kind: InvocationRateLimiterResultKind.Invoked;
441-
result: T;
442-
}
443-
444-
/**
445-
* The function was not invoked as the minimum interval since the last invocation had not elapsed.
446-
*/
447-
interface RateLimitedResult {
448-
kind: InvocationRateLimiterResultKind.RateLimited;
449-
}
450-
451-
type InvocationRateLimiterResult<T> = InvokedResult<T> | RateLimitedResult;
452-
453-
function createInvokedResult<T>(result: T): InvokedResult<T> {
454-
return {
455-
kind: InvocationRateLimiterResultKind.Invoked,
456-
result,
457-
};
458-
}
459-
460-
function createRateLimitedResult(): RateLimitedResult {
461-
return {
462-
kind: InvocationRateLimiterResultKind.RateLimited,
463-
};
464-
}
465-
466359
export interface QlPacksForLanguage {
467360
/** The name of the pack containing the dbscheme. */
468361
dbschemePack: string;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { ExtensionContext } from "vscode";
2+
3+
/**
4+
* Provides a utility method to invoke a function only if a minimum time interval has elapsed since
5+
* the last invocation of that function.
6+
*/
7+
export class InvocationRateLimiter<T> {
8+
constructor(
9+
private readonly extensionContext: ExtensionContext,
10+
private readonly funcIdentifier: string,
11+
private readonly func: () => Promise<T>,
12+
private readonly createDate: (dateString?: string) => Date = (s) =>
13+
s ? new Date(s) : new Date(),
14+
) {}
15+
16+
/**
17+
* Invoke the function if `minSecondsSinceLastInvocation` seconds have elapsed since the last invocation.
18+
*/
19+
public async invokeFunctionIfIntervalElapsed(
20+
minSecondsSinceLastInvocation: number,
21+
): Promise<InvocationRateLimiterResult<T>> {
22+
const updateCheckStartDate = this.createDate();
23+
const lastInvocationDate = this.getLastInvocationDate();
24+
if (
25+
minSecondsSinceLastInvocation &&
26+
lastInvocationDate &&
27+
lastInvocationDate <= updateCheckStartDate &&
28+
lastInvocationDate.getTime() + minSecondsSinceLastInvocation * 1000 >
29+
updateCheckStartDate.getTime()
30+
) {
31+
return createRateLimitedResult();
32+
}
33+
const result = await this.func();
34+
await this.setLastInvocationDate(updateCheckStartDate);
35+
return createInvokedResult(result);
36+
}
37+
38+
private getLastInvocationDate(): Date | undefined {
39+
const maybeDateString: string | undefined =
40+
this.extensionContext.globalState.get(
41+
InvocationRateLimiter._invocationRateLimiterPrefix +
42+
this.funcIdentifier,
43+
);
44+
return maybeDateString ? this.createDate(maybeDateString) : undefined;
45+
}
46+
47+
private async setLastInvocationDate(date: Date): Promise<void> {
48+
return await this.extensionContext.globalState.update(
49+
InvocationRateLimiter._invocationRateLimiterPrefix + this.funcIdentifier,
50+
date,
51+
);
52+
}
53+
54+
private static readonly _invocationRateLimiterPrefix =
55+
"invocationRateLimiter_lastInvocationDate_";
56+
}
57+
58+
export enum InvocationRateLimiterResultKind {
59+
Invoked,
60+
RateLimited,
61+
}
62+
63+
/**
64+
* The function was invoked and returned the value `result`.
65+
*/
66+
interface InvokedResult<T> {
67+
kind: InvocationRateLimiterResultKind.Invoked;
68+
result: T;
69+
}
70+
71+
/**
72+
* The function was not invoked as the minimum interval since the last invocation had not elapsed.
73+
*/
74+
interface RateLimitedResult {
75+
kind: InvocationRateLimiterResultKind.RateLimited;
76+
}
77+
78+
type InvocationRateLimiterResult<T> = InvokedResult<T> | RateLimitedResult;
79+
80+
function createInvokedResult<T>(result: T): InvokedResult<T> {
81+
return {
82+
kind: InvocationRateLimiterResultKind.Invoked,
83+
result,
84+
};
85+
}
86+
87+
function createRateLimitedResult(): RateLimitedResult {
88+
return {
89+
kind: InvocationRateLimiterResultKind.RateLimited,
90+
};
91+
}

0 commit comments

Comments
 (0)