-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add initial askar package (#1211)
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
- Loading branch information
Showing
35 changed files
with
2,383 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<p align="center"> | ||
<br /> | ||
<img | ||
alt="Hyperledger Aries logo" | ||
src="https://raw.githubusercontent.com/hyperledger/aries-framework-javascript/aa31131825e3331dc93694bc58414d955dcb1129/images/aries-logo.png" | ||
height="250px" | ||
/> | ||
</p> | ||
<h1 align="center"><b>Aries Framework JavaScript Askar Module</b></h1> | ||
<p align="center"> | ||
<a | ||
href="https://raw.githubusercontent.com/hyperledger/aries-framework-javascript/main/LICENSE" | ||
><img | ||
alt="License" | ||
src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" | ||
/></a> | ||
<a href="https://www.typescriptlang.org/" | ||
><img | ||
alt="typescript" | ||
src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" | ||
/></a> | ||
<a href="https://www.npmjs.com/package/@aries-framework/askar" | ||
><img | ||
alt="@aries-framework/askar version" | ||
src="https://img.shields.io/npm/v/@aries-framework/askar" | ||
/></a> | ||
|
||
</p> | ||
<br /> | ||
|
||
Askar module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { Config } from '@jest/types' | ||
|
||
import base from '../../jest.config.base' | ||
|
||
import packageJson from './package.json' | ||
|
||
const config: Config.InitialOptions = { | ||
...base, | ||
name: packageJson.name, | ||
displayName: packageJson.name, | ||
setupFilesAfterEnv: ['./tests/setup.ts'], | ||
} | ||
|
||
export default config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "@aries-framework/askar", | ||
"main": "build/index", | ||
"types": "build/index", | ||
"version": "0.3.3", | ||
"private": true, | ||
"files": [ | ||
"build" | ||
], | ||
"license": "Apache-2.0", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/askar", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/hyperledger/aries-framework-javascript", | ||
"directory": "packages/askar" | ||
}, | ||
"scripts": { | ||
"build": "yarn run clean && yarn run compile", | ||
"clean": "rimraf ./build", | ||
"compile": "tsc -p tsconfig.build.json", | ||
"prepublishOnly": "yarn run build", | ||
"test": "jest" | ||
}, | ||
"dependencies": { | ||
"@aries-framework/core": "0.3.3", | ||
"@hyperledger/aries-askar-shared": "^0.1.0-dev.1", | ||
"class-transformer": "^0.5.1", | ||
"class-validator": "^0.14.0", | ||
"rxjs": "^7.2.0", | ||
"tsyringe": "^4.7.0" | ||
}, | ||
"devDependencies": { | ||
"@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", | ||
"reflect-metadata": "^0.1.13", | ||
"rimraf": "^4.0.7", | ||
"typescript": "~4.9.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { DependencyManager, Module } from '@aries-framework/core' | ||
|
||
import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' | ||
|
||
import { AskarStorageService } from './storage' | ||
import { AskarWallet } from './wallet' | ||
|
||
export class AskarModule implements Module { | ||
public register(dependencyManager: DependencyManager) { | ||
try { | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
require('@hyperledger/aries-askar-nodejs') | ||
} catch (error) { | ||
try { | ||
require('@hyperledger/aries-askar-react-native') | ||
} catch (error) { | ||
throw new Error('Could not load aries-askar bindings') | ||
} | ||
} | ||
|
||
if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { | ||
throw new AriesFrameworkError('There is an instance of Wallet already registered') | ||
} else { | ||
dependencyManager.registerContextScoped(InjectionSymbols.Wallet, AskarWallet) | ||
} | ||
|
||
if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { | ||
throw new AriesFrameworkError('There is an instance of StorageService already registered') | ||
} else { | ||
dependencyManager.registerSingleton(InjectionSymbols.StorageService, AskarStorageService) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type { AriesAskar } from './types' | ||
|
||
/** | ||
* AskarModuleConfigOptions defines the interface for the options of the AskarModuleConfig class. | ||
*/ | ||
export interface AskarModuleConfigOptions { | ||
/** | ||
* Implementation of the Askar interface according to aries-askar JavaScript wrapper. | ||
* | ||
* | ||
* ## Node.JS | ||
* | ||
* ```ts | ||
* import { NodeJSAriesAskar } from 'aries-askar-nodejs' | ||
* | ||
* const askarModule = new AskarModule({ | ||
* askar: new NodeJSAriesAskar() | ||
* }) | ||
* ``` | ||
* | ||
* ## React Native | ||
* | ||
* ```ts | ||
* import { ReactNativeAriesAskar } from 'aries-askar-react-native' | ||
* | ||
* const askarModule = new AskarModule({ | ||
* askar: new ReactNativeAriesAskar() | ||
* }) | ||
* ``` | ||
*/ | ||
askar: AriesAskar | ||
} | ||
|
||
export class AskarModuleConfig { | ||
private options: AskarModuleConfigOptions | ||
|
||
public constructor(options: AskarModuleConfigOptions) { | ||
this.options = options | ||
} | ||
|
||
public get askar() { | ||
return this.options.askar | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Wallet | ||
export { AskarWallet } from './wallet' | ||
|
||
// Storage | ||
export { AskarStorageService } from './storage' | ||
|
||
// Module | ||
export { AskarModule } from './AskarModule' | ||
export { AskarModuleConfig } from './AskarModuleConfig' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import type { BaseRecordConstructor, AgentContext, BaseRecord, Query, StorageService } from '@aries-framework/core' | ||
|
||
import { | ||
RecordDuplicateError, | ||
WalletError, | ||
RecordNotFoundError, | ||
injectable, | ||
JsonTransformer, | ||
} from '@aries-framework/core' | ||
import { Scan } from '@hyperledger/aries-askar-shared' | ||
|
||
import { askarErrors, isAskarError } from '../utils/askarError' | ||
import { assertAskarWallet } from '../utils/assertAskarWallet' | ||
|
||
import { askarQueryFromSearchQuery, recordToInstance, transformFromRecordTagValues } from './utils' | ||
|
||
@injectable() | ||
export class AskarStorageService<T extends BaseRecord> implements StorageService<T> { | ||
/** @inheritDoc */ | ||
public async save(agentContext: AgentContext, record: T) { | ||
assertAskarWallet(agentContext.wallet) | ||
const session = agentContext.wallet.session | ||
|
||
const value = JsonTransformer.serialize(record) | ||
const tags = transformFromRecordTagValues(record.getTags()) as Record<string, string> | ||
|
||
try { | ||
await session.insert({ category: record.type, name: record.id, value, tags }) | ||
} catch (error) { | ||
if (isAskarError(error) && error.code === askarErrors.Duplicate) { | ||
throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) | ||
} | ||
|
||
throw new WalletError('Error saving record', { cause: error }) | ||
} | ||
} | ||
|
||
/** @inheritDoc */ | ||
public async update(agentContext: AgentContext, record: T): Promise<void> { | ||
assertAskarWallet(agentContext.wallet) | ||
const session = agentContext.wallet.session | ||
|
||
const value = JsonTransformer.serialize(record) | ||
const tags = transformFromRecordTagValues(record.getTags()) as Record<string, string> | ||
|
||
try { | ||
await session.replace({ category: record.type, name: record.id, value, tags }) | ||
} catch (error) { | ||
if (isAskarError(error) && error.code === askarErrors.NotFound) { | ||
throw new RecordNotFoundError(`record with id ${record.id} not found.`, { | ||
recordType: record.type, | ||
cause: error, | ||
}) | ||
} | ||
|
||
throw new WalletError('Error updating record', { cause: error }) | ||
} | ||
} | ||
|
||
/** @inheritDoc */ | ||
public async delete(agentContext: AgentContext, record: T) { | ||
assertAskarWallet(agentContext.wallet) | ||
const session = agentContext.wallet.session | ||
|
||
try { | ||
await session.remove({ category: record.type, name: record.id }) | ||
} catch (error) { | ||
if (isAskarError(error) && error.code === askarErrors.NotFound) { | ||
throw new RecordNotFoundError(`record with id ${record.id} not found.`, { | ||
recordType: record.type, | ||
cause: error, | ||
}) | ||
} | ||
throw new WalletError('Error deleting record', { cause: error }) | ||
} | ||
} | ||
|
||
/** @inheritDoc */ | ||
public async deleteById( | ||
agentContext: AgentContext, | ||
recordClass: BaseRecordConstructor<T>, | ||
id: string | ||
): Promise<void> { | ||
assertAskarWallet(agentContext.wallet) | ||
const session = agentContext.wallet.session | ||
|
||
try { | ||
await session.remove({ category: recordClass.type, name: id }) | ||
} catch (error) { | ||
if (isAskarError(error) && error.code === askarErrors.NotFound) { | ||
throw new RecordNotFoundError(`record with id ${id} not found.`, { | ||
recordType: recordClass.type, | ||
cause: error, | ||
}) | ||
} | ||
throw new WalletError('Error deleting record', { cause: error }) | ||
} | ||
} | ||
|
||
/** @inheritDoc */ | ||
public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor<T>, id: string): Promise<T> { | ||
assertAskarWallet(agentContext.wallet) | ||
const session = agentContext.wallet.session | ||
|
||
try { | ||
const record = await session.fetch({ category: recordClass.type, name: id }) | ||
if (!record) { | ||
throw new RecordNotFoundError(`record with id ${id} not found.`, { | ||
recordType: recordClass.type, | ||
}) | ||
} | ||
return recordToInstance(record, recordClass) | ||
} catch (error) { | ||
if ( | ||
isAskarError(error) && | ||
(error.code === askarErrors.NotFound || | ||
// FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario | ||
error.message === 'Received null pointer. The native library could not find the value.') | ||
) { | ||
throw new RecordNotFoundError(`record with id ${id} not found.`, { | ||
recordType: recordClass.type, | ||
cause: error, | ||
}) | ||
} | ||
throw new WalletError(`Error getting record`, { cause: error }) | ||
} | ||
} | ||
|
||
/** @inheritDoc */ | ||
public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor<T>): Promise<T[]> { | ||
assertAskarWallet(agentContext.wallet) | ||
const session = agentContext.wallet.session | ||
|
||
const records = await session.fetchAll({ category: recordClass.type }) | ||
|
||
const instances = [] | ||
for (const record of records) { | ||
instances.push(recordToInstance(record, recordClass)) | ||
} | ||
return instances | ||
} | ||
|
||
/** @inheritDoc */ | ||
public async findByQuery( | ||
agentContext: AgentContext, | ||
recordClass: BaseRecordConstructor<T>, | ||
query: Query<T> | ||
): Promise<T[]> { | ||
assertAskarWallet(agentContext.wallet) | ||
const store = agentContext.wallet.store | ||
|
||
const askarQuery = askarQueryFromSearchQuery(query) | ||
|
||
const scan = new Scan({ | ||
category: recordClass.type, | ||
store, | ||
tagFilter: askarQuery, | ||
}) | ||
|
||
const instances = [] | ||
try { | ||
const records = await scan.fetchAll() | ||
for (const record of records) { | ||
instances.push(recordToInstance(record, recordClass)) | ||
} | ||
return instances | ||
} catch (error) { | ||
if ( | ||
isAskarError(error) && // FIXME: this is current output from askar wrapper but does not describe specifically a 0 length scenario | ||
error.message === 'Received null pointer. The native library could not find the value.' | ||
) { | ||
return instances | ||
} | ||
throw new WalletError(`Error executing query`, { cause: error }) | ||
} | ||
} | ||
} |
Oops, something went wrong.