From 8e276d9933638c0194457019d7c85f305ff10760 Mon Sep 17 00:00:00 2001 From: Dexter Edwards Date: Wed, 29 Jun 2022 10:52:00 +0100 Subject: [PATCH] Feat/hosted wallet (#668) * feat: add ability to set custom wallet URL * fix: wallet state not adequately cleared on disconnect * chore: don't bother to set a prop only to overwrite it * fix: use correct URL for error message * chore: remove autofocus as per comment * chore: correct url --- .../connect-dialog/connect-dialog.spec.tsx | 39 +++++++++++++++++-- libs/wallet/src/connectors/rest-connector.ts | 18 ++++++++- libs/wallet/src/provider.tsx | 2 +- libs/wallet/src/rest-connector-form.tsx | 25 ++++++++++-- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx index 5ad9e4f43..a69470cc3 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx @@ -92,7 +92,40 @@ it('Successful connection using rest auth form', async () => { fireEvent.submit(screen.getByTestId('rest-connector-form')); }); - expect(spy).toHaveBeenCalledWith(fields); + expect(spy).toHaveBeenCalledWith('http://localhost:1789/api/v1', fields); + + expect(defaultProps.setDialogOpen).toHaveBeenCalledWith(false); +}); + +it('Successful connection using custom url', async () => { + const spy = jest + .spyOn(defaultProps.connectors['rest'] as RestConnector, 'authenticate') + .mockImplementation(() => Promise.resolve({ success: true, error: null })); + + render(generateJSX({ dialogOpen: true })); + // Switches to rest form + fireEvent.click(screen.getByText('rest provider')); + + // Client side validation + fireEvent.submit(screen.getByTestId('rest-connector-form')); + expect(spy).not.toHaveBeenCalled(); + await waitFor(() => { + expect(screen.getAllByText('Required')).toHaveLength(2); + }); + + // Set custom URL + fireEvent.change(screen.getByLabelText('Url'), { + target: { value: 'localhost:1234' }, + }); + + const fields = fillInForm(); + + // Wait for auth method to be called + await act(async () => { + fireEvent.submit(screen.getByTestId('rest-connector-form')); + }); + + expect(spy).toHaveBeenCalledWith('localhost:1234', fields); expect(defaultProps.setDialogOpen).toHaveBeenCalledWith(false); }); @@ -117,7 +150,7 @@ it('Unsuccessful connection using rest auth form', async () => { fireEvent.submit(screen.getByTestId('rest-connector-form')); }); - expect(spy).toHaveBeenCalledWith(fields); + expect(spy).toHaveBeenCalledWith('http://localhost:1789/api/v1', fields); expect(screen.getByTestId('form-error')).toHaveTextContent( 'Something went wrong' @@ -135,7 +168,7 @@ it('Unsuccessful connection using rest auth form', async () => { }); expect(screen.getByTestId('form-error')).toHaveTextContent( - 'Wallet not running at http://localhost:1789' + 'Wallet not running at http://localhost:1789/api/v1' ); // Reject eg non 200 results diff --git a/libs/wallet/src/connectors/rest-connector.ts b/libs/wallet/src/connectors/rest-connector.ts index 27f5232db..158444be0 100644 --- a/libs/wallet/src/connectors/rest-connector.ts +++ b/libs/wallet/src/connectors/rest-connector.ts @@ -1,6 +1,7 @@ import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client'; import { createConfiguration, + ServerConfiguration, DefaultApi, } from '@vegaprotocol/vegawallet-service-api-client'; import { LocalStorage } from '@vegaprotocol/react-helpers'; @@ -39,13 +40,26 @@ export class RestConnector implements VegaConnector { this.service = new DefaultApi(this.apiConfig); } - async authenticate(params: { wallet: string; passphrase: string }) { + async authenticate( + url: string, + params: { + wallet: string; + passphrase: string; + } + ) { try { - const res = await this.service.authTokenPost(params); + const service = new DefaultApi( + createConfiguration({ + baseServer: new ServerConfiguration>(url, {}), + }) + ); + + const res = await service.authTokenPost(params); // Renew service instance with default bearer authMethod now that we have the token this.service = new DefaultApi( createConfiguration({ + baseServer: new ServerConfiguration>(url, {}), authMethods: { bearer: `Bearer ${res.token}`, }, diff --git a/libs/wallet/src/provider.tsx b/libs/wallet/src/provider.tsx index f4947563d..640a79e61 100644 --- a/libs/wallet/src/provider.tsx +++ b/libs/wallet/src/provider.tsx @@ -43,7 +43,6 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => { }); setKeypairs(publicKeysWithName); - if (publicKey === null) { setPublicKey(publicKeysWithName[0].pub); } @@ -60,6 +59,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => { try { await connector.current?.disconnect(); setKeypairs(null); + setPublicKey(null); connector.current = null; LocalStorage.removeItem(WALLET_KEY); return true; diff --git a/libs/wallet/src/rest-connector-form.tsx b/libs/wallet/src/rest-connector-form.tsx index 37005a71e..9533f0d87 100644 --- a/libs/wallet/src/rest-connector-form.tsx +++ b/libs/wallet/src/rest-connector-form.tsx @@ -5,6 +5,7 @@ import { useForm } from 'react-hook-form'; import type { RestConnector } from '.'; interface FormFields { + url: string; wallet: string; passphrase: string; } @@ -14,6 +15,8 @@ interface RestConnectorFormProps { onAuthenticate: () => void; } +const VEGA_DEFAULT_URL = 'http://localhost:1789/api/v1'; + export function RestConnectorForm({ connector, onAuthenticate, @@ -24,12 +27,16 @@ export function RestConnectorForm({ register, handleSubmit, formState: { errors }, - } = useForm(); + } = useForm({ + defaultValues: { + url: VEGA_DEFAULT_URL, + }, + }); async function onSubmit(fields: FormFields) { try { setError(''); - const res = await connector.authenticate({ + const res = await connector.authenticate(fields.url, { wallet: fields.wallet, passphrase: fields.passphrase, }); @@ -41,7 +48,7 @@ export function RestConnectorForm({ } } catch (err) { if (err instanceof TypeError) { - setError(t('Wallet not running at http://localhost:1789')); + setError(t(`Wallet not running at ${fields.url}`)); } else if (err instanceof Error) { setError(t('Authentication failed')); } else { @@ -52,6 +59,18 @@ export function RestConnectorForm({ return (
+ + + {errors.url?.message && ( + + {errors.url.message} + + )} +