Use file-saver to export accounts (#701)

* use file-saver

* bring back the fake implementation

* we never know

* fix background test
This commit is contained in:
Thibaut Sardan 2021-04-22 14:34:14 +02:00 committed by GitHub
parent f4308112f7
commit 5a45e1daea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 41 additions and 22 deletions

View File

@ -69,8 +69,8 @@ describe('Extension', () => {
password password
}, {} as chrome.runtime.Port); }, {} as chrome.runtime.Port);
expect(result.exportedJson).toContain(address); expect(result.exportedJson.address).toBe(address);
expect(result.exportedJson).toContain('"encoded"'); expect(result.exportedJson.encoded).toBeDefined();
}); });
describe('account derivation', () => { describe('account derivation', () => {

View File

@ -5,7 +5,7 @@ import type { MetadataDef } from '@polkadot/extension-inject/types';
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/keyring/types'; import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/keyring/types';
import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
import type { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import type { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, ResponseAccountExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types'; import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, ResponseAccountExport, ResponseAccountsExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types';
import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults'; import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults';
import chrome from '@polkadot/extension-inject/chrome'; import chrome from '@polkadot/extension-inject/chrome';
@ -97,14 +97,12 @@ export default class Extension {
} }
private accountsExport ({ address, password }: RequestAccountExport): ResponseAccountExport { private accountsExport ({ address, password }: RequestAccountExport): ResponseAccountExport {
return { exportedJson: JSON.stringify(keyring.backupAccount(keyring.getPair(address), password)) }; return { exportedJson: keyring.backupAccount(keyring.getPair(address), password) };
} }
private async accountsBatchExport ({ addresses, password }: RequestAccountBatchExport): Promise<ResponseAccountExport> { private async accountsBatchExport ({ addresses, password }: RequestAccountBatchExport): Promise<ResponseAccountsExport> {
return { return {
exportedJson: JSON.stringify( exportedJson: await keyring.backupAccounts(addresses, password)
await keyring.backupAccounts(addresses, password)
)
}; };
} }

View File

@ -79,7 +79,7 @@ export interface RequestSignatures {
'pri(accounts.create.suri)': [RequestAccountCreateSuri, boolean]; 'pri(accounts.create.suri)': [RequestAccountCreateSuri, boolean];
'pri(accounts.edit)': [RequestAccountEdit, boolean]; 'pri(accounts.edit)': [RequestAccountEdit, boolean];
'pri(accounts.export)': [RequestAccountExport, ResponseAccountExport]; 'pri(accounts.export)': [RequestAccountExport, ResponseAccountExport];
'pri(accounts.batchExport)': [RequestAccountBatchExport, ResponseAccountExport] 'pri(accounts.batchExport)': [RequestAccountBatchExport, ResponseAccountsExport]
'pri(accounts.forget)': [RequestAccountForget, boolean]; 'pri(accounts.forget)': [RequestAccountForget, boolean];
'pri(accounts.show)': [RequestAccountShow, boolean]; 'pri(accounts.show)': [RequestAccountShow, boolean];
'pri(accounts.tie)': [RequestAccountTie, boolean]; 'pri(accounts.tie)': [RequestAccountTie, boolean];
@ -352,7 +352,11 @@ export interface ResponseSeedValidate {
} }
export interface ResponseAccountExport { export interface ResponseAccountExport {
exportedJson: string; exportedJson: KeyringPair$Json;
}
export interface ResponseAccountsExport {
exportedJson: KeyringPairs$Json;
} }
export type ResponseRpcListProviders = ProviderList; export type ResponseRpcListProviders = ProviderList;

View File

@ -21,6 +21,7 @@
"@polkadot/react-qr": "^0.72.1", "@polkadot/react-qr": "^0.72.1",
"@polkadot/util": "^6.0.5", "@polkadot/util": "^6.0.5",
"@polkadot/util-crypto": "^6.0.5", "@polkadot/util-crypto": "^6.0.5",
"file-saver": "^2.0.5",
"i18next": "^19.9.2", "i18next": "^19.9.2",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
@ -35,6 +36,7 @@
"@types/bn.js": "^4.11.6", "@types/bn.js": "^4.11.6",
"@types/enzyme": "^3.10.8", "@types/enzyme": "^3.10.8",
"@types/enzyme-adapter-react-16": "^1.0.6", "@types/enzyme-adapter-react-16": "^1.0.6",
"@types/file-saver": "^2.0.2",
"@types/react-copy-to-clipboard": "^5.0.0", "@types/react-copy-to-clipboard": "^5.0.0",
"@types/react-dom": "^17.0.3", "@types/react-dom": "^17.0.3",
"@types/react-router": "^5.1.13", "@types/react-router": "^5.1.13",

View File

@ -10,6 +10,8 @@ import { act } from 'react-dom/test-utils';
import { MemoryRouter, Route } from 'react-router'; import { MemoryRouter, Route } from 'react-router';
import { ThemeProvider } from 'styled-components'; import { ThemeProvider } from 'styled-components';
import { KeyringPair$Json } from '@polkadot/keyring/types';
import { Button, themes } from '../components'; import { Button, themes } from '../components';
import * as messaging from '../messaging'; import * as messaging from '../messaging';
import { flushAllPromises } from '../testHelpers'; import { flushAllPromises } from '../testHelpers';
@ -27,7 +29,7 @@ describe('Export component', () => {
}; };
beforeEach(() => { beforeEach(() => {
jest.spyOn(messaging, 'exportAccount').mockResolvedValue({ exportedJson: '{ "meta": { "name": "account_name" } }' }); jest.spyOn(messaging, 'exportAccount').mockResolvedValue({ exportedJson: { meta: { name: 'account_name' } } as unknown as KeyringPair$Json });
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
wrapper = mount( wrapper = mount(

View File

@ -3,6 +3,7 @@
import type { ThemeProps } from '../types'; import type { ThemeProps } from '../types';
import { saveAs } from 'file-saver';
import React, { useCallback, useContext, useState } from 'react'; import React, { useCallback, useContext, useState } from 'react';
import { RouteComponentProps, withRouter } from 'react-router'; import { RouteComponentProps, withRouter } from 'react-router';
import styled from 'styled-components'; import styled from 'styled-components';
@ -43,12 +44,9 @@ function Export ({ className, match: { params: { address } } }: Props): React.Re
exportAccount(address, pass) exportAccount(address, pass)
.then(({ exportedJson }) => { .then(({ exportedJson }) => {
const element = document.createElement('a'); const blob = new Blob([JSON.stringify(exportedJson)], { type: 'application/json; charset=utf-8' });
const { meta } = JSON.parse(exportedJson) as { meta: { name: string } };
element.href = `data:text/plain;charset=utf-8,${exportedJson}`; saveAs(blob, `${address}.json`);
element.download = `${meta.name}_exported_account_${Date.now()}.json`;
element.click();
onAction('/'); onAction('/');
}) })

View File

@ -3,6 +3,7 @@
import type { ThemeProps } from '../types'; import type { ThemeProps } from '../types';
import { saveAs } from 'file-saver';
import React, { useCallback, useContext, useState } from 'react'; import React, { useCallback, useContext, useState } from 'react';
import { RouteComponentProps, withRouter } from 'react-router'; import { RouteComponentProps, withRouter } from 'react-router';
import styled from 'styled-components'; import styled from 'styled-components';
@ -44,11 +45,9 @@ function ExportAll ({ className }: Props): React.ReactElement<Props> {
exportAccounts(accounts.map((account) => account.address), pass) exportAccounts(accounts.map((account) => account.address), pass)
.then(({ exportedJson }) => { .then(({ exportedJson }) => {
const element = document.createElement('a'); const blob = new Blob([JSON.stringify(exportedJson)], { type: 'application/json; charset=utf-8' });
element.href = `data:text/plain;charset=utf-8,${exportedJson}`; saveAs(blob, `batch_exported_account_${Date.now()}.json`);
element.download = `batch_exported_account_${Date.now()}.json`;
element.click();
onAction('/'); onAction('/');
}) })

View File

@ -79,11 +79,11 @@ export async function tieAccount (address: string, genesisHash: string | null):
return sendMessage('pri(accounts.tie)', { address, genesisHash }); return sendMessage('pri(accounts.tie)', { address, genesisHash });
} }
export async function exportAccount (address: string, password: string): Promise<{ exportedJson: string }> { export async function exportAccount (address: string, password: string): Promise<{ exportedJson: KeyringPair$Json }> {
return sendMessage('pri(accounts.export)', { address, password }); return sendMessage('pri(accounts.export)', { address, password });
} }
export async function exportAccounts (addresses: string[], password: string): Promise<{ exportedJson: string }> { export async function exportAccounts (addresses: string[], password: string): Promise<{ exportedJson: KeyringPairs$Json }> {
return sendMessage('pri(accounts.batchExport)', { addresses, password }); return sendMessage('pri(accounts.batchExport)', { addresses, password });
} }

View File

@ -2150,6 +2150,7 @@ __metadata:
"@types/bn.js": ^4.11.6 "@types/bn.js": ^4.11.6
"@types/enzyme": ^3.10.8 "@types/enzyme": ^3.10.8
"@types/enzyme-adapter-react-16": ^1.0.6 "@types/enzyme-adapter-react-16": ^1.0.6
"@types/file-saver": ^2.0.2
"@types/react-copy-to-clipboard": ^5.0.0 "@types/react-copy-to-clipboard": ^5.0.0
"@types/react-dom": ^17.0.3 "@types/react-dom": ^17.0.3
"@types/react-router": ^5.1.13 "@types/react-router": ^5.1.13
@ -2159,6 +2160,7 @@ __metadata:
"@wojtekmaj/enzyme-adapter-react-17": ^0.6.0 "@wojtekmaj/enzyme-adapter-react-17": ^0.6.0
enzyme: ^3.11.0 enzyme: ^3.11.0
enzyme-adapter-react-16: ^1.15.6 enzyme-adapter-react-16: ^1.15.6
file-saver: ^2.0.5
i18next: ^19.9.2 i18next: ^19.9.2
react: ^17.0.2 react: ^17.0.2
react-dom: ^17.0.2 react-dom: ^17.0.2
@ -2787,6 +2789,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/file-saver@npm:^2.0.2":
version: 2.0.2
resolution: "@types/file-saver@npm:2.0.2"
checksum: ccdfbbe45c68b54192d1a9fb9c5f7594c7eabe64ad55982839f0e758b72a62ae0e2edcee16af2b7c197d53f57047b5e11dac5d7805326718d0cb5a5e2f4dc382
languageName: node
linkType: hard
"@types/filesystem@npm:*": "@types/filesystem@npm:*":
version: 0.0.29 version: 0.0.29
resolution: "@types/filesystem@npm:0.0.29" resolution: "@types/filesystem@npm:0.0.29"
@ -7611,6 +7620,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"file-saver@npm:^2.0.5":
version: 2.0.5
resolution: "file-saver@npm:2.0.5"
checksum: 0f835236534377c9b7591b995d94ae5061166452fb1f784548b706ff48984930e45a7947cfe393ce86b84ef2ec89064120c5d5ab5186146c508bb022c71ced3d
languageName: node
linkType: hard
"file-selector@npm:^0.2.2": "file-selector@npm:^0.2.2":
version: 0.2.4 version: 0.2.4
resolution: "file-selector@npm:0.2.4" resolution: "file-selector@npm:0.2.4"