Skip to content

Commit afc9467

Browse files
Web browser API implementation
Signed-off-by: Mark S. Lewis <[email protected]>
1 parent e393353 commit afc9467

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1894
-3
lines changed

.github/workflows/build-docs.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ jobs:
4040
name: java-doc
4141
path: java/target/reports/apidocs/
4242

43+
web:
44+
runs-on: ubuntu-22.04
45+
name: Web documentation
46+
steps:
47+
- uses: actions/checkout@v4
48+
- uses: actions/setup-node@v4
49+
with:
50+
node-version: "lts/*"
51+
- name: Generate documentation
52+
run: make generate-docs-web
53+
- name: Upload documentation
54+
uses: actions/upload-artifact@v4
55+
with:
56+
name: web-doc
57+
path: web/apidocs/
58+
4359
site:
4460
runs-on: ubuntu-24.04
4561
name: Documentation site

.github/workflows/test.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,20 @@ jobs:
207207
run: make pull-docker-images
208208
- name: Run scenario tests
209209
run: make scenario-test-java
210+
211+
web_unit:
212+
needs: verify-versions
213+
runs-on: ubuntu-22.04
214+
name: Unit test Web
215+
steps:
216+
- uses: actions/checkout@v4
217+
- uses: actions/setup-node@v4
218+
with:
219+
node-version: 22
220+
- name: Run unit tests
221+
run: make unit-test-web
222+
- name: Upload bundle
223+
uses: actions/upload-artifact@v4
224+
with:
225+
name: web-bundle
226+
path: web/fabric-gateway-web.js

Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ base_dir := $(patsubst %/,%,$(dir $(realpath $(lastword $(MAKEFILE_LIST)))))
99
go_dir := $(base_dir)/pkg
1010
node_dir := $(base_dir)/node
1111
java_dir := $(base_dir)/java
12+
web_dir := $(base_dir)/web
1213
scenario_dir := $(base_dir)/scenario
1314

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

52+
.PHONY: build-web
53+
build-web:
54+
cd "$(web_dir)" && \
55+
npm install && \
56+
npm run build
57+
5158
.PHONY: unit-test
5259
unit-test: generate lint unit-test-go unit-test-node unit-test-java
5360

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

81+
.PHONY: unit-test-web
82+
unit-test-web: build-web
83+
cd "$(web_dir)" && \
84+
npm test
85+
7486
.PHONY: lint
7587
lint: staticcheck golangci-lint
7688

@@ -233,6 +245,12 @@ generate-docs-java:
233245
cd '$(java_dir)' && \
234246
mvn javadoc:javadoc
235247

248+
.PHONY: generate-docs-web
249+
generate-docs-web:
250+
cd "$(web_dir)" && \
251+
npm install && \
252+
npm run generate-apidoc
253+
236254
.PHONY: test
237255
test: shellcheck unit-test scenario-test
238256

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ The following Makefile targets are available:
6060
- `make unit-test-go-pkcs11` - run unit tests for the Go client API, including HSM tests
6161
- `make unit-test-node` - run unit tests for the Node client API
6262
- `make unit-test-java` - run unit tests for the Java client API
63+
- `make unit-test-web` - run unit tests for the Web client API
6364
- `make unit-test` - run unit tests for all client language implementations
6465
- `make scenario-test-go` - run the scenario (end to end integration) tests for Go client API, including HSM tests
6566
- `make scenario-test-go-no-hsm` - run the scenario (end to end integration) tests for Go client API, excluding HSM tests

node/src/signingidentity.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { Signer } from './identity/signer';
1414
export const undefinedSignerMessage = 'No signing implementation';
1515

1616
const undefinedSigner: Signer = () => {
17-
throw new Error(undefinedSignerMessage);
17+
return Promise.reject(new Error(undefinedSignerMessage));
1818
};
1919

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

58-
async sign(digest: Uint8Array): Promise<Uint8Array> {
58+
sign(digest: Uint8Array): Promise<Uint8Array> {
5959
return this.#sign(digest);
6060
}
6161
}

scenario/fixtures/rest/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
coverage/
4+
package-lock.json

scenario/fixtures/rest/package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "fabric-gateway-rest",
3+
"version": "0.0.1",
4+
"description": "REST server for fabric-gateway-web clients",
5+
"main": "dist/index.js",
6+
"engines": {
7+
"node": ">=18.12.0"
8+
},
9+
"scripts": {
10+
"format": "prettier '**/*.{ts,js}' --check",
11+
"format:fix": "prettier '**/*.{ts,js}' --write",
12+
"lint": "eslint .",
13+
"postinstall": "tsc",
14+
"test": "echo \"Error: no test specified\" && exit 1"
15+
},
16+
"license": "Apache-2.0",
17+
"dependencies": {
18+
"@grpc/grpc-js": "^1.10.4",
19+
"@hyperledger/fabric-gateway": "file:../../../node/fabric-gateway-dev.tgz",
20+
"express": "^4.19.2"
21+
},
22+
"devDependencies": {
23+
"@tsconfig/node18": "^18.2.2",
24+
"@types/express": "^4.17.21",
25+
"@types/node": "^18.19.22",
26+
"@typescript-eslint/eslint-plugin": "~7.3.1",
27+
"@typescript-eslint/parser": "~7.3.1",
28+
"eslint": "^8.57.0",
29+
"eslint-config-prettier": "^9.1.0",
30+
"prettier": "^3.2.5",
31+
"typescript": "~5.4.2"
32+
}
33+
}

scenario/fixtures/rest/src/gateway.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Gateway } from '@hyperledger/fabric-gateway';
2+
import express, { Express } from 'express';
3+
import { Server } from './server';
4+
5+
const REST_PORT = 3000;
6+
7+
export interface GatewayServerOptions {
8+
port: number;
9+
gateway: Gateway;
10+
}
11+
12+
export class GatewayServer {
13+
#gateway: Gateway;
14+
#server: Server;
15+
16+
constructor(options: GatewayServerOptions) {
17+
this.#gateway = options.gateway;
18+
this.#server = new Server({
19+
port: options.port,
20+
handlers: [this.#evaluate],
21+
});
22+
}
23+
24+
start(): Promise<void> {
25+
return this.#server.start();
26+
}
27+
28+
stop(): Promise<void> {
29+
return this.#server.stop();
30+
}
31+
32+
#evaluate(app: Express): void {
33+
app.post('/evaluate', express.json(), (request, response) => {
34+
request.body.proposal;
35+
});
36+
}
37+
}

scenario/fixtures/rest/src/index.ts

Whitespace-only changes.

scenario/fixtures/rest/src/server.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import express, { Express } from 'express';
2+
import * as http from 'node:http';
3+
4+
export interface ServerOptions {
5+
port: number;
6+
handlers: ((app: Express) => void)[];
7+
}
8+
9+
export class Server {
10+
readonly #app = express();
11+
readonly #port: number;
12+
#server?: http.Server;
13+
14+
constructor(options: ServerOptions) {
15+
this.#port = options.port;
16+
options.handlers.forEach((handler) => handler(this.#app));
17+
}
18+
19+
start(): Promise<void> {
20+
return new Promise((resolve) => {
21+
this.#server = this.#app.listen(this.#port, resolve);
22+
});
23+
}
24+
25+
stop(): Promise<void> {
26+
return new Promise((resolve, reject) => this.#server?.close((err) => (err ? resolve() : reject(err))));
27+
}
28+
}

scenario/fixtures/rest/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
"extends": "@tsconfig/node18/tsconfig.json",
4+
"compilerOptions": {
5+
"declaration": false,
6+
"sourceMap": true,
7+
"outDir": "dist",
8+
"rootDir": "src",
9+
"strict": true,
10+
"noUnusedLocals": true,
11+
"noImplicitReturns": true,
12+
"forceConsistentCasingInFileNames": true
13+
},
14+
"include": ["src/"]
15+
}

scenario/node/package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/.eslintrc.base.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module.exports = {
2+
env: {
3+
node: true,
4+
es2023: true,
5+
},
6+
parser: '@typescript-eslint/parser',
7+
parserOptions: {
8+
sourceType: 'module',
9+
ecmaFeatures: {
10+
impliedStrict: true,
11+
},
12+
project: './tsconfig.json',
13+
tsconfigRootDir: process.env.TSCONFIG_ROOT_DIR || __dirname,
14+
},
15+
plugins: ['@typescript-eslint'],
16+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/strict-type-checked', 'prettier'],
17+
rules: {
18+
complexity: ['error', 10],
19+
'@typescript-eslint/explicit-function-return-type': [
20+
'error',
21+
{
22+
allowExpressions: true,
23+
},
24+
],
25+
},
26+
};

web/.eslintrc.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
root: true,
3+
env: {
4+
jest: true,
5+
},
6+
ignorePatterns: ['*/**', '*.js', '*.ts', '!src/**/*.ts'],
7+
plugins: ['jest', 'eslint-plugin-tsdoc'],
8+
extends: ['.eslintrc.base', 'plugin:jest/recommended'],
9+
rules: {
10+
'tsdoc/syntax': ['error'],
11+
},
12+
};

web/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules/
2+
dist/
3+
coverage/
4+
*.tgz
5+
src/protos/
6+
apidocs/
7+
package-lock.json
8+
sbom.json
9+
fabric-protos-*/
10+
fabric-gateway-web.js

web/.npmignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Exclude everything except specific inclusions below
2+
**/*
3+
4+
!dist/**/*

web/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine-strict=true

web/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Hyperledger Fabric Gateway Client API for Web
2+
3+
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.

web/jest.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
roots: ['<rootDir>/src'],
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
collectCoverage: true,
6+
collectCoverageFrom: ['**/*.[jt]s?(x)', '!**/*.d.ts'],
7+
coverageProvider: 'v8',
8+
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
9+
verbose: true,
10+
workerThreads: true,
11+
};

web/package.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"name": "@hyperledger/fabric-gateway-web",
3+
"version": "1.5.0",
4+
"description": "Hyperledger Fabric Gateway client API for Web browsers",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"engines": {
8+
"node": ">=18.12.0"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/hyperledger/fabric-gateway"
13+
},
14+
"bugs": "https://github.com/hyperledger/fabric-gateway/issues",
15+
"homepage": "https://hyperledger.github.io/fabric-gateway/",
16+
"author": {
17+
"name": "hyperledger/fabric",
18+
"email": "[email protected]",
19+
"url": "https://www.hyperledger.org/use/fabric"
20+
},
21+
"scripts": {
22+
"build": "npm-run-all clean compile copy-non-ts-source bundle",
23+
"bundle": "esbuild dist/index.js --bundle --minify --outfile=fabric-gateway-web.js --analyze",
24+
"clean": "rm -rf apidocs dist",
25+
"compile": "tsc --project tsconfig.build.json",
26+
"copy-non-ts-source": "rsync -rv --prune-empty-dirs --include='*.d.ts' --exclude='*.ts' src/ dist",
27+
"format": "prettier '**/*.{ts,js}' --check",
28+
"format:fix": "prettier '**/*.{ts,js}' --write",
29+
"generate-apidoc": "typedoc",
30+
"lint": "eslint .",
31+
"sbom": "cyclonedx-npm --omit dev --output-format JSON --output-file sbom.json",
32+
"test": "npm-run-all lint format unit-test",
33+
"unit-test": "NODE_OPTIONS='--experimental-global-webcrypto' jest"
34+
},
35+
"license": "Apache-2.0",
36+
"dependencies": {
37+
"@hyperledger/fabric-protos": "0.3.3",
38+
"google-protobuf": "^3.21.0"
39+
},
40+
"devDependencies": {
41+
"@types/google-protobuf": "^3.15.12",
42+
"@types/jest": "^29.5.12",
43+
"@typescript-eslint/eslint-plugin": "~7.5.0",
44+
"@typescript-eslint/parser": "~7.5.0",
45+
"esbuild": "^0.20.2",
46+
"eslint": "^8.57.0",
47+
"eslint-config-prettier": "^9.1.0",
48+
"eslint-plugin-jest": "^27.9.0",
49+
"eslint-plugin-tsdoc": "^0.2.17",
50+
"jest": "^29.7.0",
51+
"npm-run-all": "^4.1.5",
52+
"prettier": "^3.2.5",
53+
"ts-jest": "^29.1.2",
54+
"typedoc": "^0.25.11",
55+
"typescript": "~5.4.3"
56+
}
57+
}

0 commit comments

Comments
 (0)