From 94cbd9f8c494de1bb737c24ec694c130e787d8b6 Mon Sep 17 00:00:00 2001
From: Benoit Devos
Date: Tue, 12 Mar 2024 18:04:49 +0100
Subject: [PATCH 1/2] feat: create LogionClient from Environment.
logion-network/logion-internal#1099
---
README.md | 5 +++
src/__mocks__/LogionClientMock.ts | 4 ++
src/config/development-env.json.sample | 5 +++
src/config/index.tsx | 7 ++--
src/loc/CertificateAndLimits.tsx | 5 +--
src/loc/ImportItems.tsx | 56 +++++++++++++------------
src/logion-chain/LogionChainContext.tsx | 34 ++++++++-------
7 files changed, 69 insertions(+), 47 deletions(-)
create mode 100644 src/config/development-env.json.sample
diff --git a/README.md b/README.md
index 1b5b2851..89ca8fa6 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,11 @@ For the most recent version and how to install yarn, please refer to [yarn](http
yarn install
```
+## Target an environment
+A config file `development.json` is required under [src/config/](src/config/).
+* To target a locally running node and directory, or fine-tune the config, it's recommended to copy and/or adapt from [development.json.sample](src/config/development.json.sample).
+* To target a deployed environment (i.e. DEV, TEST or MVP), it's recommended to copy and/or adapt from [development-env.json.sample](src/config/development-env.json.sample).
+
## Usage
Use below command in order to connect to a locally running infrastructure
diff --git a/src/__mocks__/LogionClientMock.ts b/src/__mocks__/LogionClientMock.ts
index 051e1718..b40c4766 100644
--- a/src/__mocks__/LogionClientMock.ts
+++ b/src/__mocks__/LogionClientMock.ts
@@ -83,6 +83,10 @@ export class LogionClient {
throw new Error();
}
}
+
+ config = {
+ rpcEndpoints: [ "ws://localhost:9944", "ws://localhost:9945", "ws://localhost:9946" ]
+ };
}
export class AccountTokens {
diff --git a/src/config/development-env.json.sample b/src/config/development-env.json.sample
new file mode 100644
index 00000000..6b425b38
--- /dev/null
+++ b/src/config/development-env.json.sample
@@ -0,0 +1,5 @@
+{
+ "APP_NAME": "Logion Wallet DEV",
+ "environment": "DEV"
+ "crossmintApiKey": "",
+}
diff --git a/src/config/index.tsx b/src/config/index.tsx
index 713392d9..76e44066 100644
--- a/src/config/index.tsx
+++ b/src/config/index.tsx
@@ -1,13 +1,15 @@
+import { EnvironmentString } from "@logion/client";
+
export interface Node {
socket: string;
peerId: string;
}
export interface ConfigType {
+ environment: EnvironmentString | undefined,
APP_NAME: string,
DEVELOPMENT_KEYRING: boolean,
PROVIDER_SOCKET?: string,
- RPC: object,
directory: string,
edgeNodes: Node[],
crossmintApiKey: string,
@@ -16,10 +18,9 @@ export interface ConfigType {
}
export const DEFAULT_CONFIG: ConfigType = {
+ environment: undefined,
APP_NAME: "Logion Wallet",
DEVELOPMENT_KEYRING: true,
- RPC: {
- },
directory: "",
edgeNodes: [],
crossmintApiKey: "",
diff --git a/src/loc/CertificateAndLimits.tsx b/src/loc/CertificateAndLimits.tsx
index 75bfb9f9..f0e23d39 100644
--- a/src/loc/CertificateAndLimits.tsx
+++ b/src/loc/CertificateAndLimits.tsx
@@ -9,7 +9,6 @@ import Dialog from '../common/Dialog';
import Icon from '../common/Icon';
import { useLogionChain } from '../logion-chain';
import { fullCertificateUrl } from '../PublicPaths';
-import config from '../config';
import NewTabLink from '../common/NewTabLink';
import StaticLabelValue from '../common/StaticLabelValue';
@@ -39,7 +38,7 @@ export interface Props {
export default function CertificateAndLimits(props: Props) {
const { loc } = props;
- const { api } = useLogionChain();
+ const { api, client } = useLogionChain();
const { backendConfig } = useCommonContext();
const [ dateLimit, setDateLimit ] = useState();
@@ -141,7 +140,7 @@ export default function CertificateAndLimits(props: Props) {
{ config.edgeNodes[0].socket }
+ { client?.config.rpcEndpoints[0] }
}
/>
diff --git a/src/loc/ImportItems.tsx b/src/loc/ImportItems.tsx
index dfa1ace3..6feb1680 100644
--- a/src/loc/ImportItems.tsx
+++ b/src/loc/ImportItems.tsx
@@ -8,6 +8,7 @@ import {
LogionClassification,
SpecificLicense,
CreativeCommons,
+ LogionClientConfig,
} from "@logion/client";
import { Fees, Hash, Lgnt } from '@logion/node-api';
import { useCallback, useMemo, useState } from "react";
@@ -28,7 +29,6 @@ import './ImportItems.css';
import { CsvItem, readItemsCsv } from "./ImportCsvReader";
import Alert from "src/common/Alert";
import { UUID } from "@logion/node-api";
-import config from "../config";
import EstimatedFees from "./fees/EstimatedFees";
import { BrowserFile } from "@logion/client-browser";
import { Call, CallBatch, CallCallback } from "src/logion-chain/LogionChainContext";
@@ -36,7 +36,7 @@ import ExtrinsicSubmissionStateView from "src/ExtrinsicSubmissionStateView";
export default function ImportItems() {
const { width } = useResponsiveContext();
- const { signer, submitCallBatch, extrinsicSubmissionState, clearSubmissionState } = useLogionChain();
+ const { signer, submitCallBatch, extrinsicSubmissionState, clearSubmissionState, client } = useLogionChain();
const { colorTheme } = useCommonContext();
const { refresh, locState } = useUserLocContext();
@@ -48,29 +48,31 @@ export default function ImportItems() {
const [ itemToSubmit, setItemToSubmit ] = useState- ();
const readCsvFile = useCallback(async (file: File) => {
- const collection = locState as ClosedCollectionLoc;
- const acceptsUpload = collectionAcceptsUpload(collection);
- clearSubmissionState();
-
- const result = await readItemsCsv(file);
- if("items" in result) {
- const rows = toItems(result.items, acceptsUpload);
-
- for(const item of rows) {
- if(!item.error) {
- const existingItem = await collection.getCollectionItem({ itemId: item.id as Hash });
- item.submitted = existingItem !== undefined;
- item.success = existingItem !== undefined;
- item.upload = shouldUpload(acceptsUpload, existingItem, item.upload);
+ if (client !== null) {
+ const collection = locState as ClosedCollectionLoc;
+ const acceptsUpload = collectionAcceptsUpload(collection);
+ clearSubmissionState();
+
+ const result = await readItemsCsv(file);
+ if ("items" in result) {
+ const rows = toItems(result.items, acceptsUpload, client.config);
+
+ for (const item of rows) {
+ if (!item.error) {
+ const existingItem = await collection.getCollectionItem({ itemId: item.id as Hash });
+ item.submitted = existingItem !== undefined;
+ item.success = existingItem !== undefined;
+ item.upload = shouldUpload(acceptsUpload, existingItem, item.upload);
+ }
}
- }
- setItems(rows);
- } else {
- setCsvReadError(result.error);
+ setItems(rows);
+ } else {
+ setCsvReadError(result.error);
+ }
+ setShowImportItems(true);
}
- setShowImportItems(true);
- }, [ clearSubmissionState, locState ]);
+ }, [ clearSubmissionState, locState, client ]);
const itemFees = useCallback(async (item: Item) => {
const collection = locState as ClosedCollectionLoc;
@@ -437,7 +439,7 @@ function collectionAcceptsUpload(collection: ClosedCollectionLoc): boolean {
return collection.data().collectionCanUpload !== undefined && collection.data().collectionCanUpload === true;
}
-function toItems(csvItems: CsvItem[], collectionAcceptsUpload: boolean): Item[] {
+function toItems(csvItems: CsvItem[], collectionAcceptsUpload: boolean, config: LogionClientConfig): Item[] {
return csvItems.map(csvItem => {
const id = csvItem.id;
const displayId = csvItem.displayId;
@@ -497,7 +499,7 @@ function toItems(csvItems: CsvItem[], collectionAcceptsUpload: boolean): Item[]
let tcValidationResult: TCValidationResult = {}
if (error === undefined) {
- tcValidationResult = validateTermsAndConditions(csvItem);
+ tcValidationResult = validateTermsAndConditions(csvItem, config);
error = tcValidationResult.tcError;
errorType = "validation";
}
@@ -531,17 +533,17 @@ interface TCValidationResult {
tcError?: string;
}
-function validateTermsAndConditions(csvItem: CsvItem): TCValidationResult {
+function validateTermsAndConditions(csvItem: CsvItem, config: LogionClientConfig): TCValidationResult {
try {
if (csvItem.termsAndConditionsType === 'logion_classification') {
- const logionClassificationLocId = UUID.fromAnyString(config.logionClassification);
+ const logionClassificationLocId = config.logionClassificationLoc;
if (logionClassificationLocId === undefined) {
return { tcError: "logion_classification: Logion Classification LOC id not properly configured" }
}
const logionClassification = LogionClassification.fromDetails(logionClassificationLocId, csvItem.termsAndConditionsParameters);
return { logionClassification }
} else if (csvItem.termsAndConditionsType === 'CC4.0') {
- const ccLocId = UUID.fromAnyString(config.creativeCommons);
+ const ccLocId = config.creativeCommonsLoc;
if (ccLocId === undefined) {
return { tcError: "CC4.0: LOC id not properly configured" }
}
diff --git a/src/logion-chain/LogionChainContext.tsx b/src/logion-chain/LogionChainContext.tsx
index 388d4085..edeafcaf 100644
--- a/src/logion-chain/LogionChainContext.tsx
+++ b/src/logion-chain/LogionChainContext.tsx
@@ -16,12 +16,12 @@ import {
InjectedAccount,
isExtensionAvailable
} from '@logion/extension';
-import { buildApiClass, LogionNodeApiClass, ValidAccountId } from '@logion/node-api';
+import { LogionNodeApiClass, ValidAccountId, UUID } from '@logion/node-api';
import axios, { AxiosInstance } from 'axios';
import React, { useReducer, useContext, Context, Reducer, useEffect, useCallback } from 'react';
import { DateTime } from 'luxon';
-import config, { Node } from '../config';
+import config from '../config';
import Accounts, { buildAccounts, toValidAccountId } from '../common/types/Accounts';
import {
clearAll,
@@ -33,7 +33,7 @@ import {
} from '../common/Storage';
import { getEndpoints, NodeMetadata } from './Connection';
-import { BrowserAxiosFileUploader } from '@logion/client-browser';
+import { BrowserAxiosFileUploader, newLogionClient } from '@logion/client-browser';
type ConsumptionStatus = 'PENDING' | 'STARTING' | 'STARTED';
@@ -79,7 +79,7 @@ export class CallBatch {
}
readonly jobs: CallBatchJobs;
-};
+}
export class ExtrinsicSubmissionState {
@@ -238,7 +238,6 @@ export interface LogionChainContextType {
injectedAccounts: InjectedAccount[] | null,
metaMaskAccounts: InjectedAccount[] | null,
allAccounts: InjectedAccount[] | null,
- edgeNodes: Node[],
connectedNodeMetadata: NodeMetadata | null,
extensionsEnabled: boolean,
client: LogionClient | null,
@@ -275,7 +274,6 @@ const initState = (): FullLogionChainContextType => ({
injectedAccounts: null,
metaMaskAccounts: null,
allAccounts: null,
- edgeNodes: config.edgeNodes,
connectedNodeMetadata: null,
extensionsEnabled: false,
connecting: false,
@@ -686,15 +684,23 @@ const LogionChainContextProvider = (props: LogionChainContextProviderProps): JSX
});
const accounts = state.allAccounts;
+ let logionClient: LogionClient;
(async function() {
- const rpcEndpoints = getEndpoints();
- const api = await buildApiClass(rpcEndpoints);
+ if (config.environment) {
+ logionClient = await newLogionClient(config.environment);
+ } else {
+ const rpcEndpoints = getEndpoints();
+ logionClient = await LogionClient.create({
+ rpcEndpoints,
+ directoryEndpoint: config.directory,
+ buildFileUploader: () => new BrowserAxiosFileUploader(),
+ logionClassificationLoc: UUID.fromAnyString(config.logionClassification),
+ creativeCommonsLoc: UUID.fromAnyString(config.creativeCommons),
+ });
+ }
+
+ const api = logionClient.logionApi;
const peerId = await api.polkadot.rpc.system.localPeerId();
- const logionClient = await LogionClient.create({
- rpcEndpoints,
- directoryEndpoint: config.directory,
- buildFileUploader: () => new BrowserAxiosFileUploader(),
- });
let startupTokens: AccountTokens;
try {
@@ -857,7 +863,7 @@ const LogionChainContextProvider = (props: LogionChainContextProviderProps): JSX
type: 'SET_METAMASK_ACCOUNTS',
metaMaskAccounts: [],
});
-
+
const metaMaskEnabled = await enableMetaMask(config.APP_NAME);
if(metaMaskEnabled) {
const metaMaskAccounts = await allMetamaskAccounts();
From d7d8fae64612ff84bba342a97b84061dc7ff3015 Mon Sep 17 00:00:00 2001
From: Benoit Devos
Date: Wed, 13 Mar 2024 16:01:23 +0100
Subject: [PATCH 2/2] feat: replace edgeNodes with rpcEndpoints; remove useless
properties.
logion-network/logion-internal#1099
---
public/config.js | 7 +------
src/config/development.json.sample | 17 ++++------------
src/config/entrypoint.test.tsx | 20 -------------------
src/config/index.tsx | 26 +++----------------------
src/config/production.json | 1 -
src/config/test.json | 17 ++++------------
src/logion-chain/Connection.ts | 11 -----------
src/logion-chain/LogionChainContext.tsx | 5 ++---
8 files changed, 14 insertions(+), 90 deletions(-)
delete mode 100644 src/config/entrypoint.test.tsx
diff --git a/public/config.js b/public/config.js
index c0f091ca..1d64bd27 100644
--- a/public/config.js
+++ b/public/config.js
@@ -2,10 +2,5 @@ let CONFIG = {
// Uncomment below lines to provide runtime configuration.
// APP_NAME: "Logion Wallet",
// directory: "http://localhost:8090/api",
-// edgeNodes: [
-// {
-// peerId: "12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2",
-// socket: "ws://localhost:9944"
-// }
-// ]
+// rpcEndpoints: [ "ws://localhost:9944" ]
};
diff --git a/src/config/development.json.sample b/src/config/development.json.sample
index 144d7e5d..74f06181 100644
--- a/src/config/development.json.sample
+++ b/src/config/development.json.sample
@@ -1,19 +1,10 @@
{
"APP_NAME": "Logion Wallet DEV",
"directory": "http://localhost:8090",
- "edgeNodes": [
- {
- "peerId": "12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2",
- "socket": "ws://localhost:9944"
- },
- {
- "peerId": "12D3KooWQYV9dGMFoRzNStwpXztXaBUjtPqi6aU76ZgUriHhKust",
- "socket": "ws://localhost:9945"
- },
- {
- "peerId": "12D3KooWJvyP3VJYymTqG7eH4PM5rN4T2agk5cdNCfNymAqwqcvZ",
- "socket": "ws://localhost:9946"
- }
+ "rpcEndpoints": [
+ "ws://localhost:9944",
+ "ws://localhost:9945",
+ "ws://localhost:9946"
],
"crossmintApiKey": "",
"logionClassification": "",
diff --git a/src/config/entrypoint.test.tsx b/src/config/entrypoint.test.tsx
deleted file mode 100644
index c428b931..00000000
--- a/src/config/entrypoint.test.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import 'luxon';
-
-const OLD_ENV = process.env;
-
-beforeEach(() => {
- jest.resetModules() // Most important - it clears the cache
- process.env = { ...OLD_ENV }; // Make a copy
-});
-
-afterAll(() => {
- process.env = OLD_ENV; // Restore old environment
-});
-
-test("Config reads socket and keyring from env", () => {
- process.env.REACT_APP_PROVIDER_SOCKET = "my-socket";
- process.env.REACT_APP_DEVELOPMENT_KEYRING = "true";
- const config = require('./index.tsx').default;
- expect(config.PROVIDER_SOCKET).toBe("my-socket");
- expect(config.DEVELOPMENT_KEYRING).toBe("true");
-});
diff --git a/src/config/index.tsx b/src/config/index.tsx
index 76e44066..585da260 100644
--- a/src/config/index.tsx
+++ b/src/config/index.tsx
@@ -1,17 +1,10 @@
import { EnvironmentString } from "@logion/client";
-export interface Node {
- socket: string;
- peerId: string;
-}
-
export interface ConfigType {
environment: EnvironmentString | undefined,
APP_NAME: string,
- DEVELOPMENT_KEYRING: boolean,
- PROVIDER_SOCKET?: string,
directory: string,
- edgeNodes: Node[],
+ rpcEndpoints: string[],
crossmintApiKey: string,
logionClassification: string,
creativeCommons: string,
@@ -20,9 +13,8 @@ export interface ConfigType {
export const DEFAULT_CONFIG: ConfigType = {
environment: undefined,
APP_NAME: "Logion Wallet",
- DEVELOPMENT_KEYRING: true,
directory: "",
- edgeNodes: [],
+ rpcEndpoints: [],
crossmintApiKey: "",
logionClassification: "",
creativeCommons: "",
@@ -34,19 +26,7 @@ export interface EnvConfigType extends Record {
const configEnv: EnvConfigType = require(`./${process.env.NODE_ENV}.json`);
-const envVarNames: string[] = [
- 'REACT_APP_PROVIDER_SOCKET',
- 'REACT_APP_DEVELOPMENT_KEYRING'
-];
-const envVars: EnvConfigType = envVarNames.reduce((mem, n) => {
- if (process.env[n] !== undefined) {
- const configFieldName = n.slice(10);
- mem[configFieldName] = process.env[n];
- }
- return mem;
-}, {});
-
declare var CONFIG: any;
-const config: ConfigType = { ...DEFAULT_CONFIG, ...configEnv, ...envVars, ...CONFIG };
+const config: ConfigType = { ...DEFAULT_CONFIG, ...configEnv, ...CONFIG };
export default config;
diff --git a/src/config/production.json b/src/config/production.json
index 1205499c..0d82e82c 100644
--- a/src/config/production.json
+++ b/src/config/production.json
@@ -1,6 +1,5 @@
{
"APP_NAME": "Logion Wallet",
"directory": "http://localhost:8090/api",
- "edgeNodes": [],
"crossmintApiKey": ""
}
diff --git a/src/config/test.json b/src/config/test.json
index 13deef3d..f25ebe73 100644
--- a/src/config/test.json
+++ b/src/config/test.json
@@ -1,19 +1,10 @@
{
"APP_NAME": "Logion Wallet TEST",
"directory": "http://localhost:8090/api",
- "edgeNodes": [
- {
- "peerId": "12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2",
- "socket": "ws://localhost:9944"
- },
- {
- "peerId": "12D3KooWQYV9dGMFoRzNStwpXztXaBUjtPqi6aU76ZgUriHhKust",
- "socket": "ws://localhost:9945"
- },
- {
- "peerId": "12D3KooWJvyP3VJYymTqG7eH4PM5rN4T2agk5cdNCfNymAqwqcvZ",
- "socket": "ws://localhost:9946"
- }
+ "rpcEndpoints": [
+ "ws://localhost:9944",
+ "ws://localhost:9945",
+ "ws://localhost:9946"
],
"crossmintApiKey": ""
}
diff --git a/src/logion-chain/Connection.ts b/src/logion-chain/Connection.ts
index c52db151..4f629593 100644
--- a/src/logion-chain/Connection.ts
+++ b/src/logion-chain/Connection.ts
@@ -1,14 +1,3 @@
-import config from '../config';
-
export interface NodeMetadata {
peerId: string
}
-
-export function getEndpoints(): string[] {
- const providerSocket = config.PROVIDER_SOCKET;
- if(providerSocket !== undefined) {
- return [ providerSocket ];
- } else {
- return config.edgeNodes.map(node => node.socket);
- }
-}
diff --git a/src/logion-chain/LogionChainContext.tsx b/src/logion-chain/LogionChainContext.tsx
index edeafcaf..f94e4792 100644
--- a/src/logion-chain/LogionChainContext.tsx
+++ b/src/logion-chain/LogionChainContext.tsx
@@ -32,7 +32,7 @@ import {
storeTokens
} from '../common/Storage';
-import { getEndpoints, NodeMetadata } from './Connection';
+import { NodeMetadata } from './Connection';
import { BrowserAxiosFileUploader, newLogionClient } from '@logion/client-browser';
type ConsumptionStatus = 'PENDING' | 'STARTING' | 'STARTED';
@@ -689,9 +689,8 @@ const LogionChainContextProvider = (props: LogionChainContextProviderProps): JSX
if (config.environment) {
logionClient = await newLogionClient(config.environment);
} else {
- const rpcEndpoints = getEndpoints();
logionClient = await LogionClient.create({
- rpcEndpoints,
+ rpcEndpoints: config.rpcEndpoints,
directoryEndpoint: config.directory,
buildFileUploader: () => new BrowserAxiosFileUploader(),
logionClassificationLoc: UUID.fromAnyString(config.logionClassification),