Skip to content

Commit b6b3cff

Browse files
feat(promotionProvider): fetch dependency check metadata from remote
1 parent 9c50a29 commit b6b3cff

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

src/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { promotionProvider } from "./promotionProvider";
2626

2727
export async function activate(context: ExtensionContext): Promise<void> {
2828
contextManager.initialize(context);
29+
promotionProvider.initialize(context);
2930
await initializeFromJsonFile(context.asAbsolutePath("./package.json"));
3031
await initExpService(context);
3132
await instrumentOperation("activation", activateExtension)(context);

src/promotionProvider.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,37 @@ import {
77
Diagnostic,
88
Range,
99
DiagnosticSeverity,
10-
Uri
10+
Uri,
11+
type ExtensionContext
1112
} from "vscode";
1213
import * as semver from "semver";
1314
import type { INodeData } from "../extension.bundle";
15+
import { fetchDependencyCheckMetadata, type DependencyCheckMetadata } from "./utils/requestUtils";
1416

1517
const EARLIEST_JAVA_VERSION_NOT_TO_PROMPT = 21;
1618
const JAVA_UPGRADE_EXTENSION_ID = "vscjava.vscode-java-upgrade";
17-
const PACKAGES_TO_CHECK_FOR_EOL: Record<string, { name: string, supportedVersion: string }> = {
18-
// https://spring.io/projects/spring-boot#support
19-
"org.springframework.boot": {
20-
name: "Spring Boot",
21-
supportedVersion: "2.7.x || >=3.2.x"
22-
},
23-
// https://spring.io/projects/spring-framework#support
24-
"org.springframework": {
25-
name: "Spring Framework",
26-
supportedVersion: "5.3.x || >=6.2.x"
27-
},
28-
};
19+
const METADATA_UPDATE_INTERVAL_IN_DAYS = 7;
20+
const METADATA_STORAGE_KEY = "dependencyCheckMetadata";
2921

22+
type MementoItem<T> = {
23+
lastUpdatedTs: number;
24+
data: T;
25+
}
3026

3127
export class PromotionProvider implements Disposable {
3228
public static getInstance() {
3329
return promotionProvider;
3430
}
3531

32+
private context: ExtensionContext;
3633
private haveAlreadyPrompted = false;
37-
private diagnostics = languages.createDiagnosticCollection('javaUpgrade')
34+
private diagnostics = languages.createDiagnosticCollection('javaUpgrade');
35+
private dependencyCheckMetadata: DependencyCheckMetadata = {};
36+
37+
public initialize(context: ExtensionContext) {
38+
this.context = context;
39+
this.refreshDependencyCheckMetadata();
40+
}
3841

3942
public dispose() {
4043
this.diagnostics.dispose();
@@ -61,7 +64,7 @@ export class PromotionProvider implements Disposable {
6164
public checkDependencyVersion(data: INodeData) {
6265
const versionString = data.metaData?.["maven.version"];
6366
const groupId = data.metaData?.["maven.groupId"];
64-
const supportedVersionDefinition = PACKAGES_TO_CHECK_FOR_EOL[groupId];
67+
const supportedVersionDefinition = this.dependencyCheckMetadata[groupId];
6568
if (!versionString || !groupId || !supportedVersionDefinition) {
6669
return;
6770
}
@@ -70,7 +73,23 @@ export class PromotionProvider implements Disposable {
7073
return;
7174
}
7275
if (!semver.satisfies(currentVersion, supportedVersionDefinition.supportedVersion)) {
73-
this.triggerFrameworkVersionUpgrade(groupId, versionString);
76+
this.triggerFrameworkVersionUpgrade(supportedVersionDefinition.name, versionString);
77+
}
78+
}
79+
80+
private async refreshDependencyCheckMetadata(): Promise<void> {
81+
const metadata = this.context.globalState.get(METADATA_STORAGE_KEY) as MementoItem<DependencyCheckMetadata> | undefined;
82+
const nowTs = Number(new Date()) / 1000;
83+
if (!metadata || (nowTs - (metadata?.lastUpdatedTs ?? 0)) > METADATA_UPDATE_INTERVAL_IN_DAYS * 24 * 60 * 60) {
84+
const newMetadata = await fetchDependencyCheckMetadata();
85+
this.context.globalState.update(METADATA_STORAGE_KEY, {
86+
lastUpdatedTs: nowTs,
87+
data: newMetadata
88+
});
89+
this.dependencyCheckMetadata = newMetadata;
90+
return;
91+
} else {
92+
this.dependencyCheckMetadata = metadata.data ?? {};
7493
}
7594
}
7695

@@ -84,10 +103,9 @@ export class PromotionProvider implements Disposable {
84103
}
85104
}
86105

87-
private triggerFrameworkVersionUpgrade(groupId: string, currentVersion: string) {
106+
private triggerFrameworkVersionUpgrade(frameworkName: string, currentVersion: string) {
88107
if (!this.haveAlreadyPrompted) {
89108
this.haveAlreadyPrompted = true;
90-
const frameworkName = PACKAGES_TO_CHECK_FOR_EOL[groupId].name;
91109
this.promptForUpgrade(`Your project's ${frameworkName} version (${currentVersion}) has reached end-of-life. Consider upgrading using Java Upgrade Tool for better security and performance.`,
92110
`Upgrade ${frameworkName} to a supported version with Java Upgrade Tool`)
93111
}

src/utils/requestUtils.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import type { IncomingMessage } from "http";
5+
import * as https from "https";
6+
7+
// TODO: change
8+
const URL_DEPENDENCY_CHECK_METADATA = "https://gist.githubusercontent.com/FluoriteCafe-work/6126d5d93fe4e35c25c431d98bb9ff7e/raw/c01f7db84044a41e30b6c88fd9ee3cb4841d9e4d/data.json";
9+
10+
export type DependencyCheckMetadata = Record<string, { name: string, supportedVersion: string }>
11+
12+
export async function fetchDependencyCheckMetadata(): Promise<DependencyCheckMetadata> {
13+
const raw = await httpsGet(URL_DEPENDENCY_CHECK_METADATA);
14+
try {
15+
return JSON.parse(raw);
16+
} catch (error) {
17+
console.error(error);
18+
return {};
19+
}
20+
}
21+
22+
// TODO: switch to `fetch()` when we are confident users are using Node 18+
23+
async function httpsGet(urlString: string): Promise<string> {
24+
return new Promise<string>((resolve, reject) => {
25+
let result = "";
26+
https.get(urlString, {
27+
headers: {
28+
'User-Agent': 'vscode-java-dependency/0.1',
29+
}
30+
}, (res: IncomingMessage) => {
31+
res.on("data", chunk => {
32+
result = result.concat(chunk.toString());
33+
});
34+
res.on("end", () => {
35+
resolve(result);
36+
});
37+
res.on("error", err => {
38+
reject(err);
39+
});
40+
});
41+
});
42+
}

0 commit comments

Comments
 (0)