Skip to content

Commit

Permalink
✨ (signer) [DSDK-365]: Add signer context-module (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
aussedatlo authored Jul 5, 2024
2 parents 33fcf03 + 8d73066 commit 1e2a2fd
Show file tree
Hide file tree
Showing 68 changed files with 3,682 additions and 61 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"typecheck": "turbo run typecheck",
"health-check": "turbo run health-check --output-logs=errors-only --continue",
"core": "pnpm --filter @ledgerhq/device-sdk-core",
"signer": "pnpm --filter @ledgerhq/device-sdk-signer",
"context-module": "pnpm --filter @ledgerhq/context-module",
"trusted-apps": "pnpm --filter @ledgerhq/device-sdk-trusted-apps",
"ui": "pnpm --filter @ledgerhq/device-sdk-ui",
"sample": "pnpm --filter @ledgerhq/device-sdk-sample",
Expand Down
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions packages/signer/context-module/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Context Module
File renamed without changes.
1 change: 1 addition & 0 deletions packages/signer/context-module/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./src/index";
20 changes: 20 additions & 0 deletions packages/signer/context-module/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint no-restricted-syntax: 0 */
import type { JestConfigWithTsJest } from "ts-jest";

const config: JestConfigWithTsJest = {
preset: "@ledgerhq/jest-config-dsdk",
setupFiles: ["<rootDir>/jest.setup.ts"],
testPathIgnorePatterns: ["<rootDir>/lib/esm", "<rootDir>/lib/cjs"],
collectCoverageFrom: [
"src/**/*.ts",
"!src/**/*.stub.ts",
"!src/index.ts",
"!src/api/index.ts",
],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
"^@root/(.*)$": "<rootDir>/$1",
},
};

export default config;
1 change: 1 addition & 0 deletions packages/signer/context-module/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "reflect-metadata";
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"name": "@ledgerhq/device-sdk-signer",
"version": "1.0.0",
"name": "@ledgerhq/context-module",
"version": "0.1.0",
"license": "MIT",
"main": "lib/cjs/index.js",
"types": "lib/cjs/index.d.ts",
"private": true,
"exports": {
".": {
Expand Down Expand Up @@ -37,18 +35,23 @@
"lint:fix": "pnpm lint --fix",
"prettier": "prettier . --check",
"prettier:fix": "prettier . --write",
"typecheck": "tsc --noEmit",
"test": "jest --passWithNoTests",
"test:coverage": "pnpm test -- --coverage"
},
"dependencies": {
"inversify": "^6.0.2",
"reflect-metadata": "^0.2.2"
"test": "jest",
"test:watch": "pnpm test -- --watch",
"test:coverage": "pnpm test -- --coverage",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@ledgerhq/eslint-config-dsdk": "workspace:*",
"@ledgerhq/jest-config-dsdk": "workspace:*",
"@ledgerhq/prettier-config-dsdk": "workspace:*",
"@ledgerhq/tsconfig-dsdk": "workspace:*"
"@ledgerhq/tsconfig-dsdk": "workspace:*",
"ts-node": "^10.9.2"
},
"dependencies": {
"axios": "^1.7.2",
"ethers": "^5.7.2",
"inversify": "^6.0.2",
"purify-ts": "^2.0.3",
"reflect-metadata": "^0.2.2"
}
}
36 changes: 36 additions & 0 deletions packages/signer/context-module/scripts/add-module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env zx
import "zx/globals";
import { basename } from "node:path";
const modules = argv._;

if (!modules.length) {
console.error(`Usage: ${basename(__filename)} <module1> [<module2> ...]`);
process.exit(1);
}

within(async () => {
cd("src/internal");
for (const mod of modules) {
const rootFolderName = `${mod}`;
const moduleUppercased = mod.charAt(0).toUpperCase() + mod.slice(1);
await $`mkdir ${rootFolderName}`;
within(async () => {
cd(rootFolderName);
await $`mkdir data di model service usecase`;
const files = [
`data/${moduleUppercased}DataSource.ts`,
`di/${mod}Module.test.ts`,
`di/${mod}Module.ts`,
`di/${mod}Types.ts`,
`model/.gitkeep`,
`service/${moduleUppercased}Service.ts`,
`service/Default${moduleUppercased}Service.test.ts`,
`service/Default${moduleUppercased}Service.ts`,
`usecase/.gitkeep`,
];
for (const file of files) {
await $`touch ${file}`;
}
});
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ const tsconfigEsm = path.join(root, "tsconfig.esm.json");
const tsconfigCjs = path.join(root, "tsconfig.cjs.json");

const buildEsm = async () => {
await $`tsc --project ${tsconfigEsm}`;
await $`tsc --project ${tsconfigEsm} --incremental`;
await $`tsc-alias --project ${tsconfigEsm}`;
};

const builCjs = async () => {
await $`tsc --project ${tsconfigCjs}`;
await $`tsc --project ${tsconfigCjs} --incremental`;
await $`tsc-alias --project ${tsconfigCjs}`;
};

Expand Down
7 changes: 7 additions & 0 deletions packages/signer/context-module/src/ContextModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ClearSignContext } from "@/shared/model/ClearSignContext";

import { TransactionContext } from "./shared/model/TransactionContext";

export interface ContextModule {
getContexts(transaction: TransactionContext): Promise<ClearSignContext[]>;
}
24 changes: 24 additions & 0 deletions packages/signer/context-module/src/ContextModuleBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ContextModuleBuilder } from "./ContextModuleBuilder";
import { DefaultContextModule } from "./DefaultContextModule";

describe("ContextModuleBuilder", () => {
it("should return a default context module", () => {
const contextModuleBuilder = new ContextModuleBuilder();

const res = contextModuleBuilder.build();

expect(res).toBeInstanceOf(DefaultContextModule);
});

it("should return a custom context module", () => {
const contextModuleBuilder = new ContextModuleBuilder();
const customLoader = { load: jest.fn() };

const res = contextModuleBuilder
.withoutDefaultLoaders()
.addLoader(customLoader)
.build();

expect(res).toBeInstanceOf(DefaultContextModule);
});
});
64 changes: 64 additions & 0 deletions packages/signer/context-module/src/ContextModuleBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { externalPluginTypes } from "@/external-plugin/di/externalPluginTypes";
import { forwardDomainTypes } from "@/forward-domain/di/forwardDomainTypes";
import { nftTypes } from "@/nft/di/nftTypes";
import { tokenTypes } from "@/token/di/tokenTypes";

import { ExternalPluginContextLoader } from "./external-plugin/domain/ExternalPluginContextLoader";
import { ForwardDomainContextLoader } from "./forward-domain/domain/ForwardDomainContextLoader";
import { NftContextLoader } from "./nft/domain/NftContextLoader";
import { ContextLoader } from "./shared/domain/ContextLoader";
import { TokenContextLoader } from "./token/domain/TokenContextLoader";
import { ContextModule } from "./ContextModule";
import { DefaultContextModule } from "./DefaultContextModule";
import { makeContainer } from "./di";

export class ContextModuleBuilder {
private customLoaders: ContextLoader[] = [];
private defaultLoaders: ContextLoader[] = [];

constructor() {
const container = makeContainer();

this.defaultLoaders = [
container.get<ExternalPluginContextLoader>(
externalPluginTypes.ExternalPluginContextLoader,
),
container.get<ForwardDomainContextLoader>(
forwardDomainTypes.ForwardDomainContextLoader,
),
container.get<NftContextLoader>(nftTypes.NftContextLoader),
container.get<TokenContextLoader>(tokenTypes.TokenContextLoader),
];
}

/**
* Remove default loaders from the list of loaders
*
* @returns this
*/
withoutDefaultLoaders() {
this.defaultLoaders = [];
return this;
}

/**
* Add a custom loader to the list of loaders
*
* @param loader loader to add to the list of loaders
* @returns this
*/
addLoader(loader: ContextLoader) {
this.customLoaders.push(loader);
return this;
}

/**
* Build the context module
*
* @returns the context module
*/
build(): ContextModule {
const loaders = [...this.defaultLoaders, ...this.customLoaders];
return new DefaultContextModule({ loaders });
}
}
62 changes: 62 additions & 0 deletions packages/signer/context-module/src/DefaultContextModule.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { TransactionContext } from "./shared/model/TransactionContext";
import { DefaultContextModule } from "./DefaultContextModule";

const contextLoaderStubBuilder = () => {
return { load: jest.fn() };
};

describe("DefaultContextModule", () => {
beforeEach(() => {
jest.restoreAllMocks();
});

it("should initialize the context module with all the default loaders", async () => {
const contextModule = new DefaultContextModule({ loaders: [] });

const res = await contextModule.getContexts({} as TransactionContext);

expect(res).toEqual([]);
});

it("should return an empty array when no loaders", async () => {
const contextModule = new DefaultContextModule({ loaders: [] });

const res = await contextModule.getContexts({} as TransactionContext);

expect(res).toEqual([]);
});

it("should call all fetch method from metadata fetcher", async () => {
const loader = contextLoaderStubBuilder();
const contextModule = new DefaultContextModule({
loaders: [loader, loader],
});

await contextModule.getContexts({} as TransactionContext);

expect(loader.load).toHaveBeenCalledTimes(2);
});

it("should return an array of context response", async () => {
const loader = contextLoaderStubBuilder();
const responses = [
[{ type: "provideERC20Info", payload: "payload1" }],
[
{ type: "provideERC20Info", payload: "payload2" },
{ type: "plugin", payload: "payload3" },
],
];
jest
.spyOn(loader, "load")
.mockResolvedValueOnce(responses[0])
.mockResolvedValueOnce(responses[1]);
const contextModule = new DefaultContextModule({
loaders: [loader, loader],
});

const res = await contextModule.getContexts({} as TransactionContext);

expect(loader.load).toHaveBeenCalledTimes(2);
expect(res).toEqual(responses.flat());
});
});
24 changes: 24 additions & 0 deletions packages/signer/context-module/src/DefaultContextModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ContextLoader } from "./shared/domain/ContextLoader";
import { ClearSignContext } from "./shared/model/ClearSignContext";
import { TransactionContext } from "./shared/model/TransactionContext";
import { ContextModule } from "./ContextModule";

type DefaultContextModuleConstructorArgs = {
loaders: ContextLoader[];
};

export class DefaultContextModule implements ContextModule {
private _loaders: ContextLoader[];

constructor(args: DefaultContextModuleConstructorArgs) {
this._loaders = args.loaders;
}

public async getContexts(
transaction: TransactionContext,
): Promise<ClearSignContext[]> {
const promises = this._loaders.map((fetcher) => fetcher.load(transaction));
const responses = await Promise.all(promises);
return responses.flat();
}
}
19 changes: 19 additions & 0 deletions packages/signer/context-module/src/di.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Container } from "inversify";

import { externalPluginModuleFactory } from "@/external-plugin/di/externalPluginModuleFactory";
import { forwardDomainModuleFactory } from "@/forward-domain/di/forwardDomainModuleFactory";
import { nftModuleFactory } from "@/nft/di/nftModuleFactory";
import { tokenModuleFactory } from "@/token/di/tokenModuleFactory";

export const makeContainer = () => {
const container = new Container();

container.load(
externalPluginModuleFactory(),
forwardDomainModuleFactory(),
nftModuleFactory(),
tokenModuleFactory(),
);

return container;
};
Loading

0 comments on commit 1e2a2fd

Please sign in to comment.