Skip to content

Commit

Permalink
fix(importDataSources): get error result of error in URL fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulHax committed Oct 25, 2023
1 parent 491a0a7 commit e04a090
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 178 deletions.
5 changes: 3 additions & 2 deletions src/components/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,9 @@ import SaveSession from './SaveSession.vue';
import { useGlobalErrorHook } from '../composables/useGlobalErrorHook';
import { useWebGLWatchdog } from '../composables/useWebGLWatchdog';
import { useAppLoadingNotifications } from '../composables/useAppLoadingNotifications';
import { partition, wrapInArray } from '../utils';
import { wrapInArray } from '../utils';
import { useKeyboardShortcuts } from '../composables/useKeyboardShortcuts';
import { partitionResults } from '../core/pipeline';
async function loadFiles(
sources: DataSource[],
Expand All @@ -290,7 +291,7 @@ async function loadFiles(
return;
}
const [succeeded, errored] = partition((result) => result.ok, results);
const [succeeded, errored] = partitionResults(results);
if (!dataStore.primarySelection && succeeded.length) {
const selection = convertSuccessResultToDataSelection(succeeded[0]);
Expand Down
83 changes: 52 additions & 31 deletions src/core/__tests__/pipeline.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.true;
expect(result.errors).to.have.length(0);
expect(result.data).to.deep.equal([42]);
if (result.ok) {
expect(result.data).to.deep.equal([42]);
}
expect(callOrder).to.deep.equal([1, 2, 3]);
});

Expand All @@ -56,8 +57,9 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.true;
expect(result.errors).to.have.length(0);
expect(result.data).to.have.length(0);
if (result.ok) {
expect(result.data).to.have.length(0);
}
expect(callOrder).to.deep.equal([1, 2, 3]);
});

Expand All @@ -82,7 +84,6 @@ describe('Pipeline', () => {
const result = await pipeline.execute(5);

expect(result.ok).to.be.true;
expect(result.errors).to.have.length(0);
expect(calc).to.equal(8);
});

Expand Down Expand Up @@ -111,7 +112,6 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.true;
expect(result.errors).to.have.length(0);
expect(callOrder).to.deep.equal([1, 2, 3]);
});

Expand All @@ -127,8 +127,9 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.true;
expect(result.errors).to.have.length(0);
expect(result.data).to.deep.equal([]);
if (result.ok) {
expect(result.data).to.deep.equal([]);
}
});

it('should detect double done()', async () => {
Expand All @@ -142,7 +143,9 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.false;
expect(result.errors).to.have.length(1);
if (!result.ok) {
expect(result.errors).to.have.length(1);
}
});

it('should handle top-level errors', async () => {
Expand All @@ -156,8 +159,10 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.false;
expect(result.errors).to.have.length(1);
expect(result.errors[0].message).to.equal(error.message);
if (!result.ok) {
expect(result.errors).to.have.length(1);
expect(result.errors[0].message).to.equal(error.message);
}
});

it('should handle top-level async errors', async () => {
Expand All @@ -172,8 +177,10 @@ describe('Pipeline', () => {
const result = await pipeline.execute();

expect(result.ok).to.be.false;
expect(result.errors).to.have.length(1);
expect(result.errors[0].message).to.equal(error.message);
if (!result.ok) {
expect(result.errors).to.have.length(1);
expect(result.errors[0].message).to.equal(error.message);
}
});

it('should handle nested executions', async () => {
Expand All @@ -186,11 +193,17 @@ describe('Pipeline', () => {
return idx;
},
async (idx, { execute, done }) => {
let fnum = (await execute(idx - 1)).data[0];
if (idx > 1) {
fnum += (await execute(idx - 2)).data[0];
const result = await execute(idx - 1);
if (result.ok) {
let fnum = result.data[0];
if (idx > 1) {
const r = await execute(idx - 2);
if (r.ok) fnum += r.data[0];
else throw new Error('error');
}
return done(fnum);
}
return done(fnum);
throw new Error('error');
},
];

Expand All @@ -199,8 +212,10 @@ describe('Pipeline', () => {
const result = await pipeline.execute(N);

expect(result.ok).to.be.true;
// pick first result data, which is the top-level pipeline result
expect(result.data[0]).to.equal(8);
if (result.ok) {
// pick first result data, which is the top-level pipeline result
expect(result.data[0]).to.equal(8);
}
});

it('should handle allow extra context overriding', async () => {
Expand All @@ -219,7 +234,9 @@ describe('Pipeline', () => {
const result = await pipeline.execute(0, 21);

expect(result.ok).to.be.true;
expect(result.data).to.deep.equal([42]);
if (result.ok) {
expect(result.data).to.deep.equal([42]);
}
});

it('should handle nested async errors', async () => {
Expand Down Expand Up @@ -248,16 +265,20 @@ describe('Pipeline', () => {
const result = await pipeline.execute(N);

expect(result.ok).to.be.false;
// we expect there to be fib(N+1) errors
expect(result.errors).to.have.length(8);

result.errors.forEach((err) => {
const { message, inputDataStackTrace } = err;
expect(message).to.equal(error.message);
// first object should be the input passed to the erroring handler
expect(inputDataStackTrace[0]).to.equal(0);
// last object should be the input passed to the pipeline.
expect(inputDataStackTrace.at(-1)).to.equal(N);
});
if (!result.ok) {
// we expect there to be fib(N+1) errors
expect(result.errors).to.have.length(8);

result.errors.forEach((err) => {
const { message, inputDataStackTrace } = err;
expect(message).to.equal(error.message);
// first object should be the input passed to the erroring handler
expect(inputDataStackTrace[0]).to.equal(0);
// last object should be the input passed to the pipeline.
expect(inputDataStackTrace.at(-1)).to.equal(N);
});
} else {
expect.fail('Expected not ok result');
}
});
});
96 changes: 56 additions & 40 deletions src/core/pipeline.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Awaitable } from '@vueuse/core';
import { Maybe } from '@/src/types';
import { defer, partition } from '../utils';
import { defer, partitionByType } from '../utils';

/**
* Represents a pipeline error.
Expand All @@ -18,6 +18,16 @@ export interface PipelineError<DataType> {
cause: unknown;
}

export interface PipelineResultSuccess<ResultType> {
ok: true;
data: ResultType[];
}

export interface PipelineResultError<DataType> {
ok: false;
errors: PipelineError<DataType>[];
}

/**
* Represents a pipeline's execution result.
*
Expand All @@ -26,11 +36,15 @@ export interface PipelineError<DataType> {
* The errors property holds any errors reported from (potentially nested)
* executions.
*/
export interface PipelineResult<DataType, ResultType> {
ok: boolean;
data: ResultType[];
errors: PipelineError<DataType>[];
}
export type PipelineResult<DataType, ResultType> =
| PipelineResultSuccess<ResultType>
| PipelineResultError<DataType>;

export const partitionResults = <T, U>(arr: Array<PipelineResult<T, U>>) =>
partitionByType(
(r: PipelineResult<T, U>): r is PipelineResultSuccess<U> => r.ok,
arr
);

function createPipelineError<DataType>(
message: string,
Expand Down Expand Up @@ -228,48 +242,50 @@ export default class Pipeline<
invokeHandler(output as DataType, index + 1);
};

const result: PipelineResult<DataType, ResultType> = {
ok: true,
data: [],
errors: [],
};

try {
await invokeHandler(input, 0);
const ret = await execution.promise;
if (ret != null) {
result.data.push(ret);
const result: PipelineResult<DataType, ResultType> = await (async () => {
try {
await invokeHandler(input, 0);
const ret = await execution.promise;
if (ret != null) {
return {
ok: true as const,
data: [ret] as Array<ResultType>,
};
}
return { ok: true as const, data: [] };
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return {
ok: false as const,
errors: [createPipelineError(message, input, err)],
};
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
result.ok = false;
result.errors.push(createPipelineError(message, input, err));
}
})();

const innerResults = await Promise.all(nestedExecutions);
const [succeededInner, failedInner] = partition(
(res) => res.ok,
innerResults
);
const [succeededInner, failedInner] = partitionResults(innerResults);

if (failedInner.length > 0) {
result.ok = false;
}

succeededInner.forEach((okResult) => {
result.data.push(...okResult.data);
});
const errors = failedInner.flatMap((failedResult) => {
const { errors: innerErrors } = failedResult;
// add current input to the input stack trace
innerErrors.forEach((err) => {
err.inputDataStackTrace.push(input);
});
return innerErrors;
});

failedInner.forEach((failedResult) => {
const { errors } = failedResult;
return {
ok: false as const,
errors,
};
}

// add current input to the input stack trace
errors.forEach((err) => {
err.inputDataStackTrace.push(input);
if (result.ok) {
succeededInner.forEach((okResult) => {
result.data.push(...okResult.data);
});

result.errors.push(...errors);
});
}

return result;
}
Expand Down
22 changes: 22 additions & 0 deletions src/io/import/__tests__/importDataSources.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, it } from 'vitest';
import { expect } from 'chai';
import { DataSource } from '../dataSource';
import { importDataSources } from '../importDataSources';

describe('importDataSources', () => {
it('should return error if illegal URI', async () => {
const input: DataSource = {
uriSrc: {
uri: '// asdf asdf',
name: 'image.jpg',
},
};
const output = await importDataSources([input]);

const firstResult = output[0];
expect(firstResult.ok).to.equals(false);
if (!firstResult.ok) {
expect(firstResult.errors.length).to.greaterThan(0);
}
});
});
Loading

0 comments on commit e04a090

Please sign in to comment.