mirror of
https://github.com/EvernodeXRPL/hp-devkit.git
synced 2026-04-29 15:37:58 +00:00
Added nodejs code generator. (#1)
This commit is contained in:
@@ -22,4 +22,7 @@ RUN chmod -R +x /build/scripts/*.sh
|
||||
FROM ubuntu:focal as runner
|
||||
COPY --from=builder /build/docker-extracted/usr/bin/docker /usr/bin/
|
||||
COPY --from=builder /build/scripts/cluster.sh /usr/bin/cluster
|
||||
COPY --from=builder /build/jq /usr/bin/jq
|
||||
COPY --from=builder /build/scripts/codegen.sh /usr/bin/codegen
|
||||
COPY --from=builder /build/jq /usr/bin/jq
|
||||
|
||||
COPY code-templates /code-templates
|
||||
6
docker/code-templates/nodejs/starter/dist/hp.cfg.override
vendored
Normal file
6
docker/code-templates/nodejs/starter/dist/hp.cfg.override
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"contract": {
|
||||
"bin_path": "/usr/bin/node",
|
||||
"bin_args": "index.js"
|
||||
}
|
||||
}
|
||||
13
docker/code-templates/nodejs/starter/package.json
Normal file
13
docker/code-templates/nodejs/starter/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "_projname_",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "npx ncc build src/contract.js -o dist",
|
||||
"build:prod": "npx ncc build src/contract.js --minify -o dist",
|
||||
"start": "npm run build && hpdevkit deploy dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"hotpocket-nodejs-contract": "0.5.3",
|
||||
"@vercel/ncc": "0.34.0"
|
||||
}
|
||||
}
|
||||
60
docker/code-templates/nodejs/starter/src/_projname_.js
Normal file
60
docker/code-templates/nodejs/starter/src/_projname_.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const fs = require('fs').promises;
|
||||
|
||||
// This sample application writes and reads from a simple text file to serve user requests.
|
||||
// Real-world applications may use a proper local database like sqlite.
|
||||
const dataFile = 'datafile.txt'
|
||||
|
||||
export class _projname_ {
|
||||
sendOutput; // This function must be wired up by the caller.
|
||||
|
||||
async handleRequest(userPubKey, message, isReadOnly) {
|
||||
|
||||
// This sample application defines two simple messages. 'get' and 'set'.
|
||||
// It's up to the application to decide the structure and contents of messages.
|
||||
|
||||
if (message.type == 'get') {
|
||||
|
||||
// Retrieved previously saved data and return to the user.
|
||||
const data = await this.getData();
|
||||
await this.sendOutput(userPubKey, {
|
||||
type: 'data_result',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
else if (message.type == 'set') {
|
||||
|
||||
if (!isReadOnly) {
|
||||
// Save the provided data into storage.
|
||||
await this.setData(message.data);
|
||||
}
|
||||
else {
|
||||
await this.sendOutput(userPubKey, {
|
||||
type: 'error',
|
||||
error: 'Set data not supported in readonly mode'
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
await this.sendOutput(userPubKey, {
|
||||
type: 'error',
|
||||
error: 'Unknown message type'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async setData(data) {
|
||||
// Hot Pocket subjects data on-disk to consensus.
|
||||
await fs.writeFile(dataFile, data);
|
||||
}
|
||||
|
||||
async getData() {
|
||||
try {
|
||||
return (await fs.readFile(dataFile)).toString();
|
||||
}
|
||||
catch {
|
||||
console.log('Data file not created yet. Returning empty data.');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
49
docker/code-templates/nodejs/starter/src/contract.js
Normal file
49
docker/code-templates/nodejs/starter/src/contract.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const HotPocket = require('hotpocket-nodejs-contract');
|
||||
const { _projname_ } = require('./_projname_');
|
||||
|
||||
// Hot Pocket smart contract is defined as a function which takes the Hot Pocket ExecutionContext as an argument.
|
||||
// This function gets invoked every consensus round and whenever a user sends a out-of-concensus read-request.
|
||||
async function contract(ctx) {
|
||||
|
||||
// Create our application logic component.
|
||||
// This pattern allows us to test the application logic independently of Hot Pocket.
|
||||
const app = new _projname_();
|
||||
|
||||
// Wire-up output emissions from the application before we pass user inputs to it.
|
||||
app.sendOutput = async (userPubKey, output) => {
|
||||
// In Hot Pocket, each user is represented by their Ed25519 public key.
|
||||
const user = ctx.users.find(userPubKey);
|
||||
await user.send(output)
|
||||
}
|
||||
|
||||
// In 'readonly' mode, nothing our contract does will get persisted on the ledger. The benefit is
|
||||
// readonly messages gets processed much faster due to not being subjected to consensus.
|
||||
// We should only use readonly mode for returning/replying data for the requesting user.
|
||||
//
|
||||
// In consensus mode (NOT read-only), we can do anything like persisting to data storage and/or
|
||||
// sending data to any connected user at the time. Everything will get subjected to consensus so
|
||||
// there is a time-penalty.
|
||||
const isReadOnly = ctx.readonly;
|
||||
|
||||
// Process user inputs.
|
||||
// Loop through list of users who have sent us inputs.
|
||||
for (const user of ctx.users.list()) {
|
||||
|
||||
// Loop through inputs sent by each user.
|
||||
for (const input of user.inputs) {
|
||||
|
||||
// Read the data buffer sent by user (this can be any kind of data like string, json or binary data).
|
||||
const buf = await ctx.users.read(input);
|
||||
|
||||
// Let's assume all data buffers for this contract are JSON.
|
||||
// In real-world apps, we need to gracefully fitler out invalid data formats for our contract.
|
||||
const message = JSON.parse(buf);
|
||||
|
||||
// Pass the JSON message to our application logic component.
|
||||
await app.handleRequest(userPubKey, message, isReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hpc = new HotPocket.Contract();
|
||||
hpc.init(contract);
|
||||
40
docker/scripts/codegen.sh
Normal file
40
docker/scripts/codegen.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
platform=$1
|
||||
apptype=$2
|
||||
projname=$3
|
||||
|
||||
templates_dir="/code-templates"
|
||||
placeholder="_projname_"
|
||||
output_dir=$CODEGEN_OUTPUT
|
||||
usage="Usage: <platform> <app type> <project name>"
|
||||
|
||||
if [ -z $templates_dir ] || [ -z $output_dir ] ; then
|
||||
echo "Mandatory values missing." && exit 1
|
||||
fi
|
||||
|
||||
[ -z $platform ] && echo "Platform is required. $usage" && exit 1
|
||||
[ -z $apptype ] && echo "App type is required. $usage" && exit 1
|
||||
[ -z $projname ] && echo "Project name is required. $usage" && exit 1
|
||||
|
||||
[ ! -d $templates_dir/$platform ] && echo "Invalid platform '$platform' specified." && exit 1
|
||||
[ ! -d $templates_dir/$platform/$apptype ] && echo "Invalid application type '$apptype' specified." && exit 1
|
||||
if ! [[ "$projname" =~ ^[a-z][a-z0-9_-]*$ ]]; then
|
||||
echo "Invalid project name. Must be lowercase. Must start with a letter. Can only contain letters, numbers, dash and underscore."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $output_dir && rm -rf $output_dir/*
|
||||
cp -r $templates_dir/$platform/$apptype/* $output_dir
|
||||
|
||||
pushd $output_dir > /dev/null
|
||||
|
||||
# Replace placeholder in all file contents.
|
||||
find -type f -exec sed -i "s/${placeholder}/${projname}/g" {} \;
|
||||
|
||||
# Rename files with placeholder name.
|
||||
for f in $(find -type f -name "*${placeholder}*")
|
||||
do
|
||||
mv "$f" "$(echo "$f" | sed s/${placeholder}/${projname}/g)"
|
||||
done
|
||||
|
||||
popd > /dev/null
|
||||
Reference in New Issue
Block a user