mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 09:36:27 +00:00
add proper error handling for fail to fetch
This commit is contained in:
@@ -53,6 +53,8 @@ export enum FailToFinishReasonType {
|
||||
NoFirmware = 'flashFirmware.failToFinish.reason.noFirmware',
|
||||
/** The provided firmware.zip does not match the connected hub. */
|
||||
DeviceMismatch = 'flashFirmware.failToFinish.reason.deviceMismatch',
|
||||
/** Failed to fetch firmware from the server. */
|
||||
FailedToFetch = 'flashFirmware.failToFinish.reason.failedToFetch',
|
||||
/** There was a problem with the zip file. */
|
||||
ZipError = 'flashFirmware.failToFinish.reason.zipError',
|
||||
/** Metadata property is missing or invalid. */
|
||||
@@ -87,6 +89,10 @@ export type FailToFinishReasonNoFirmware = Reason<FailToFinishReasonType.NoFirmw
|
||||
|
||||
export type FailToFinishReasonDeviceMismatch = Reason<FailToFinishReasonType.DeviceMismatch>;
|
||||
|
||||
export type FailToFinishReasonFailedToFetch = Reason<FailToFinishReasonType.FailedToFetch> & {
|
||||
response: Response;
|
||||
};
|
||||
|
||||
export type FailToFinishReasonZipError = Reason<FailToFinishReasonType.ZipError> & {
|
||||
err: FirmwareReaderError;
|
||||
};
|
||||
@@ -112,6 +118,7 @@ export type FailToFinishReason =
|
||||
| FailToFinishReasonHubError
|
||||
| FailToFinishReasonNoFirmware
|
||||
| FailToFinishReasonDeviceMismatch
|
||||
| FailToFinishReasonFailedToFetch
|
||||
| FailToFinishReasonZipError
|
||||
| FailToFinishReasonBadMetadata
|
||||
| FailToFinishReasonFirmwareSize
|
||||
@@ -183,6 +190,11 @@ export function didFailToFinish(
|
||||
hubError: HubError,
|
||||
): FlashFirmwareDidFailToFinishAction;
|
||||
|
||||
export function didFailToFinish(
|
||||
reason: FailToFinishReasonType.FailedToFetch,
|
||||
response: Response,
|
||||
): FlashFirmwareDidFailToFinishAction;
|
||||
|
||||
export function didFailToFinish(
|
||||
reason: FailToFinishReasonType.ZipError,
|
||||
err: FirmwareReaderError,
|
||||
@@ -204,6 +216,7 @@ export function didFailToFinish(
|
||||
FailToFinishReasonType,
|
||||
| FailToFinishReasonType.BleError
|
||||
| FailToFinishReasonType.HubError
|
||||
| FailToFinishReasonType.FailedToFetch
|
||||
| FailToFinishReasonType.ZipError
|
||||
| FailToFinishReasonType.BadMetadata
|
||||
| FailToFinishReasonType.Unknown
|
||||
@@ -216,7 +229,7 @@ export function didFailToFinish(
|
||||
*/
|
||||
export function didFailToFinish(
|
||||
reason: FailToFinishReasonType,
|
||||
arg1?: string | HubError | Error,
|
||||
arg1?: string | HubError | Error | Response,
|
||||
arg2?: MetadataProblem,
|
||||
): FlashFirmwareDidFailToFinishAction {
|
||||
if (reason === FailToFinishReasonType.BleError) {
|
||||
@@ -241,6 +254,17 @@ export function didFailToFinish(
|
||||
};
|
||||
}
|
||||
|
||||
if (reason === FailToFinishReasonType.FailedToFetch) {
|
||||
// istanbul ignore if: programmer error give wrong arg
|
||||
if (!(arg1 instanceof Response)) {
|
||||
throw new Error('missing or invalid response');
|
||||
}
|
||||
return {
|
||||
type: FlashFirmwareActionType.DidFailToFinish,
|
||||
reason: { reason, response: arg1 },
|
||||
};
|
||||
}
|
||||
|
||||
if (reason === FailToFinishReasonType.ZipError) {
|
||||
// istanbul ignore if: programmer error give wrong arg
|
||||
if (!(arg1 instanceof FirmwareReaderError)) {
|
||||
|
||||
@@ -541,6 +541,77 @@ describe('flashFirmware', () => {
|
||||
await saga.end();
|
||||
});
|
||||
|
||||
test('failed to fetch firmware', async () => {
|
||||
const metadata: FirmwareMetadata = {
|
||||
'metadata-version': '1.0.0',
|
||||
'device-id': HubType.MoveHub,
|
||||
'checksum-type': 'sum',
|
||||
'firmware-version': '1.2.3',
|
||||
'max-firmware-size': 1024,
|
||||
'mpy-abi-version': 5,
|
||||
'mpy-cross-options': ['-mno-unicode'],
|
||||
'user-mpy-offset': 100,
|
||||
};
|
||||
|
||||
const zip = new JSZip();
|
||||
zip.file('firmware-base.bin', new Uint8Array(64));
|
||||
zip.file('firmware.metadata.json', JSON.stringify(metadata));
|
||||
zip.file('main.py', 'print("test")');
|
||||
zip.file('ReadMe_OSS.txt', 'test');
|
||||
|
||||
const response = new Response(undefined, { status: 404 });
|
||||
jest.spyOn(window, 'fetch').mockResolvedValueOnce(response);
|
||||
|
||||
const saga = new AsyncSaga(
|
||||
flashFirmware,
|
||||
{
|
||||
bootloader: { connection: BootloaderConnectionState.Disconnected },
|
||||
settings: { flashCurrentProgram: false },
|
||||
},
|
||||
{
|
||||
nextMessageId: createCountFunc(),
|
||||
},
|
||||
);
|
||||
|
||||
// saga is triggered by this action
|
||||
|
||||
saga.put(flashFirmwareAction());
|
||||
|
||||
// first step is to connect to the hub bootloader
|
||||
|
||||
let action = await saga.take();
|
||||
expect(action).toEqual(connect());
|
||||
|
||||
saga.updateState({
|
||||
bootloader: { connection: BootloaderConnectionState.Connected },
|
||||
});
|
||||
saga.put(didConnect());
|
||||
|
||||
// then find out what kind of hub it is
|
||||
|
||||
action = await saga.take();
|
||||
expect(action).toEqual(infoRequest(0));
|
||||
|
||||
// received an unknown hub type ID
|
||||
|
||||
saga.put(didRequest(0));
|
||||
saga.put(infoResponse(0x01000000, 0x08005000, 0x081f800, HubType.MoveHub));
|
||||
|
||||
// should raise an error that we don't have any firmware for this hub
|
||||
|
||||
action = await saga.take();
|
||||
expect(action).toStrictEqual(
|
||||
didFailToFinish(FailToFinishReasonType.FailedToFetch, response),
|
||||
);
|
||||
|
||||
// should request to disconnect after failure
|
||||
|
||||
action = await saga.take();
|
||||
expect(action).toEqual(disconnect());
|
||||
|
||||
await saga.end();
|
||||
});
|
||||
|
||||
test('connected device does not match firmware device', async () => {
|
||||
const metadata: FirmwareMetadata = {
|
||||
'metadata-version': '1.0.0',
|
||||
|
||||
@@ -47,7 +47,6 @@ import {
|
||||
checksumRequest,
|
||||
connect,
|
||||
disconnect,
|
||||
disconnectRequest,
|
||||
eraseRequest,
|
||||
infoRequest,
|
||||
initRequest,
|
||||
@@ -60,7 +59,6 @@ import {
|
||||
MpyDidFailToCompileAction,
|
||||
compile,
|
||||
} from '../actions/mpy';
|
||||
import * as notification from '../actions/notification';
|
||||
import { MaxProgramFlashSize, Result } from '../protocols/lwp3-bootloader';
|
||||
import { RootState } from '../reducers';
|
||||
import { BootloaderConnectionState } from '../reducers/bootloader';
|
||||
@@ -322,10 +320,10 @@ function* flashFirmware(action: FlashFirmwareFlashAction): Generator {
|
||||
|
||||
const response = yield* call(() => fetch(firmwarePath));
|
||||
if (!response.ok) {
|
||||
yield* put(notification.add('error', 'Failed to fetch firmware.'));
|
||||
const disconnectAction = yield* put(disconnectRequest(nextMessageId()));
|
||||
yield* waitForDidRequest(disconnectAction.id);
|
||||
return;
|
||||
yield* put(
|
||||
didFailToFinish(FailToFinishReasonType.FailedToFetch, response),
|
||||
);
|
||||
yield* disconnectAndCancel();
|
||||
}
|
||||
|
||||
const data = yield* call(() => response.arrayBuffer());
|
||||
|
||||
Reference in New Issue
Block a user