Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use browser websocket in browser #1112

Merged
merged 2 commits into from
Oct 16, 2024
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
2 changes: 1 addition & 1 deletion examples/web/hello.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ <h3>Results</h3>
await glm.connect();
appendResults("Request for renting a provider machine");
const rental = await glm.oneOf({ order });
appendResults("Rented resources from", rental.agreement.provider.name);
appendResults("Rented resources from " + rental.agreement.provider.name);
await rental
.getExeUnit()
.then(async (exe) =>
Expand Down
150 changes: 150 additions & 0 deletions examples/web/transfer-data.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Requestor in browser</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous"
/>
</head>
<body>
<div class="container">
<h1 class="pb-4">Transfer some data to the provider, process it there and download the result</h1>
<div class="row pb-4">
<h3>Options</h3>
<div id="options" class="row">
<div class="col-4 form-group">
<label for="YAGNA_APPKEY">Yagna AppKey: </label>
<input id="YAGNA_APPKEY" class="form-control" type="text" value="" />
</div>
<div class="col-4 form-group">
<label for="YAGNA_API_BASEPATH">Yagna Api Url: </label>
<input id="YAGNA_API_BASEPATH" class="form-control" type="text" value="http://127.0.0.1:7465" />
</div>
</div>
<div class="row pb-4">
<div class="col-4 form-group">
<label for="IMAGE_TAG">Image Tag: </label>
<input id="IMAGE_TAG" type="text" class="form-control" value="golem/alpine:latest" />
</div>
<div class="col-4 form-group">
<label for="SUBNET_TAG">Subnet Tag: </label>
<input id="SUBNET_TAG" type="text" class="form-control" value="public" />
</div>
<div class="col-4 form-group">
<label for="PAYMENT_NETWORK">Payment Network: </label>
<input id="PAYMENT_NETWORK" type="text" class="form-control" value="holesky" />
</div>
</div>
</div>
<div class="row pb-4">
<div class="form-group">
<label for="DATA">Data to be processed: </label>
<textarea id="DATA" class="form-control" rows="2">Hello Golem!</textarea>
</div>
</div>
<div class="row pb-4">
<h3>Actions</h3>
<div>
<button id="transfer-data" class="btn btn-primary" onclick="run()">Process file on the provider</button>
</div>
</div>
<div class="row">
<div class="alert alert-info" role="alert">
<h4 class="alert-heading">Debugging</h4>
<p>You can see <code>@golem-sdk/golem-js</code> logs in your browser&apos;s <code>console</code> :)</p>
</div>
<h3>Results</h3>
<div class="col">
<ul id="results"></ul>
</div>
</div>
</div>

<script type="module">
import { GolemNetwork } from "https://unpkg.com/@golem-sdk/golem-js";

// This line allows you to watch golem-js internal logs in the browser console!
localStorage.debug = "golem-js:*";

export function appendResults(result) {
const resultsEl = document.getElementById("results");
const li = document.createElement("li");
li.appendChild(document.createTextNode(result));
resultsEl.appendChild(li);
}

async function run() {
const key = document.getElementById("YAGNA_APPKEY").value;

if (!key) {
alert("You didn't provide your Yagna AppKey");
return;
}

const url = document.getElementById("YAGNA_API_BASEPATH").value;
const subnetTag = document.getElementById("SUBNET_TAG").value;
const imageTag = document.getElementById("IMAGE_TAG").value;
const network = document.getElementById("PAYMENT_NETWORK").value;

// Define the order that we're going to place on the market
const order = {
demand: {
workload: {
imageTag,
},
subnetTag,
},
market: {
rentHours: 0.5,
pricing: {
model: "linear",
maxStartPrice: 0.5,
maxCpuPerHourPrice: 1.0,
maxEnvPerHourPrice: 0.5,
},
},
payment: { network },
};

const glm = new GolemNetwork({
api: { key, url },
});

glm.payment.events.on("invoiceAccepted", ({ invoice }) => appendResults(`Total cost: ${invoice.amount} GLM`));

try {
appendResults("Establishing a connection to the Golem Network");
await glm.connect();
appendResults("Request for renting a provider machine");
const rental = await glm.oneOf({ order });
appendResults("Rented resources from " + rental.agreement.provider.name);
await rental.getExeUnit().then(async (exe) => {
appendResults("Uploading some data to the provider");
const data = document.getElementById("DATA").value;
await exe.uploadData(new TextEncoder().encode(data), "/golem/work/input.txt");
appendResults("Processing the data on the provider");
await exe.run(
"cat /golem/work/input.txt | tr '[:upper:][:lower:]' '[:lower:][:upper:]' > /golem/work/output.txt",
);
appendResults("Downloading the result");
const result = await exe.downloadData("/golem/work/output.txt");
appendResults("Result: " + new TextDecoder().decode(result.data));
});
appendResults("Finished all work with the resources");
await rental.stopAndFinalize();
appendResults("Finalized renting process");
} catch (err) {
console.error("Failed to run the example", err);
} finally {
await glm.disconnect();
}
}

window.run = run;
</script>
</body>
</html>
17 changes: 13 additions & 4 deletions src/shared/storage/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { encode, toObject } from "flatbuffers/js/flexbuffers.js";
import * as jsSha3 from "js-sha3";
import { defaultLogger, isBrowser, Logger, YagnaApi } from "../utils";
import { GolemInternalError, GolemUserError } from "../error/golem-error";
import WebSocket from "ws";
import NodeWebSocket from "ws";

type WebSocketLike = NodeWebSocket | WebSocket;

// FIXME: cannot import fs/promises because the rollup polyfill doesn't work with it
import * as fs from "fs";
Expand Down Expand Up @@ -256,9 +258,16 @@ export class WebSocketStorageProvider implements StorageProvider {
};
}

private async createSocket(fileInfo: GftpFileInfo, components: string[]): Promise<WebSocket> {
private getWsConstructor() {
if (isBrowser) {
return WebSocket;
}
return NodeWebSocket;
}

private async createSocket(fileInfo: GftpFileInfo, components: string[]): Promise<WebSocketLike> {
const service = await this.createService(fileInfo, components);
const ws = new WebSocket(service.url, ["gsb+flexbuffers"]);
const ws = new (this.getWsConstructor())(service.url, ["gsb+flexbuffers"]);
ws.addEventListener("error", () => {
this.logger.error(`Socket Error (${fileInfo.id})`);
});
Expand Down Expand Up @@ -287,7 +296,7 @@ export class WebSocketStorageProvider implements StorageProvider {
await this.yagnaApi.gsb.unbindServices(id);
}

private respond(ws: WebSocket, id: string, payload: unknown) {
private respond(ws: WebSocketLike, id: string, payload: unknown) {
ws.send(
encode({
id,
Expand Down
13 changes: 13 additions & 0 deletions tests/cypress/ui/transfer-data.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
describe("Transfer data example", () => {
it("should run the example", () => {
cy.visit("/transfer-data");
cy.get("#YAGNA_APPKEY").clear().type(Cypress.env("YAGNA_APPKEY"));
cy.get("#YAGNA_API_BASEPATH").clear().type(Cypress.env("YAGNA_API_BASEPATH"));
cy.get("#SUBNET_TAG").clear().type(Cypress.env("YAGNA_SUBNET"));
cy.get("#PAYMENT_NETWORK").clear().type(Cypress.env("PAYMENT_NETWORK"));
cy.get("#DATA").clear().type("Hello Golem!");
cy.get("#transfer-data").click();
cy.get("#results").should("include.text", "hELLO gOLEM!", { timeout: 60000 });
cy.get("#results").should("include.text", "Finalized renting process", { timeout: 10000 });
});
});