diff --git a/package.json b/package.json index 1d63450..b9ece01 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,12 @@ ], "scope": "window", "description": "Controls if a new sub-folder should be created for the newly generated project." + }, + "spring.initializr.useApiDefaults": { + "default": false, + "type": "boolean", + "scope": "window", + "description": "Use default values provided by Spring Initializr API for Spring Boot version, language, Java version and packaging. When specific defaults are set for each configuration, those take precedence over the API defaults." } } } diff --git a/src/handler/GenerateProjectHandler.ts b/src/handler/GenerateProjectHandler.ts index 368ba2d..6d3c325 100644 --- a/src/handler/GenerateProjectHandler.ts +++ b/src/handler/GenerateProjectHandler.ts @@ -29,10 +29,14 @@ export class GenerateProjectHandler extends BaseHandler { constructor(projectType: "maven-project" | "gradle-project", defaults?: IDefaultProjectData) { super(); this.projectType = projectType; + + const settings = vscode.workspace.getConfiguration("spring.initializr"); + this.metadata = { pickSteps: [], defaults: defaults || {}, - parentFolder: vscode.workspace.getConfiguration("spring.initializr").get("parentFolder") + parentFolder: settings.get("parentFolder"), + useApiDefaults: settings.get("useApiDefaults") }; } diff --git a/src/handler/HandlerInterfaces.ts b/src/handler/HandlerInterfaces.ts index 96b6f8c..94a31e2 100644 --- a/src/handler/HandlerInterfaces.ts +++ b/src/handler/HandlerInterfaces.ts @@ -22,6 +22,7 @@ export interface IProjectMetadata { pickSteps: IStep[]; defaults: IDefaultProjectData; parentFolder?: ParentFolder; + useApiDefaults?: boolean; } export interface IDefaultProjectData { @@ -37,6 +38,7 @@ export interface IDefaultProjectData { export interface IHandlerItem extends QuickPickItem { label: string; value?: T; + default?: boolean; } export interface IPickMetadata { diff --git a/src/handler/SpecifyBootVersionStep.ts b/src/handler/SpecifyBootVersionStep.ts index bab4610..a1d9181 100644 --- a/src/handler/SpecifyBootVersionStep.ts +++ b/src/handler/SpecifyBootVersionStep.ts @@ -3,7 +3,7 @@ import { instrumentOperationStep, sendInfo } from "vscode-extension-telemetry-wrapper"; import { serviceManager } from "../model"; -import { BootVersion, MatadataType } from "../model/Metadata"; +import { BootVersion, MetadataType } from "../model/Metadata"; import { IPickMetadata, IProjectMetadata, IStep } from "./HandlerInterfaces"; import { SpecifyLanguageStep } from "./SpecifyLanguageStep"; import { createPickBox } from "./utils"; @@ -29,13 +29,25 @@ export class SpecifyBootVersionStep implements IStep { } private async specifyBootVersion(projectMetadata: IProjectMetadata): Promise { + const items = await serviceManager.getItems(projectMetadata.serviceUrl, MetadataType.BOOTVERSION); + + if (projectMetadata.useApiDefaults === true) { + const recommendedBootVersion: string = items.find(x => x.default === true)?.value?.id; + + if (recommendedBootVersion) { + projectMetadata.bootVersion = recommendedBootVersion; + return true; + } + } + const pickMetaData: IPickMetadata = { metadata: projectMetadata, title: "Spring Initializr: Specify Spring Boot version", pickStep: SpecifyBootVersionStep.getInstance(), placeholder: "Specify Spring Boot version.", - items: serviceManager.getItems(projectMetadata.serviceUrl, MatadataType.BOOTVERSION), + items: items, }; + return await createPickBox(pickMetaData); } } diff --git a/src/handler/SpecifyJavaVersionStep.ts b/src/handler/SpecifyJavaVersionStep.ts index 579a368..f11da97 100644 --- a/src/handler/SpecifyJavaVersionStep.ts +++ b/src/handler/SpecifyJavaVersionStep.ts @@ -4,7 +4,7 @@ import { workspace } from "vscode"; import { instrumentOperationStep } from "vscode-extension-telemetry-wrapper"; import { serviceManager } from "../model"; -import { JavaVersion, MatadataType } from "../model/Metadata"; +import { JavaVersion, MetadataType } from "../model/Metadata"; import { IPickMetadata, IProjectMetadata, IStep } from "./HandlerInterfaces"; import { SpecifyDependenciesStep } from "./SpecifyDependenciesStep"; import { createPickBox } from "./utils"; @@ -30,17 +30,31 @@ export class SpecifyJavaVersionStep implements IStep { private async specifyJavaVersion(projectMetadata: IProjectMetadata): Promise { const javaVersion: string = projectMetadata.defaults.javaVersion || workspace.getConfiguration("spring.initializr").get("defaultJavaVersion"); + if (javaVersion) { projectMetadata.javaVersion = javaVersion; return true; } + + const items = await serviceManager.getItems(projectMetadata.serviceUrl, MetadataType.JAVAVERSION); + + if (projectMetadata.useApiDefaults === true) { + const recommendedJavaVersion: string = items.find(x => x.default === true)?.value?.id; + + if (recommendedJavaVersion) { + projectMetadata.javaVersion = recommendedJavaVersion; + return true; + } + } + const pickMetaData: IPickMetadata = { metadata: projectMetadata, title: "Spring Initializr: Specify Java version", pickStep: SpecifyJavaVersionStep.getInstance(), placeholder: "Specify Java version.", - items: serviceManager.getItems(projectMetadata.serviceUrl, MatadataType.JAVAVERSION), + items: items }; + return await createPickBox(pickMetaData); } } diff --git a/src/handler/SpecifyLanguageStep.ts b/src/handler/SpecifyLanguageStep.ts index 2955ce1..f834c2d 100644 --- a/src/handler/SpecifyLanguageStep.ts +++ b/src/handler/SpecifyLanguageStep.ts @@ -4,7 +4,7 @@ import { workspace } from "vscode"; import { instrumentOperationStep } from "vscode-extension-telemetry-wrapper"; import { serviceManager } from "../model"; -import { Language, MatadataType } from "../model/Metadata"; +import { Language, MetadataType } from "../model/Metadata"; import { IPickMetadata, IProjectMetadata, IStep } from "./HandlerInterfaces"; import { SpecifyGroupIdStep } from "./SpecifyGroupIdStep"; import { createPickBox } from "./utils"; @@ -30,17 +30,31 @@ export class SpecifyLanguageStep implements IStep { private async specifyLanguage(projectMetadata: IProjectMetadata): Promise { const language: string = projectMetadata.defaults.language || workspace.getConfiguration("spring.initializr").get("defaultLanguage"); + if (language) { projectMetadata.language = language && language.toLowerCase(); return true; } + + const items = await serviceManager.getItems(projectMetadata.serviceUrl, MetadataType.LANGUAGE); + + if (projectMetadata.useApiDefaults === true) { + const recommendedLanguage: string = items.find(x => x.default === true)?.label.toLowerCase(); + + if (recommendedLanguage) { + projectMetadata.language = recommendedLanguage; + return true; + } + } + const pickMetaData: IPickMetadata = { metadata: projectMetadata, title: "Spring Initializr: Specify project language", pickStep: SpecifyLanguageStep.getInstance(), placeholder: "Specify project language.", - items: serviceManager.getItems(projectMetadata.serviceUrl, MatadataType.LANGUAGE), + items: items }; + return await createPickBox(pickMetaData); } } diff --git a/src/handler/SpecifyPackagingStep.ts b/src/handler/SpecifyPackagingStep.ts index 59711a8..429f6bf 100644 --- a/src/handler/SpecifyPackagingStep.ts +++ b/src/handler/SpecifyPackagingStep.ts @@ -4,7 +4,7 @@ import { workspace } from "vscode"; import { instrumentOperationStep } from "vscode-extension-telemetry-wrapper"; import { serviceManager } from "../model"; -import { MatadataType, Packaging } from "../model/Metadata"; +import { MetadataType, Packaging } from "../model/Metadata"; import { IPickMetadata, IProjectMetadata, IStep } from "./HandlerInterfaces"; import { SpecifyJavaVersionStep } from "./SpecifyJavaVersionStep"; import { createPickBox } from "./utils"; @@ -30,17 +30,31 @@ export class SpecifyPackagingStep implements IStep { private async specifyPackaging(projectMetadata: IProjectMetadata): Promise { const packaging: string = projectMetadata.defaults.packaging || workspace.getConfiguration("spring.initializr").get("defaultPackaging"); + if (packaging) { projectMetadata.packaging = packaging && packaging.toLowerCase(); return true; } + + const items = await serviceManager.getItems(projectMetadata.serviceUrl, MetadataType.PACKAGING); + + if (projectMetadata.useApiDefaults === true) { + const recommendedPackaging: string = items.find(x => x.default === true)?.label?.toLowerCase(); + + if (recommendedPackaging) { + projectMetadata.packaging = recommendedPackaging; + return true; + } + } + const pickMetaData: IPickMetadata = { metadata: projectMetadata, title: "Spring Initializr: Specify packaging type", pickStep: SpecifyPackagingStep.getInstance(), placeholder: "Specify packaging type.", - items: serviceManager.getItems(projectMetadata.serviceUrl, MatadataType.PACKAGING), + items: items }; + return await createPickBox(pickMetaData); } } diff --git a/src/model/Metadata.ts b/src/model/Metadata.ts index 30707a9..b6108d8 100644 --- a/src/model/Metadata.ts +++ b/src/model/Metadata.ts @@ -13,7 +13,7 @@ export interface Metadata { type: Category; } -export enum MatadataType { +export enum MetadataType { BOOTVERSION, JAVAVERSION, LANGUAGE, diff --git a/src/model/ServiceManager.ts b/src/model/ServiceManager.ts index 4bda7db..5e60d32 100644 --- a/src/model/ServiceManager.ts +++ b/src/model/ServiceManager.ts @@ -6,7 +6,7 @@ import { IDependency, IStarters } from "."; import { IHandlerItem } from "../handler/HandlerInterfaces"; import { downloadFile } from "../Utils"; import { matchRange } from "../Utils/VersionHelper"; -import { DependencyGroup, Identifiable, MatadataType, Metadata } from "./Metadata"; +import { DependencyGroup, Identifiable, MetadataType, Metadata } from "./Metadata"; /** * Prefer v2.2 and fallback to v2.1 @@ -17,7 +17,7 @@ const METADATA_HEADERS = { Accept: "application/vnd.initializr.v2.2+json,applica class ServiceManager { private metadataMap: Map = new Map(); - public async getItems(serviceUrl: string, type: MatadataType): Promise>> { + public async getItems(serviceUrl: string, type: MetadataType): Promise>> { const metadata = await this.ensureMetadata(serviceUrl); if (!metadata) { throw new Error("Failed to fetch metadata."); @@ -25,19 +25,19 @@ class ServiceManager { let defaultLabel: string; let values: any[]; switch (type) { - case MatadataType.BOOTVERSION: + case MetadataType.BOOTVERSION: defaultLabel = metadata.bootVersion.default; values = metadata.bootVersion.values; break; - case MatadataType.JAVAVERSION: + case MetadataType.JAVAVERSION: defaultLabel = metadata.javaVersion.default; values = metadata.javaVersion.values; break; - case MatadataType.LANGUAGE: + case MetadataType.LANGUAGE: defaultLabel = metadata.language.default; values = metadata.language.values; break; - case MatadataType.PACKAGING: + case MetadataType.PACKAGING: defaultLabel = metadata.packaging.default; values = metadata.packaging.values; break; @@ -45,16 +45,29 @@ class ServiceManager { throw new Error("Invalid metadata type."); } - const sortedValues = values.filter(x => x.id === defaultLabel).concat(values.filter(x => x.id !== defaultLabel)); + const defaultValues = values.filter(x => x.id === defaultLabel); + const nonDefaultValues = values.filter(x => x.id !== defaultLabel); + + let mappedDefault: Array> = this.mapValues(defaultValues, type, true); + let mappedNonDefault: Array> = this.mapValues(nonDefaultValues, type, false); + + return mappedDefault.concat(mappedNonDefault); + } + + private mapValues( + items: any[], + type: MetadataType, + isDefault: boolean + ): Array> { switch (type) { - case MatadataType.BOOTVERSION: - return sortedValues.map(v => ({ value: v, label: v.name })); - case MatadataType.JAVAVERSION: - return sortedValues.map(v => ({ value: v, label: v.name })); + case MetadataType.BOOTVERSION: + return items.map(v => ({ value: v, label: v.name, default: isDefault})) + case MetadataType.JAVAVERSION: + return items.map(v => ({ value: v, label: v.name, default: isDefault })); default: - return sortedValues.map(v => ({ label: v.name })); + return items.map(v => ({ label: v.name, default: isDefault })); } - } + } public async getAvailableDependencies(serviceUrl: string, bootVersion: string): Promise { const metadata = await this.ensureMetadata(serviceUrl);