diff --git a/CHANGELOG.md b/CHANGELOG.md index 18e0eab..e426b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to the MicroProfile Starter extension will be documented below. +## 0.2.5 +- MicroProfile 3.3 +- Support for generating a WildFly project +- Better support for selecting a JavaSE version +- Use version 4 of the MicroProfile Starter API + ## 0.2.4 - Use version 3 of the MicroProfile Starter API when generating a project - Update dependencies diff --git a/README.md b/README.md index 076cdeb..0adba3f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # VS Code MicroProfile Starter Extension [![Marketplace Version](https://vsmarketplacebadge.apphb.com/version/MicroProfile-Community.mp-starter-vscode-ext.svg "Current Release")](https://marketplace.visualstudio.com/items?itemName=MicroProfile-Community.mp-starter-vscode-ext) +[![Marketplace Installs](https://vsmarketplacebadge.apphb.com/installs-short/MicroProfile-Community.mp-starter-vscode-ext.svg "Installs")](https://marketplace.visualstudio.com/items?itemName=MicroProfile-Community.mp-starter-vscode-ext) [![Build Status](https://travis-ci.org/MicroShed/mp-starter-vscode-ext.svg?branch=master)](https://travis-ci.org/MicroShed/mp-starter-vscode-ext) The MicroProfile Starter extension provides support for generating a MicroProfile Maven project with examples based on the Eclipse MicroProfile Starter project (https://start.microprofile.io/) by the MicroProfile community. You will be able to generate a project by choosing a MicroProfile version, server and specifications, such as CDI, Config, Health, Metrics, and more. The code for this extension is hosted under the MicroShed organization on GitHub. diff --git a/package-lock.json b/package-lock.json index 3180be2..44857e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mp-starter-vscode-ext", - "version": "0.2.4", + "version": "0.2.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 034079d..30927a4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "mp-starter-vscode-ext", "displayName": "MicroProfile Starter", "description": "Generate Java Microservice Maven projects using Eclipse MicroProfile", - "version": "0.2.4", + "version": "0.2.5", "publisher": "MicroProfile-Community", "preview": true, "license": "Apache-2.0", diff --git a/src/commands/generateProject.ts b/src/commands/generateProject.ts index 495973d..325ef84 100644 --- a/src/commands/generateProject.ts +++ b/src/commands/generateProject.ts @@ -2,23 +2,12 @@ import * as vscode from "vscode"; import * as util from "../util/util"; import * as prompts from "../util/vscodePrompts"; import * as path from "path"; -import fetch from "node-fetch"; -import { MP_STARTER_API_ROOT, OPEN_NEW_PROJECT_OPTIONS, EXTENSION_USER_AGENT } from "../constants"; +import { OPEN_NEW_PROJECT_OPTIONS, ERRORS } from "../constants"; +import * as mpStarterApi from "../util/mpStarterApi"; export async function generateProject(): Promise { try { - const mpSupportResponse = await fetch(`${MP_STARTER_API_ROOT}/supportMatrix`, { - method: "GET", - headers: { - "User-Agent": EXTENSION_USER_AGENT, - }, - }); - if (mpSupportResponse.status >= 400 && mpSupportResponse.status < 600) { - throw new Error(`Bad response ${mpSupportResponse.status}: ${mpSupportResponse.statusText}`); - } - - const mpSupportMatrix = await mpSupportResponse.json(); - + const mpSupportMatrix = await mpStarterApi.getSupportMatrix(); // mpConfigurations is a map of mp version -> mp configuration const mpConfigurations = mpSupportMatrix.configs; const allMpVersions = Object.keys(mpConfigurations); @@ -44,18 +33,20 @@ export async function generateProject(): Promise { return; } - const javaSEVersion = await prompts.askForJavaSEVersion(mpVersion, mpServer); + // gets support information about which JavaSE versions / microprofile specs are supported by the + // users selected mp server / mp version combination + const { javaSEVersions, mpSpecs } = await mpStarterApi.getSupportedJavaAndSpecs( + mpServer, + mpVersion + ); + + const javaSEVersion = await prompts.askForJavaSEVersion(javaSEVersions); if (javaSEVersion === undefined) { return; } - // ask user to pick a list of mp specifications to use for the given version of mp they selected - const allSupportedSpecs = mpConfigurations[mpVersion].specs; const specDescriptions = mpSupportMatrix.descriptions; - const mpSpecifications = await prompts.askForMPSpecifications( - allSupportedSpecs, - specDescriptions - ); + const mpSpecifications = await prompts.askForMPSpecifications(mpSpecs, specDescriptions); if (mpSpecifications === undefined) { return; } @@ -67,7 +58,7 @@ export async function generateProject(): Promise { const targetDirString = targetFolder.fsPath; - const requestPayload = { + const projectOptions = { groupId: groupId, artifactId: artifactId, mpVersion: mpVersion, @@ -80,16 +71,6 @@ export async function generateProject(): Promise { // location to download the zip file const zipPath = path.join(targetDirString, zipName); - const requestOptions = { - url: `${MP_STARTER_API_ROOT}/project`, - method: "POST", - headers: { - "Content-Type": "application/json", - "User-Agent": EXTENSION_USER_AGENT, - }, - body: JSON.stringify(requestPayload), - }; - // show a progress bar as the zip file is being downloaded await vscode.window.withProgress( { @@ -97,44 +78,47 @@ export async function generateProject(): Promise { title: "Generating the MicroProfile Starter project...", cancellable: false, }, - () => util.downloadFile(requestOptions, zipPath) + () => mpStarterApi.downloadMPStarterProjectZip(projectOptions, zipPath) ); + const targetDirFolder = path.join(targetDirString, artifactId); + try { - const targetDirFolder = path.join(targetDirString, artifactId); await util.unzipFile(zipPath, targetDirString, targetDirFolder); - try { - await util.deleteFile(zipPath); - } catch (e) { - console.error(e); - vscode.window.showErrorMessage(`Failed to delete file ${zipName}`); - } - - // open the unzipped folder in a new VS Code window - const uriPath = vscode.Uri.file(targetDirFolder); - // prompt user whether they want to add project to current workspace or open in a new window - const selection = await vscode.window.showInformationMessage( - "MicroProfile Starter project generated. Would you like to add your project to the current workspace or open it in a new window?", - ...[ - OPEN_NEW_PROJECT_OPTIONS.ADD_CURRENT_WORKSPACE, - OPEN_NEW_PROJECT_OPTIONS.OPEN_NEW_WINDOW, - ] - ); - if (selection === OPEN_NEW_PROJECT_OPTIONS.ADD_CURRENT_WORKSPACE) { - vscode.workspace.updateWorkspaceFolders(0, 0, { uri: uriPath }); - } else if (selection === OPEN_NEW_PROJECT_OPTIONS.OPEN_NEW_WINDOW) { - await vscode.commands.executeCommand("vscode.openFolder", uriPath, true); - } - } catch (err) { - console.error(err); - vscode.window.showErrorMessage("Failed to extract the MicroProfile Starter project."); + } catch (e) { + console.error(e); + const err = new Error("Unable to extract MicroProfile Starter project"); + err.name = ERRORS.EXTRACT_PROJECT_ERROR; + throw err; + } + + // if failed to delete the zip, no need to error out but show a warning to users + try { + await util.deleteFile(zipPath); + } catch (e) { + console.error(e); + vscode.window.showErrorMessage(`Failed to delete file ${zipName}`); + } + + const uriPath = vscode.Uri.file(targetDirFolder); + // prompt user whether they want to add project to current workspace or open in a new window + const selection = await vscode.window.showInformationMessage( + "MicroProfile Starter project generated. Add your project to the current workspace or open it in a new window?", + ...[OPEN_NEW_PROJECT_OPTIONS.ADD_CURRENT_WORKSPACE, OPEN_NEW_PROJECT_OPTIONS.OPEN_NEW_WINDOW] + ); + if (selection === OPEN_NEW_PROJECT_OPTIONS.ADD_CURRENT_WORKSPACE) { + vscode.workspace.updateWorkspaceFolders(0, 0, { uri: uriPath }); + } else if (selection === OPEN_NEW_PROJECT_OPTIONS.OPEN_NEW_WINDOW) { + await vscode.commands.executeCommand("vscode.openFolder", uriPath, true); } } catch (e) { console.error(e); - if (e.name === "FetchError") { + if (e.name === ERRORS.FETCH_ERROR) { vscode.window.showErrorMessage( "Failed to connect to the MicroProfile Starter. Please check your network connection and try again." ); + } else if (e.name === ERRORS.EXTRACT_PROJECT_ERROR) { + vscode.window.showErrorMessage("Failed to extract the MicroProfile Starter project"); } else { vscode.window.showErrorMessage("Failed to generate a MicroProfile Starter project"); } diff --git a/src/constants.ts b/src/constants.ts index 5dc4554..28a8056 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,7 @@ -export const MP_STARTER_API_ROOT = "https://start.microprofile.io/api/3"; +export const MP_STARTER_API_ROOT = "https://start.microprofile.io/api/4"; export const MP_VERSION_LABELS: Record = { + MP33: "Version 3.3", MP32: "Version 3.2", MP30: "Version 3.0", MP22: "Version 2.2", @@ -17,7 +18,7 @@ export const MP_SERVER_LABELS: Record = { PAYARA_MICRO: "Payara Micro", THORNTAIL_V2: "Thorntail Version 2", KUMULUZEE: "KumuluzEE", - TOMEE: "Apache TomEE 8.00-M2", + TOMEE: "Apache TomEE 8.00-M3", WILDFLY: "WildFly", WILDFLY_SWARM: "WildFly Swarm", QUARKUS: "Quarkus", @@ -34,3 +35,8 @@ export const CONFIRM_OPTIONS = { YES: "Yes", NO: "No", }; + +export const ERRORS = { + FETCH_ERROR: "FetchError", + EXTRACT_PROJECT_ERROR: "ExtractProjectError", +}; diff --git a/src/util/mpStarterApi.ts b/src/util/mpStarterApi.ts new file mode 100644 index 0000000..27052c1 --- /dev/null +++ b/src/util/mpStarterApi.ts @@ -0,0 +1,89 @@ +import { MP_STARTER_API_ROOT, EXTENSION_USER_AGENT } from "../constants"; +import fetch from "node-fetch"; +import * as util from "../util/util"; + +interface MPVersionSupport { + supportedServers: string[]; + specs: string[]; +} + +interface SupportMatrix { + configs: Record; + descriptions: Record; +} + +export async function getSupportMatrix(): Promise { + const mpSupportResponse = await fetch(`${MP_STARTER_API_ROOT}/supportMatrix`, { + method: "GET", + headers: { + "User-Agent": EXTENSION_USER_AGENT, + }, + }); + if (mpSupportResponse.status >= 400 && mpSupportResponse.status < 600) { + throw new Error(`Bad response ${mpSupportResponse.status}: ${mpSupportResponse.statusText}`); + } + + return mpSupportResponse.json(); +} + +interface SupportDetails { + mpVersion: string; + mpSpecs: string[]; + javaSEVersions: string[]; +} + +export async function getSupportedJavaAndSpecs( + serverName: string, + microprofileVersion: string +): Promise { + const serverSupportResponse = await fetch(`${MP_STARTER_API_ROOT}/supportMatrix/servers`, { + method: "GET", + headers: { + "User-Agent": EXTENSION_USER_AGENT, + }, + }); + if (serverSupportResponse.status >= 400 && serverSupportResponse.status < 600) { + throw new Error( + `Bad response ${serverSupportResponse.status}: ${serverSupportResponse.statusText}` + ); + } + + const supportJSON = await serverSupportResponse.json(); + const serverInformation = supportJSON.configs[serverName]; + + const supportDetails: SupportDetails | undefined = serverInformation.find( + (supportRecord: SupportDetails) => supportRecord.mpVersion === microprofileVersion + ); + + if (supportDetails === undefined) { + throw new Error("Unable to find supported MicroProfile specifications and Java versions"); + } + + return supportDetails; +} + +interface StarterProjectOptions { + groupId: string; + artifactId: string; + mpVersion: string; + supportedServer: string; + javaSEVersion: string; + selectedSpecs: string[]; +} + +export async function downloadMPStarterProjectZip( + options: StarterProjectOptions, + downloadLocation: string +): Promise { + const requestOptions = { + url: `${MP_STARTER_API_ROOT}/project`, + method: "POST", + headers: { + "Content-Type": "application/json", + "User-Agent": EXTENSION_USER_AGENT, + }, + body: JSON.stringify(options), + }; + + await util.downloadFile(requestOptions, downloadLocation); +} diff --git a/src/util/vscodePrompts.ts b/src/util/vscodePrompts.ts index cc47521..2a5f720 100644 --- a/src/util/vscodePrompts.ts +++ b/src/util/vscodePrompts.ts @@ -41,17 +41,8 @@ export async function askForArtifactID(): Promise { } export async function askForJavaSEVersion( - mpVersion: string, - mpServer: string + supportedJavaSEVersions: string[] ): Promise { - const MP32_JAVA_11_SUPPORTED = ["LIBERTY", "PAYARA_MICRO", "HELIDON", "THORNTAIL_V2", "WILDFLY"]; - - let supportedJavaSEVersions = ["SE8"]; - - if (mpVersion === "MP32" && MP32_JAVA_11_SUPPORTED.includes(mpServer)) { - supportedJavaSEVersions = ["SE8", "SE11"]; - } - return await vscode.window.showQuickPick(supportedJavaSEVersions, { ignoreFocusOut: true, placeHolder: "Select a Java SE version.",