Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
8 changes: 4 additions & 4 deletions resources/new-contract/java/build.gradle.template.txt
Original file line number Diff line number Diff line change
@@ -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_$'
Expand All @@ -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'
}

Expand Down
31 changes: 31 additions & 0 deletions src/extension/templates/languages.ts
Original file line number Diff line number Diff line change
@@ -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<string>;
Expand Down Expand Up @@ -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()),
},
},
},
};
Expand Down
10 changes: 7 additions & 3 deletions src/extension/templates/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions src/extension/util/ioHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ export default class IoHelpers {
}
}

static async enterString(prompt: string): Promise<string | undefined> {
return await vscode.window.showInputBox({ prompt });
static async enterString(
prompt: string,
value?: string
): Promise<string | undefined> {
return await vscode.window.showInputBox({ prompt, value });
}

static async multipleChoice(placeHolder: string, ...items: string[]) {
Expand Down
62 changes: 62 additions & 0 deletions src/extension/util/tryFetchJson.ts
Original file line number Diff line number Diff line change
@@ -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();
});
}