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
This commit is contained in:
Dexter Edwards 2022-06-29 10:52:00 +01:00 committed by GitHub
parent 0473412487
commit 8e276d9933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 9 deletions

View File

@ -92,7 +92,40 @@ it('Successful connection using rest auth form', async () => {
fireEvent.submit(screen.getByTestId('rest-connector-form')); 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); expect(defaultProps.setDialogOpen).toHaveBeenCalledWith(false);
}); });
@ -117,7 +150,7 @@ it('Unsuccessful connection using rest auth form', async () => {
fireEvent.submit(screen.getByTestId('rest-connector-form')); 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( expect(screen.getByTestId('form-error')).toHaveTextContent(
'Something went wrong' 'Something went wrong'
@ -135,7 +168,7 @@ it('Unsuccessful connection using rest auth form', async () => {
}); });
expect(screen.getByTestId('form-error')).toHaveTextContent( 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 // Reject eg non 200 results

View File

@ -1,6 +1,7 @@
import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client'; import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client';
import { import {
createConfiguration, createConfiguration,
ServerConfiguration,
DefaultApi, DefaultApi,
} from '@vegaprotocol/vegawallet-service-api-client'; } from '@vegaprotocol/vegawallet-service-api-client';
import { LocalStorage } from '@vegaprotocol/react-helpers'; import { LocalStorage } from '@vegaprotocol/react-helpers';
@ -39,13 +40,26 @@ export class RestConnector implements VegaConnector {
this.service = new DefaultApi(this.apiConfig); this.service = new DefaultApi(this.apiConfig);
} }
async authenticate(params: { wallet: string; passphrase: string }) { async authenticate(
url: string,
params: {
wallet: string;
passphrase: string;
}
) {
try { try {
const res = await this.service.authTokenPost(params); const service = new DefaultApi(
createConfiguration({
baseServer: new ServerConfiguration<Record<string, never>>(url, {}),
})
);
const res = await service.authTokenPost(params);
// Renew service instance with default bearer authMethod now that we have the token // Renew service instance with default bearer authMethod now that we have the token
this.service = new DefaultApi( this.service = new DefaultApi(
createConfiguration({ createConfiguration({
baseServer: new ServerConfiguration<Record<string, never>>(url, {}),
authMethods: { authMethods: {
bearer: `Bearer ${res.token}`, bearer: `Bearer ${res.token}`,
}, },

View File

@ -43,7 +43,6 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
}); });
setKeypairs(publicKeysWithName); setKeypairs(publicKeysWithName);
if (publicKey === null) { if (publicKey === null) {
setPublicKey(publicKeysWithName[0].pub); setPublicKey(publicKeysWithName[0].pub);
} }
@ -60,6 +59,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
try { try {
await connector.current?.disconnect(); await connector.current?.disconnect();
setKeypairs(null); setKeypairs(null);
setPublicKey(null);
connector.current = null; connector.current = null;
LocalStorage.removeItem(WALLET_KEY); LocalStorage.removeItem(WALLET_KEY);
return true; return true;

View File

@ -5,6 +5,7 @@ import { useForm } from 'react-hook-form';
import type { RestConnector } from '.'; import type { RestConnector } from '.';
interface FormFields { interface FormFields {
url: string;
wallet: string; wallet: string;
passphrase: string; passphrase: string;
} }
@ -14,6 +15,8 @@ interface RestConnectorFormProps {
onAuthenticate: () => void; onAuthenticate: () => void;
} }
const VEGA_DEFAULT_URL = 'http://localhost:1789/api/v1';
export function RestConnectorForm({ export function RestConnectorForm({
connector, connector,
onAuthenticate, onAuthenticate,
@ -24,12 +27,16 @@ export function RestConnectorForm({
register, register,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
} = useForm<FormFields>(); } = useForm<FormFields>({
defaultValues: {
url: VEGA_DEFAULT_URL,
},
});
async function onSubmit(fields: FormFields) { async function onSubmit(fields: FormFields) {
try { try {
setError(''); setError('');
const res = await connector.authenticate({ const res = await connector.authenticate(fields.url, {
wallet: fields.wallet, wallet: fields.wallet,
passphrase: fields.passphrase, passphrase: fields.passphrase,
}); });
@ -41,7 +48,7 @@ export function RestConnectorForm({
} }
} catch (err) { } catch (err) {
if (err instanceof TypeError) { 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) { } else if (err instanceof Error) {
setError(t('Authentication failed')); setError(t('Authentication failed'));
} else { } else {
@ -52,6 +59,18 @@ export function RestConnectorForm({
return ( return (
<form onSubmit={handleSubmit(onSubmit)} data-testid="rest-connector-form"> <form onSubmit={handleSubmit(onSubmit)} data-testid="rest-connector-form">
<FormGroup label={t('Url')} labelFor="url">
<Input
{...register('url', { required: t('Required') })}
id="url"
type="text"
/>
{errors.url?.message && (
<InputError intent="danger" className="mt-4">
{errors.url.message}
</InputError>
)}
</FormGroup>
<FormGroup label={t('Wallet')} labelFor="wallet"> <FormGroup label={t('Wallet')} labelFor="wallet">
<Input <Input
{...register('wallet', { required: t('Required') })} {...register('wallet', { required: t('Required') })}