Skip to content

Commit

Permalink
✨ (context-module) [DSDK-513]: Add config to change cal endpoint/mode (
Browse files Browse the repository at this point in the history
  • Loading branch information
aussedatlo authored Oct 4, 2024
2 parents 08344ff + ec65c15 commit f6b7415
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-keys-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/context-module": minor
---

Add context module configuration for cal endpoint/mode
9 changes: 9 additions & 0 deletions packages/signer/context-module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ The context-module package exposes a builder `ContextModuleBuilder` which will b
const contextModule = new ContextModuleBuilder().build();
```

You can use a custom configuration for your context module.

```ts
const config: ContextModuleConfig = {
// config to use
};
const contextModule = new ContextModuleBuilder().withConfig(config).build();
```

It is also possible to instantiate the context module without the default loaders.

```ts
Expand Down
24 changes: 24 additions & 0 deletions packages/signer/context-module/src/ContextModuleBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ContextModuleConfig } from "./config/model/ContextModuleConfig";
import { ContextModuleBuilder } from "./ContextModuleBuilder";
import { DefaultContextModule } from "./DefaultContextModule";

Expand All @@ -21,4 +22,27 @@ describe("ContextModuleBuilder", () => {

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

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

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

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

it("should return a custom context module with a custom config", () => {
const contextModuleBuilder = new ContextModuleBuilder();
const customConfig: ContextModuleConfig = {
cal: { url: "https://locahost:3000", mode: "test" },
};

const res = contextModuleBuilder.withConfig(customConfig).build();

expect(res).toBeInstanceOf(DefaultContextModule);
});
});
64 changes: 44 additions & 20 deletions packages/signer/context-module/src/ContextModuleBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { nftTypes } from "@/nft/di/nftTypes";
import { tokenTypes } from "@/token/di/tokenTypes";
import { typedDataTypes } from "@/typed-data/di/typedDataTypes";

import { ContextModuleConfig } from "./config/model/ContextModuleConfig";
import { ExternalPluginContextLoader } from "./external-plugin/domain/ExternalPluginContextLoader";
import { ForwardDomainContextLoader } from "./forward-domain/domain/ForwardDomainContextLoader";
import { NftContextLoader } from "./nft/domain/NftContextLoader";
Expand All @@ -14,28 +15,22 @@ import { ContextModule } from "./ContextModule";
import { DefaultContextModule } from "./DefaultContextModule";
import { makeContainer } from "./di";

const DEFAULT_CAL_URL = "https://crypto-assets-service.api.ledger.com/v1";

export const DEFAULT_CONFIG: ContextModuleConfig = {
cal: {
url: DEFAULT_CAL_URL,
mode: "prod",
},
};

export class ContextModuleBuilder {
private config: Partial<ContextModuleConfig> = {};
private customLoaders: ContextLoader[] = [];
private defaultLoaders: ContextLoader[] = [];
private typedDataLoader: TypedDataContextLoader;
private customTypedDataLoader?: TypedDataContextLoader;

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),
];
this.typedDataLoader = container.get<TypedDataContextLoader>(
typedDataTypes.TypedDataContextLoader,
);
}
constructor() {}

/**
* Remove default loaders from the list of loaders
Expand Down Expand Up @@ -64,7 +59,18 @@ export class ContextModuleBuilder {
* @returns this
*/
withTypedDataLoader(loader: TypedDataContextLoader) {
this.typedDataLoader = loader;
this.customTypedDataLoader = loader;
return this;
}

/**
* Set the configuration for the context module
*
* @param config configuration for the context module
* @returns this
*/
withConfig(config: Partial<ContextModuleConfig>) {
this.config = config;
return this;
}

Expand All @@ -74,10 +80,28 @@ export class ContextModuleBuilder {
* @returns the context module
*/
build(): ContextModule {
const container = makeContainer({
config: { ...DEFAULT_CONFIG, ...this.config },
});

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

const loaders = [...this.defaultLoaders, ...this.customLoaders];
return new DefaultContextModule({
loaders,
typedDataLoader: this.typedDataLoader,
typedDataLoader: this.customTypedDataLoader ?? defaultTypedDataLoader,
});
}
}
12 changes: 12 additions & 0 deletions packages/signer/context-module/src/DefaultContextModule.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TransactionContext } from "./shared/model/TransactionContext";
import { TypedDataContext } from "./shared/model/TypedDataContext";
import type { TypedDataContextLoader } from "./typed-data/domain/TypedDataContextLoader";
import { DefaultContextModule } from "./DefaultContextModule";

Expand Down Expand Up @@ -70,4 +71,15 @@ describe("DefaultContextModule", () => {
expect(loader.load).toHaveBeenCalledTimes(2);
expect(res).toEqual(responses.flat());
});

it("should call the typed data loader", async () => {
const contextModule = new DefaultContextModule({
loaders: [],
typedDataLoader,
});

await contextModule.getTypedDataFilters({} as TypedDataContext);

expect(typedDataLoader.load).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ContainerModule } from "inversify";

import { ContextModuleConfig } from "@/config/model/ContextModuleConfig";

import { configTypes } from "./configTypes";

export const configModuleFactory = (config: ContextModuleConfig) =>
new ContainerModule((bind, _unbind, _isBound, _rebind) => {
bind<ContextModuleConfig>(configTypes.Config).toConstantValue(config);
});
3 changes: 3 additions & 0 deletions packages/signer/context-module/src/config/di/configTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const configTypes = {
Config: Symbol.for("config"),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type ContextModuleCalConfig = {
url: string;
mode: "prod" | "test";
};

export type ContextModuleConfig = {
cal: ContextModuleCalConfig;
};
9 changes: 8 additions & 1 deletion packages/signer/context-module/src/di.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { Container } from "inversify";

import { configModuleFactory } from "@/config/di/configModuleFactory";
import { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
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";
import { typedDataModuleFactory } from "@/typed-data/di/typedDataModuleFactory";

export const makeContainer = () => {
type MakeContainerArgs = {
config: ContextModuleConfig;
};

export const makeContainer = ({ config }: MakeContainerArgs) => {
const container = new Container();

container.load(
configModuleFactory(config),
externalPluginModuleFactory(),
forwardDomainModuleFactory(),
nftModuleFactory(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axios from "axios";

import { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
import ABI from "@/external-plugin/__tests__/abi.json";
import {
Abis,
Expand Down Expand Up @@ -49,7 +50,12 @@ describe("HttpExternalPuginDataSource", () => {
};

beforeAll(() => {
datasource = new HttpExternalPluginDataSource();
const config = {
cal: {
url: "https://crypto-assets-service.api.ledger.com/v1",
},
} as ContextModuleConfig;
datasource = new HttpExternalPluginDataSource(config);
jest.clearAllMocks();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import axios from "axios";
import { injectable } from "inversify";
import { inject, injectable } from "inversify";
import { Either, Left, Right } from "purify-ts";

import { configTypes } from "@/config/di/configTypes";
import type { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
import { DAppDto } from "@/external-plugin/data/DAppDto";
import {
ExternalPluginDataSource,
Expand All @@ -13,7 +15,9 @@ import PACKAGE from "@root/package.json";

@injectable()
export class HttpExternalPluginDataSource implements ExternalPluginDataSource {
constructor() {}
constructor(
@inject(configTypes.Config) private readonly config: ContextModuleConfig,
) {}

async getDappInfos({
chainId,
Expand All @@ -23,7 +27,7 @@ export class HttpExternalPluginDataSource implements ExternalPluginDataSource {
try {
const dappInfos = await axios.request<DAppDto[]>({
method: "GET",
url: "https://crypto-assets-service.api.ledger.com/v1/dapps",
url: `${this.config.cal.url}/dapps`,
params: {
output: "b2c,b2c_signatures,abis",
chain_id: chainId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios from "axios";
import { Left } from "purify-ts";

import { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
import { HttpTokenDataSource } from "@/token/data/HttpTokenDataSource";
import { TokenDataSource } from "@/token/data/TokenDataSource";
import { TokenDto } from "@/token/data/TokenDto";
Expand All @@ -12,7 +13,12 @@ describe("HttpTokenDataSource", () => {
let datasource: TokenDataSource;

beforeAll(() => {
datasource = new HttpTokenDataSource();
const config = {
cal: {
url: "https://crypto-assets-service.api.ledger.com/v1",
},
} as ContextModuleConfig;
datasource = new HttpTokenDataSource(config);
jest.clearAllMocks();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import axios from "axios";
import { injectable } from "inversify";
import { inject, injectable } from "inversify";
import { Either, Left, Right } from "purify-ts";

import { configTypes } from "@/config/di/configTypes";
import type { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
import { HexStringUtils } from "@/shared/utils/HexStringUtils";
import PACKAGE from "@root/package.json";

Expand All @@ -10,14 +12,17 @@ import { TokenDto } from "./TokenDto";

@injectable()
export class HttpTokenDataSource implements TokenDataSource {
constructor(
@inject(configTypes.Config) private readonly config: ContextModuleConfig,
) {}
public async getTokenInfosPayload({
chainId,
address,
}: GetTokenInfosParams): Promise<Either<Error, string>> {
try {
const response = await axios.request<TokenDto[]>({
method: "GET",
url: `https://crypto-assets-service.api.ledger.com/v1/tokens`,
url: `${this.config.cal.url}/tokens`,
params: {
contract_address: address,
chain_id: chainId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios from "axios";
import { Right } from "purify-ts";

import { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
import { HttpTypedDataDataSource } from "@/typed-data/data/HttpTypedDataDataSource";
import { type TypedDataDataSource } from "@/typed-data/data/TypedDataDataSource";
import PACKAGE from "@root/package.json";
Expand Down Expand Up @@ -60,7 +61,12 @@ describe("HttpTypedDataDataSource", () => {
};

beforeAll(() => {
datasource = new HttpTypedDataDataSource();
const config = {
cal: {
url: "https://crypto-assets-service.api.ledger.com/v1",
},
} as ContextModuleConfig;
datasource = new HttpTypedDataDataSource(config);
jest.clearAllMocks();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import axios from "axios";
import SHA224 from "crypto-js/sha224";
import { injectable } from "inversify";
import { inject, injectable } from "inversify";
import { Either, Left, Right } from "purify-ts";

import { configTypes } from "@/config/di/configTypes";
import type { ContextModuleConfig } from "@/config/model/ContextModuleConfig";
import type {
TypedDataFilter,
TypedDataMessageInfo,
Expand All @@ -25,6 +27,10 @@ import {

@injectable()
export class HttpTypedDataDataSource implements TypedDataDataSource {
constructor(
@inject(configTypes.Config) private readonly config: ContextModuleConfig,
) {}

public async getTypedDataFilters({
chainId,
address,
Expand All @@ -36,7 +42,7 @@ export class HttpTypedDataDataSource implements TypedDataDataSource {
try {
const response = await axios.request<FiltersDto[]>({
method: "GET",
url: `https://crypto-assets-service.api.ledger.com/v1/dapps`,
url: `${this.config.cal.url}/dapps`,
params: {
contracts: address,
chain_id: chainId,
Expand Down

0 comments on commit f6b7415

Please sign in to comment.