diff --git a/CHANGELOG.md b/CHANGELOG.md index ca80aec..56133ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ### Changed +- Creating a Java smart contract automatically targets the latest version of neow3j (per Maven Central) - Make use of the ms-dotnettools.vscode-dotnet-sdk extension to acquire a path to dotnet (instead of requiring a global installation accessible in the PATH) diff --git a/resources/new-contract/java/build.gradle.template.txt b/resources/new-contract/java/build.gradle.template.txt index 74fe969..5caecc7 100644 --- a/resources/new-contract/java/build.gradle.template.txt +++ b/resources/new-contract/java/build.gradle.template.txt @@ -1,6 +1,6 @@ plugins { id 'java' - id 'io.neow3j.gradle-plugin' version "3.8.0" + id 'io.neow3j.gradle-plugin' version "$_NEOW3JLIBVERSION_$" } group '$_REVERSEDOMAINNAME_$' @@ -15,9 +15,9 @@ repositories { } dependencies { - implementation 'io.neow3j:contract:3.8.0' - implementation 'io.neow3j:devpack:3.8.0' - implementation 'io.neow3j:compiler:3.8.0' + implementation 'io.neow3j:contract:$_NEOW3JLIBVERSION_$' + implementation 'io.neow3j:devpack:$_NEOW3JLIBVERSION_$' + implementation 'io.neow3j:compiler:$_NEOW3JLIBVERSION_$' implementation 'junit:junit:4.12' } diff --git a/src/extension/templates/languages.ts b/src/extension/templates/languages.ts index 6afa855..f0803df 100644 --- a/src/extension/templates/languages.ts +++ b/src/extension/templates/languages.ts @@ -1,3 +1,14 @@ +import tryFetchJson from "../util/tryFetchJson"; + +// Variable resolution procedure: +// i) The `eval` function is called (if-present) and its result is used as the +// variable value. +// ii) If a `prompt` is provided the user is allowed to optionally specify a +// value (which will overwrite the result of `eval`, if any) +// iii) If a `parse` function is present it will be called and can modify the +// user-provided value. +// After the above steps, the variable value must be non-empty or template hydration +// will not proceed. type VariableDeclaration = { prompt?: string; eval?: (existingVariableValues: { [key: string]: string }) => Promise; @@ -94,6 +105,26 @@ const languages: { [code: string]: Language } = { eval: async ($) => `src/main/java/${$["$_REVERSEDOMAINNAMEPATH_$"]}/${$["$_CLASSNAME_$"]}.java`, }, + NEOW3JLIBVERSION: { + eval: async () => { + // Attempt to get the latest neow3j from Maven Central (falling back to + // a hard-coded version if Maven Central is not available or returns a + // malformed response): + const FALLBACK = "3.8.0"; + const searchResults = await tryFetchJson( + "https", + "search.maven.org", + `/solrsearch/select?q=g:"io.neow3j"+AND+a:"core"` + ); + return ( + (searchResults.response?.docs || [])[0]?.latestVersion + ?.replace(/[^.a-z0-9]/gi, "") + ?.trim() || FALLBACK + ); + }, + prompt: "Which version of neow3j would you like to target?", + parse: (_) => Promise.resolve(_?.replace(/[^.a-z0-9]/gi, "").trim()), + }, }, }, }; diff --git a/src/extension/templates/templates.ts b/src/extension/templates/templates.ts index 0ef0cb3..67f428e 100644 --- a/src/extension/templates/templates.ts +++ b/src/extension/templates/templates.ts @@ -128,11 +128,15 @@ export default class Templates { for (const variableName of Object.keys(language.variables)) { const variable = language.variables[variableName]; let value: string | undefined = ""; - if (variable.prompt) { - value = await IoHelpers.enterString(variable.prompt); - } else if (variable.eval) { + if (variable.eval) { value = await variable.eval(result); } + if (variable.prompt) { + value = await IoHelpers.enterString(variable.prompt, value); + } + if (variable.parse) { + value = await variable.parse(value); + } if (!value) { // All variables are considered required return undefined; diff --git a/src/extension/util/ioHelpers.ts b/src/extension/util/ioHelpers.ts index e386fad..105b2b1 100644 --- a/src/extension/util/ioHelpers.ts +++ b/src/extension/util/ioHelpers.ts @@ -63,8 +63,11 @@ export default class IoHelpers { } } - static async enterString(prompt: string): Promise { - return await vscode.window.showInputBox({ prompt }); + static async enterString( + prompt: string, + value?: string + ): Promise { + return await vscode.window.showInputBox({ prompt, value }); } static async multipleChoice(placeHolder: string, ...items: string[]) { diff --git a/src/extension/util/tryFetchJson.ts b/src/extension/util/tryFetchJson.ts new file mode 100644 index 0000000..6e072ab --- /dev/null +++ b/src/extension/util/tryFetchJson.ts @@ -0,0 +1,62 @@ +import * as wget from "wget-improved"; + +import JSONC from "./JSONC"; +import Log from "../../shared/log"; + +const LOG_PREFIX = "tryFetchJson"; + +// Attempts to retrieve a URL using a HTTP GET request. Expects the server to respond with +// a HTTP 200 status code and provide valid JSONC in the response body. Upon success, the +// parsed JSONC object is returned. Upon failure, a warning is logged and an empty object +// is returned. +export default function tryFetchJson( + protocol: "https" | "http", + host: string, + path: string +): any { + return new Promise((resolve) => { + const request = wget.request( + { host, method: "GET", path: encodeURI(path), protocol }, + (response) => { + if (response.statusCode !== 200) { + Log.warn( + LOG_PREFIX, + `Got HTTP code ${response.statusCode} when attempting to download ${protocol}://${host}${path}` + ); + resolve({}); + } else { + let content = ""; + response.on("error", (err) => { + Log.warn( + LOG_PREFIX, + `Error ("${err}") when processing response from ${protocol}://${host}${path}` + ); + resolve({}); + }); + response.on("data", (data) => { + content = content + data; + }); + response.on("end", () => { + try { + resolve(JSONC.parse(content)); + } catch (e) { + Log.warn( + LOG_PREFIX, + `Exception ("${e}") when parsing JSON from ${protocol}://${host}${path}` + ); + resolve({}); + } + }); + } + } + ); + request.on("error", (err) => { + Log.warn( + LOG_PREFIX, + `Error ("${err}") when sending request to ${protocol}://${host}${path}` + ); + resolve({}); + }); + request.end(); + }); +}