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'));
});
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

View File

@ -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<Record<string, never>>(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<Record<string, never>>(url, {}),
authMethods: {
bearer: `Bearer ${res.token}`,
},

View File

@ -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;

View File

@ -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<FormFields>();
} = useForm<FormFields>({
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 (
<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">
<Input
{...register('wallet', { required: t('Required') })}