Skip to content

Web browser API implementation #701

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
16 changes: 16 additions & 0 deletions .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ jobs:
name: java-doc
path: java/target/reports/apidocs/

web:
runs-on: ubuntu-22.04
name: Web documentation
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
- name: Generate documentation
run: make generate-docs-web
- name: Upload documentation
uses: actions/upload-artifact@v4
with:
name: web-doc
path: web/apidocs/

site:
runs-on: ubuntu-24.04
name: Documentation site
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,20 @@ jobs:
run: make pull-docker-images
- name: Run scenario tests
run: make scenario-test-java

web_unit:
needs: verify-versions
runs-on: ubuntu-22.04
name: Unit test Web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Run unit tests
run: make unit-test-web
- name: Upload bundle
uses: actions/upload-artifact@v4
with:
name: web-bundle
path: web/fabric-gateway-web.js
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ base_dir := $(patsubst %/,%,$(dir $(realpath $(lastword $(MAKEFILE_LIST)))))
go_dir := $(base_dir)/pkg
node_dir := $(base_dir)/node
java_dir := $(base_dir)/java
web_dir := $(base_dir)/web
scenario_dir := $(base_dir)/scenario

go_bin_dir := $(shell go env GOPATH)/bin
Expand Down Expand Up @@ -48,6 +49,12 @@ build-java:
cd '$(java_dir)' && \
mvn -DskipTests install

.PHONY: build-web
build-web:
cd "$(web_dir)" && \
npm install && \
npm run build

.PHONY: unit-test
unit-test: generate lint unit-test-go unit-test-node unit-test-java

Expand All @@ -71,6 +78,11 @@ unit-test-java:
cd '$(java_dir)' && \
mvn test jacoco:report

.PHONY: unit-test-web
unit-test-web: build-web
cd "$(web_dir)" && \
npm test

.PHONY: lint
lint: staticcheck golangci-lint

Expand Down Expand Up @@ -233,6 +245,12 @@ generate-docs-java:
cd '$(java_dir)' && \
mvn javadoc:javadoc

.PHONY: generate-docs-web
generate-docs-web:
cd "$(web_dir)" && \
npm install && \
npm run generate-apidoc

.PHONY: test
test: shellcheck unit-test scenario-test

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ The following Makefile targets are available:
- `make unit-test-go-pkcs11` - run unit tests for the Go client API, including HSM tests
- `make unit-test-node` - run unit tests for the Node client API
- `make unit-test-java` - run unit tests for the Java client API
- `make unit-test-web` - run unit tests for the Web client API
- `make unit-test` - run unit tests for all client language implementations
- `make scenario-test-go` - run the scenario (end to end integration) tests for Go client API, including HSM tests
- `make scenario-test-go-no-hsm` - run the scenario (end to end integration) tests for Go client API, excluding HSM tests
Expand Down
4 changes: 2 additions & 2 deletions node/src/signingidentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Signer } from './identity/signer';
export const undefinedSignerMessage = 'No signing implementation';

const undefinedSigner: Signer = () => {
throw new Error(undefinedSignerMessage);
return Promise.reject(new Error(undefinedSignerMessage));
};

type SigningIdentityOptions = Pick<ConnectOptions, 'identity' | 'signer' | 'hash'>;
Expand Down Expand Up @@ -55,7 +55,7 @@ export class SigningIdentity {
return this.#hash(message);
}

async sign(digest: Uint8Array): Promise<Uint8Array> {
sign(digest: Uint8Array): Promise<Uint8Array> {
return this.#sign(digest);
}
}
4 changes: 4 additions & 0 deletions scenario/fixtures/rest/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
coverage/
package-lock.json
33 changes: 33 additions & 0 deletions scenario/fixtures/rest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "fabric-gateway-rest",
"version": "0.0.1",
"description": "REST server for fabric-gateway-web clients",
"main": "dist/index.js",
"engines": {
"node": ">=18.12.0"
},
"scripts": {
"format": "prettier '**/*.{ts,js}' --check",
"format:fix": "prettier '**/*.{ts,js}' --write",
"lint": "eslint .",
"postinstall": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "Apache-2.0",
"dependencies": {
"@grpc/grpc-js": "^1.10.4",
"@hyperledger/fabric-gateway": "file:../../../node/fabric-gateway-dev.tgz",
"express": "^4.19.2"
},
"devDependencies": {
"@tsconfig/node18": "^18.2.2",
"@types/express": "^4.17.21",
"@types/node": "^18.19.22",
"@typescript-eslint/eslint-plugin": "~7.3.1",
"@typescript-eslint/parser": "~7.3.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.2.5",
"typescript": "~5.4.2"
}
}
37 changes: 37 additions & 0 deletions scenario/fixtures/rest/src/gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Gateway } from '@hyperledger/fabric-gateway';
import express, { Express } from 'express';
import { Server } from './server';

const REST_PORT = 3000;

export interface GatewayServerOptions {
port: number;
gateway: Gateway;
}

export class GatewayServer {
#gateway: Gateway;
#server: Server;

constructor(options: GatewayServerOptions) {
this.#gateway = options.gateway;
this.#server = new Server({
port: options.port,
handlers: [this.#evaluate],
});
}

start(): Promise<void> {
return this.#server.start();
}

stop(): Promise<void> {
return this.#server.stop();
}

#evaluate(app: Express): void {
app.post('/evaluate', express.json(), (request, response) => {
request.body.proposal;
});
}
}
Empty file.
28 changes: 28 additions & 0 deletions scenario/fixtures/rest/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import express, { Express } from 'express';
import * as http from 'node:http';

export interface ServerOptions {
port: number;
handlers: ((app: Express) => void)[];
}

export class Server {
readonly #app = express();
readonly #port: number;
#server?: http.Server;

constructor(options: ServerOptions) {
this.#port = options.port;
options.handlers.forEach((handler) => handler(this.#app));
}

start(): Promise<void> {
return new Promise((resolve) => {
this.#server = this.#app.listen(this.#port, resolve);
});
}

stop(): Promise<void> {
return new Promise((resolve, reject) => this.#server?.close((err) => (err ? resolve() : reject(err))));
}
}
15 changes: 15 additions & 0 deletions scenario/fixtures/rest/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node18/tsconfig.json",
"compilerOptions": {
"declaration": false,
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/"]
}
3 changes: 2 additions & 1 deletion scenario/node/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions web/.eslintrc.base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports = {
env: {
node: true,
es2023: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
sourceType: 'module',
ecmaFeatures: {
impliedStrict: true,
},
project: './tsconfig.json',
tsconfigRootDir: process.env.TSCONFIG_ROOT_DIR || __dirname,
},
plugins: ['@typescript-eslint'],
extends: ['eslint:recommended', 'plugin:@typescript-eslint/strict-type-checked', 'prettier'],
rules: {
complexity: ['error', 10],
'@typescript-eslint/explicit-function-return-type': [
'error',
{
allowExpressions: true,
},
],
},
};
12 changes: 12 additions & 0 deletions web/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
root: true,
env: {
jest: true,
},
ignorePatterns: ['*/**', '*.js', '*.ts', '!src/**/*.ts'],
plugins: ['jest', 'eslint-plugin-tsdoc'],
extends: ['.eslintrc.base', 'plugin:jest/recommended'],
rules: {
'tsdoc/syntax': ['error'],
},
};
10 changes: 10 additions & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules/
dist/
coverage/
*.tgz
src/protos/
apidocs/
package-lock.json
sbom.json
fabric-protos-*/
fabric-gateway-web.js
4 changes: 4 additions & 0 deletions web/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Exclude everything except specific inclusions below
**/*

!dist/**/*
1 change: 1 addition & 0 deletions web/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
3 changes: 3 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Hyperledger Fabric Gateway Client API for Web

The Fabric Gateway client API for Web bundle helps browser applications to interact with a Hyperledger Fabric blockchain network using an intermediary service. It implements a subset of the Fabric programming model, providing a simple API to generate signed transaction proposals with minimal code.
11 changes: 11 additions & 0 deletions web/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
roots: ['<rootDir>/src'],
preset: 'ts-jest',
testEnvironment: 'node',
collectCoverage: true,
collectCoverageFrom: ['**/*.[jt]s?(x)', '!**/*.d.ts'],
coverageProvider: 'v8',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
verbose: true,
workerThreads: true,
};
57 changes: 57 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@hyperledger/fabric-gateway-web",
"version": "1.5.0",
"description": "Hyperledger Fabric Gateway client API for Web browsers",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"engines": {
"node": ">=18.12.0"
},
"repository": {
"type": "git",
"url": "https://github.com/hyperledger/fabric-gateway"
},
"bugs": "https://github.com/hyperledger/fabric-gateway/issues",
"homepage": "https://hyperledger.github.io/fabric-gateway/",
"author": {
"name": "hyperledger/fabric",
"email": "[email protected]",
"url": "https://www.hyperledger.org/use/fabric"
},
"scripts": {
"build": "npm-run-all clean compile copy-non-ts-source bundle",
"bundle": "esbuild dist/index.js --bundle --minify --outfile=fabric-gateway-web.js --analyze",
"clean": "rm -rf apidocs dist",
"compile": "tsc --project tsconfig.build.json",
"copy-non-ts-source": "rsync -rv --prune-empty-dirs --include='*.d.ts' --exclude='*.ts' src/ dist",
"format": "prettier '**/*.{ts,js}' --check",
"format:fix": "prettier '**/*.{ts,js}' --write",
"generate-apidoc": "typedoc",
"lint": "eslint .",
"sbom": "cyclonedx-npm --omit dev --output-format JSON --output-file sbom.json",
"test": "npm-run-all lint format unit-test",
"unit-test": "NODE_OPTIONS='--experimental-global-webcrypto' jest"
},
"license": "Apache-2.0",
"dependencies": {
"@hyperledger/fabric-protos": "0.3.3",
"google-protobuf": "^3.21.0"
},
"devDependencies": {
"@types/google-protobuf": "^3.15.12",
"@types/jest": "^29.5.12",
"@typescript-eslint/eslint-plugin": "~7.5.0",
"@typescript-eslint/parser": "~7.5.0",
"esbuild": "^0.20.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-tsdoc": "^0.2.17",
"jest": "^29.7.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"typedoc": "^0.25.11",
"typescript": "~5.4.3"
}
}
Loading
Loading