Skip to content

Commit

Permalink
Web browser API implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Mark S. Lewis <[email protected]>
  • Loading branch information
bestbeforetoday committed Dec 11, 2024
1 parent e393353 commit afc9467
Show file tree
Hide file tree
Showing 42 changed files with 1,894 additions and 3 deletions.
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

0 comments on commit afc9467

Please sign in to comment.