Skip to content

Commit b54c511

Browse files
ci: add workflow to test docs examples (#225)
Signed-off-by: SimiHunjan <[email protected]> Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: SimiHunjan <[email protected]>
1 parent c53e74c commit b54c511

File tree

6 files changed

+605
-0
lines changed

6 files changed

+605
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import com.hedera.hashgraph.sdk.*;
2+
import java.net.http.*;
3+
import java.net.URI;
4+
import com.google.gson.*;
5+
6+
public class CreateAccountDemo {
7+
public static void main(String[] args) throws Exception {
8+
// .env-provided
9+
AccountId operatorId = AccountId.fromString(System.getenv("OPERATOR_ID"));
10+
PrivateKey operatorKey = PrivateKey.fromString(System.getenv("OPERATOR_KEY"));
11+
String network = System.getenv().getOrDefault("HEDERA_NETWORK", "local");
12+
String mirrorNodeUrl = System.getenv().getOrDefault(
13+
"MIRROR_NODE_URL",
14+
"http://localhost:5551/api/v1"
15+
);
16+
17+
if (operatorId == null || operatorKey == null) {
18+
throw new IllegalStateException("OPERATOR_ID / OPERATOR_KEY not set");
19+
}
20+
21+
Client client =
22+
"local".equalsIgnoreCase(network)
23+
? Client.forNetwork(java.util.Map.of("127.0.0.1:50211", new AccountId(3)))
24+
: Client.forTestnet();
25+
26+
client.setOperator(operatorId, operatorKey);
27+
28+
PrivateKey newPrivateKey = PrivateKey.generateECDSA();
29+
PublicKey newPublicKey = newPrivateKey.getPublicKey();
30+
31+
AccountCreateTransaction transaction = new AccountCreateTransaction()
32+
.setKeyWithAlias(newPublicKey)
33+
.setInitialBalance(new Hbar(20));
34+
35+
TransactionResponse txResponse = transaction.execute(client);
36+
TransactionReceipt receipt = txResponse.getReceipt(client);
37+
AccountId newAccountId = receipt.accountId;
38+
39+
System.out.println("\nHedera account created: " + newAccountId);
40+
System.out.println("EVM Address: 0x" + newPublicKey.toEvmAddress() + "\n");
41+
42+
System.out.println("\nWaiting for Mirror Node to update...\n");
43+
Thread.sleep(10000);
44+
45+
String url = mirrorNodeUrl + "/balances?account.id=" + newAccountId;
46+
47+
HttpClient httpClient = HttpClient.newHttpClient();
48+
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
49+
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
50+
51+
Gson gson = new Gson();
52+
JsonObject data = gson.fromJson(response.body(), JsonObject.class);
53+
54+
if (data.has("balances") && data.getAsJsonArray("balances").size() > 0) {
55+
JsonArray balances = data.getAsJsonArray("balances");
56+
JsonObject accountBalance = balances.get(0).getAsJsonObject();
57+
long tinybars = accountBalance.get("balance").getAsLong();
58+
double hbar = tinybars / 100_000_000.0;
59+
System.out.println("Account balance: " + hbar + " ℏ\n");
60+
} else {
61+
System.out.println("Account balance not yet available in Mirror Node");
62+
}
63+
64+
client.close();
65+
}
66+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import com.hedera.hashgraph.sdk.*;
2+
import java.net.http.HttpClient;
3+
import java.net.http.HttpRequest;
4+
import java.net.http.HttpResponse;
5+
import java.net.URI;
6+
import com.google.gson.Gson;
7+
import com.google.gson.JsonObject;
8+
import com.google.gson.JsonArray;
9+
10+
public class CreateTokenDemo {
11+
public static void main(String[] args ) throws Exception {
12+
// .env-provided
13+
AccountId operatorId = AccountId.fromString(System.getenv("OPERATOR_ID"));
14+
PrivateKey operatorKey = PrivateKey.fromString(System.getenv("OPERATOR_KEY"));
15+
String network = System.getenv().getOrDefault("HEDERA_NETWORK", "local"); // "local" for Solo
16+
String mirrorNode = System.getenv().getOrDefault(
17+
"MIRROR_NODE_URL",
18+
"http://localhost:5551/api/v1"
19+
);
20+
21+
if (operatorId == null || operatorKey == null) {
22+
throw new IllegalStateException("OPERATOR_ID / OPERATOR_KEY not set");
23+
}
24+
25+
Client client =
26+
"local".equalsIgnoreCase(network)
27+
? Client.forNetwork(java.util.Map.of("127.0.0.1:50211", new AccountId(3))) // Solo default node + node account (adjust if needed)
28+
: Client.forTestnet();
29+
30+
client.setOperator(operatorId, operatorKey);
31+
System.out.println(operatorId);
32+
33+
// generate token keys
34+
PrivateKey supplyKey = PrivateKey.generateECDSA();
35+
PrivateKey adminKey = supplyKey;
36+
37+
// build & execute the token creation transaction
38+
TokenCreateTransaction transaction = new TokenCreateTransaction()
39+
.setTokenName("Demo Token")
40+
.setTokenSymbol("DEMO")
41+
.setDecimals(2)
42+
.setInitialSupply(100_000)
43+
.setSupplyType(TokenSupplyType.FINITE)
44+
.setMaxSupply(100_000)
45+
.setTreasuryAccountId(operatorId)
46+
.setAdminKey(adminKey.getPublicKey())
47+
.setSupplyKey(supplyKey.getPublicKey())
48+
.setTokenMemo("Created via tutorial")
49+
.freezeWith(client);
50+
51+
TokenCreateTransaction signedTx = transaction.sign(adminKey);
52+
TransactionResponse txResponse = signedTx.execute(client);
53+
TransactionReceipt receipt = txResponse.getReceipt(client);
54+
TokenId tokenId = receipt.tokenId;
55+
56+
System.out.println("\nFungible token created: " + tokenId );
57+
58+
// Wait for Mirror Node to populate data
59+
System.out.println("\nWaiting for Mirror Node to update...");
60+
Thread.sleep(10000);
61+
62+
// query balance using Mirror Node
63+
String mirrorNodeUrl = String.format(
64+
"%s/accounts/%s/tokens?token.id=%s",
65+
mirrorNode, operatorId, tokenId
66+
);
67+
68+
HttpClient httpClient = HttpClient.newHttpClient( );
69+
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(mirrorNodeUrl)).build();
70+
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString( ));
71+
72+
try {
73+
if (response.statusCode() != 200) {
74+
System.out.println("Mirror Node returned status " + response.statusCode() + ". Skipping balance read.");
75+
} else {
76+
JsonObject data = new Gson().fromJson(response.body(), JsonObject.class);
77+
JsonArray tokens = (data != null) ? data.getAsJsonArray("tokens") : null;
78+
79+
if (tokens != null && tokens.size() > 0) {
80+
long balance = tokens.get(0).getAsJsonObject().get("balance").getAsLong();
81+
System.out.println("\nTreasury holds: " + balance + " DEMO\n");
82+
} else {
83+
System.out.println("Token balance not yet available in Mirror Node");
84+
}
85+
}
86+
} catch (Exception e) {
87+
System.out.println("Error reading token balance from Mirror Node: " + e.getMessage());
88+
// continue without failing
89+
}
90+
91+
client.close();
92+
}
93+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import com.hedera.hashgraph.sdk.*;
2+
import java.net.http.HttpClient;
3+
import java.net.http.HttpRequest;
4+
import java.net.http.HttpResponse;
5+
import java.net.URI;
6+
import com.google.gson.Gson;
7+
import com.google.gson.JsonObject;
8+
import com.google.gson.JsonArray;
9+
10+
public class CreateTopicDemo {
11+
public static void main(String[] args ) throws Exception {
12+
// .env-provided
13+
AccountId operatorId = AccountId.fromString(System.getenv("OPERATOR_ID"));
14+
PrivateKey operatorKey = PrivateKey.fromString(System.getenv("OPERATOR_KEY"));
15+
String network = System.getenv().getOrDefault("HEDERA_NETWORK", "local");
16+
String mirrorNode = System.getenv().getOrDefault(
17+
"MIRROR_NODE_URL",
18+
"http://localhost:5551/api/v1"
19+
);
20+
21+
if (operatorId == null || operatorKey == null) {
22+
throw new IllegalStateException("OPERATOR_ID / OPERATOR_KEY not set");
23+
}
24+
25+
Client client =
26+
"local".equalsIgnoreCase(network)
27+
? Client.forNetwork(java.util.Map.of("127.0.0.1:50211", new AccountId(3)))
28+
: Client.forTestnet();
29+
30+
client.setOperator(operatorId, operatorKey);
31+
// build & execute the topic creation transaction
32+
TopicCreateTransaction transaction = new TopicCreateTransaction()
33+
.setTopicMemo("My first HCS topic");
34+
35+
TransactionResponse txResponse = transaction.execute(client);
36+
TransactionReceipt receipt = txResponse.getReceipt(client);
37+
TopicId topicId = receipt.topicId;
38+
39+
System.out.println("\nTopic created: " + topicId);
40+
41+
// build & execute the message submission transaction
42+
String message = "Hello, Hedera!";
43+
44+
TopicMessageSubmitTransaction messageTransaction = new TopicMessageSubmitTransaction()
45+
.setTopicId(topicId)
46+
.setMessage(message);
47+
48+
messageTransaction.execute(client);
49+
50+
System.out.println("\nMessage submitted: " + message);
51+
52+
// wait for Mirror Node to populate data
53+
System.out.println("\nWaiting for Mirror Node to update...");
54+
Thread.sleep(10000);
55+
56+
// query messages using Mirror Node
57+
String mirrorNodeUrl = mirrorNode + "/topics/" + topicId + "/messages";
58+
59+
HttpClient httpClient = HttpClient.newHttpClient( );
60+
HttpRequest request = HttpRequest.newBuilder()
61+
.uri(URI.create(mirrorNodeUrl))
62+
.build();
63+
64+
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString( ));
65+
Gson gson = new Gson();
66+
JsonObject data = gson.fromJson(response.body(), JsonObject.class);
67+
68+
if (data.has("messages") && data.getAsJsonArray("messages").size() > 0) {
69+
JsonArray messages = data.getAsJsonArray("messages");
70+
JsonObject latestMessage = messages.get(messages.size() - 1).getAsJsonObject();
71+
String encodedMessage = latestMessage.get("message").getAsString();
72+
String messageContent = new String(java.util.Base64.getDecoder().decode(encodedMessage)).trim();
73+
74+
System.out.println("\nLatest message: " + messageContent + "\n");
75+
} else {
76+
System.out.println("No messages found yet in Mirror Node");
77+
}
78+
79+
client.close();
80+
}
81+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Create a minimal Gradle project that compiles & runs examples in .github/examples/java
5+
cat > build.gradle <<'EOF'
6+
plugins {
7+
id 'java'
8+
}
9+
10+
repositories {
11+
mavenCentral()
12+
}
13+
14+
dependencies {
15+
implementation 'com.hedera.hashgraph:sdk:2.60.0'
16+
implementation 'com.google.code.gson:gson:2.11.0'
17+
implementation 'io.grpc:grpc-netty-shaded:1.61.0'
18+
runtimeOnly 'org.slf4j:slf4j-simple:2.0.13'
19+
}
20+
21+
/**
22+
* Define an 'examples' source set for .github/examples/java.
23+
* IMPORTANT: Use resolvable configurations (examplesCompileClasspath/examplesRuntimeClasspath).
24+
*/
25+
sourceSets {
26+
examples {
27+
java.srcDirs = ['.github/examples/java']
28+
// Add main's compiled classes to examples' classpaths
29+
compileClasspath += sourceSets.main.output + configurations.examplesCompileClasspath
30+
runtimeClasspath += output + sourceSets.main.output + configurations.examplesRuntimeClasspath
31+
}
32+
}
33+
34+
configurations {
35+
// Let examples inherit dependencies declared on 'implementation'/'runtimeOnly'
36+
examplesImplementation.extendsFrom implementation
37+
examplesRuntimeOnly.extendsFrom runtimeOnly
38+
}
39+
40+
def examplesDir = file('.github/examples/java')
41+
42+
// Derive a fully-qualified class name from a .java file by reading its 'package ...;' line (if present)
43+
def fqnFor = { File f ->
44+
def pkgLine = f.readLines().find { it =~ /^\s*package\s+[\w\.]+\s*;/ }
45+
def pkg = pkgLine ? pkgLine.replaceAll(/^\s*package\s+/, '').replace(';','').trim() : null
46+
def cls = f.name.replaceFirst(/\.java$/, '')
47+
pkg ? "${pkg}.${cls}" : cls
48+
}
49+
50+
def exampleFiles = examplesDir.exists()
51+
? fileTree(examplesDir) { include '**/*.java' }.files
52+
: [] as Set<File>
53+
54+
/**
55+
* Run a single example via:
56+
* ./gradlew runExample -PexampleClass=<fully.qualified.ClassName>
57+
* If your example has no 'package' line, use just the class name (e.g., CreateAccountDemo).
58+
*/
59+
tasks.register('runExample', JavaExec) {
60+
group = 'examples'
61+
description = 'Run a single Java example (use -PexampleClass=<FQN>)'
62+
// Use the examples source set's runtime classpath
63+
classpath = sourceSets.examples.runtimeClasspath
64+
65+
doFirst {
66+
if (!project.hasProperty('exampleClass')) {
67+
throw new GradleException("Missing -PexampleClass=<fully.qualified.ClassName>")
68+
}
69+
// Set to a concrete String value at execution time
70+
mainClass.set(project.property('exampleClass').toString())
71+
}
72+
}
73+
74+
// Create a JavaExec task per discovered example: run_<FQN with dots replaced by underscores>
75+
exampleFiles.each { f ->
76+
def fqn = fqnFor(f)
77+
tasks.register("run_${fqn.replace('.', '_')}", JavaExec) {
78+
group = 'examples'
79+
description = "Run example ${fqn}"
80+
classpath = sourceSets.examples.runtimeClasspath
81+
mainClass.set(fqn)
82+
}
83+
}
84+
85+
// Aggregate task: run all discovered examples
86+
tasks.register('runAllExamples') {
87+
group = 'examples'
88+
description = 'Run all Java examples in .github/examples/java'
89+
dependsOn tasks.matching { it.name.startsWith('run_') }
90+
}
91+
EOF
92+
93+
echo "rootProject.name = 'docs-examples-runner'" > settings.gradle
94+
95+
# Gradle wrapper (works on GitHub runners)
96+
if [ ! -f gradlew ]; then
97+
curl -sL https://services.gradle.org/distributions/gradle-8.9-bin.zip -o gradle.zip
98+
mkdir -p .gradle/wrapper/dists
99+
unzip -q gradle.zip -d .gradle >/dev/null || true
100+
rm -f gradle.zip
101+
if command -v gradle >/dev/null 2>&1; then
102+
gradle wrapper -q
103+
else
104+
curl -s https://raw.githubusercontent.com/gradle/gradle/master/gradlew -o gradlew
105+
chmod +x gradlew
106+
fi
107+
fi
108+
109+
chmod +x gradlew
110+
echo "✅ Gradle bootstrap complete"

0 commit comments

Comments
 (0)