fabric-samples/full-stack-asset-transfer-guide/applications/conga-cards/src/contract.ts
jkneubuh a299e18e26
Moves the Full Stack Asset Transfer Development Guide to fabric-samples (#852)
* Import Full Stack Asset Transfer Guide at commit fb554befdbbeff9e69159b54fce0b811603f29c7

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* Update the workshop with a new WORKSHOP_PATH under fabric-samples

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* Update the workshop with a new WORKSHOP_PATH under fabric-samples

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* missed a .git ignored directory on add

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* Updates to run the workshop on the Apple M1

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* Workaround for https://github.com/eslint/eslint/issues/15299 in the contract tslinter

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* Build an arch-specific CC images on M1

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* empty commit - force a build

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

* revert an accidental commit that was building the top-level asset-transfer as arm64

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>

Signed-off-by: Josh Kneubuhl <jkneubuh@us.ibm.com>
2022-11-10 10:40:27 -05:00

104 lines
3.2 KiB
TypeScript

/*
* Copyright contributors to the Hyperledgendary Full Stack Asset Transfer Guide project
*
* SPDX-License-Identifier: Apache-2.0
*/
import { CommitError, Contract, StatusCode } from '@hyperledger/fabric-gateway';
import { TextDecoder } from 'util';
const RETRIES = 2;
const utf8Decoder = new TextDecoder();
export interface Asset {
ID: string;
Color: string;
Size: number;
Owner: string;
AppraisedValue: number;
}
export type AssetCreate = Omit<Asset, 'Owner'> & Partial<Asset>;
export type AssetUpdate = Pick<Asset, 'ID'> & Partial<Omit<Asset, 'Owner'>>;
/**
* AssetTransfer presents the smart contract in a form appropriate to the business application. Internally it uses the
* Fabric Gateway client API to invoke transaction functions, and deals with the translation between the business
* application and API representation of parameters and return values.
*/
export class AssetTransfer {
readonly #contract: Contract;
constructor(contract: Contract) {
this.#contract = contract;
}
async createAsset(asset: AssetCreate): Promise<void> {
await this.#contract.submit('CreateAsset', {
arguments: [JSON.stringify(asset)],
});
}
async getAllAssets(): Promise<Asset[]> {
const result = await this.#contract.evaluate('GetAllAssets');
if (result.length === 0) {
return [];
}
return JSON.parse(utf8Decoder.decode(result)) as Asset[];
}
async readAsset(id: string): Promise<Asset> {
const result = await this.#contract.evaluate('ReadAsset', {
arguments: [id],
});
return JSON.parse(utf8Decoder.decode(result)) as Asset;
}
async updateAsset(asset: AssetUpdate): Promise<void> {
await submitWithRetry(() => this.#contract.submit('UpdateAsset', {
arguments: [JSON.stringify(asset)],
}));
}
async deleteAsset(id: string): Promise<void> {
await submitWithRetry(() => this.#contract.submit('DeleteAsset', {
arguments: [id],
}));
}
async assetExists(id: string): Promise<boolean> {
const result = await this.#contract.evaluate('AssetExists', {
arguments: [id],
});
return utf8Decoder.decode(result).toLowerCase() === 'true';
}
async transferAsset(id: string, newOwner: string, newOwnerOrg: string): Promise<void> {
await submitWithRetry(() => this.#contract.submit('TransferAsset', {
arguments: [id, newOwner, newOwnerOrg],
}));
}
}
async function submitWithRetry<T>(submit: () => Promise<T>): Promise<T> {
let lastError: unknown | undefined;
for (let retryCount = 0; retryCount < RETRIES; retryCount++) {
try {
return await submit();
} catch (err: unknown) {
lastError = err;
if (err instanceof CommitError) {
// Transaction failed validation and did not update the ledger. Handle specific transaction validation codes.
if (err.code === StatusCode.MVCC_READ_CONFLICT) {
continue; // Retry
}
}
break; // Failure -- don't retry
}
}
throw lastError;
}