Skip to content

Commit a2aff9c

Browse files
authored
feat: expose error (#72)
1 parent b2bef6c commit a2aff9c

File tree

6 files changed

+200
-11
lines changed

6 files changed

+200
-11
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"devDependencies": {
3535
"@feathersjs/adapter-commons": "5.0.0-pre.15",
3636
"@feathersjs/feathers": "5.0.0-pre.15",
37+
"@feathersjs/errors": "5.0.0-pre.15",
3738
"@geprog/eslint-config": "1.0.2",
3839
"@geprog/semantic-release-config": "1.0.0",
3940
"@jest/types": "27.4.2",

pnpm-lock.yaml

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/useFind.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { FeathersError } from '@feathersjs/errors';
12
import type { Application, FeathersService, Params, ServiceMethods } from '@feathersjs/feathers';
23
import sift from 'sift';
34
import { getCurrentInstance, onBeforeUnmount, Ref, ref, watch } from 'vue';
@@ -73,6 +74,7 @@ function loadServiceEventHandlers<
7374
export type UseFind<T> = {
7475
data: Ref<T[]>;
7576
isLoading: Ref<boolean>;
77+
error: Ref<FeathersError | undefined>;
7678
unload: () => void;
7779
};
7880

@@ -94,22 +96,30 @@ export default <CustomApplication extends Application>(feathers: CustomApplicati
9496
// type cast is fine here (source: https://github.com/vuejs/vue-next/issues/2136#issuecomment-693524663)
9597
const data = ref<M[]>([]) as Ref<M[]>;
9698
const isLoading = ref(false);
99+
const error = ref<FeathersError>();
97100

98101
const service = feathers.service(serviceName as string);
99102
const unloadEventHandlers = loadServiceEventHandlers(service, params, data);
100103

101104
const find = async () => {
102105
isLoading.value = true;
106+
error.value = undefined;
107+
103108
if (!params.value) {
104109
data.value = [];
105110
isLoading.value = false;
106111
return;
107112
}
108113

109-
// TODO: the typecast below is necessary due to the prerelease state of feathers v5. The problem there is
110-
// that the AdapterService interface is not yet updated and is not compatible with the ServiceMethods interface.
111-
const res = await (service as unknown as ServiceMethods<M>).find(params.value);
112-
data.value = Array.isArray(res) ? res : [res];
114+
try {
115+
// TODO: the typecast below is necessary due to the prerelease state of feathers v5. The problem there is
116+
// that the AdapterService interface is not yet updated and is not compatible with the ServiceMethods interface.
117+
const res = await (service as unknown as ServiceMethods<M>).find(params.value);
118+
data.value = Array.isArray(res) ? res : [res];
119+
} catch (_error) {
120+
error.value = _error as FeathersError;
121+
}
122+
113123
isLoading.value = false;
114124
};
115125

@@ -129,5 +139,5 @@ export default <CustomApplication extends Application>(feathers: CustomApplicati
129139
onBeforeUnmount(unload);
130140
}
131141

132-
return { data, isLoading, unload };
142+
return { data, isLoading, unload, error };
133143
};

src/useGet.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { FeathersError } from '@feathersjs/errors';
12
import type { Application, FeathersService, Id, Params, ServiceMethods } from '@feathersjs/feathers';
23
import { getCurrentInstance, onBeforeUnmount, Ref, ref, watch } from 'vue';
34

@@ -56,6 +57,7 @@ function loadServiceEventHandlers<
5657
export type UseGet<T> = {
5758
data: Ref<T | undefined>;
5859
isLoading: Ref<boolean>;
60+
error: Ref<FeathersError | undefined>;
5961
unload: () => void;
6062
};
6163

@@ -77,21 +79,30 @@ export default <CustomApplication extends Application>(feathers: CustomApplicati
7779
): UseGet<M> => {
7880
const data = ref<M>();
7981
const isLoading = ref(false);
82+
const error = ref<FeathersError>();
8083

8184
const service = feathers.service(serviceName as string);
8285

8386
const unloadEventHandlers = loadServiceEventHandlers(service, _id, data);
8487

8588
const get = async () => {
8689
isLoading.value = true;
90+
error.value = undefined;
91+
8792
if (!_id.value) {
8893
data.value = undefined;
8994
isLoading.value = false;
9095
return;
9196
}
92-
// TODO: the typecast below is necessary due to the prerelease state of feathers v5. The problem there is
93-
// that the AdapterService interface is not yet updated and is not compatible with the ServiceMethods interface.
94-
data.value = await (service as unknown as ServiceMethods<M>).get(_id.value, params.value);
97+
98+
try {
99+
// TODO: the typecast below is necessary due to the prerelease state of feathers v5. The problem there is
100+
// that the AdapterService interface is not yet updated and is not compatible with the ServiceMethods interface.
101+
data.value = await (service as unknown as ServiceMethods<M>).get(_id.value, params.value);
102+
} catch (_error) {
103+
error.value = _error as FeathersError;
104+
}
105+
95106
isLoading.value = false;
96107
};
97108

@@ -111,5 +122,5 @@ export default <CustomApplication extends Application>(feathers: CustomApplicati
111122
onBeforeUnmount(unload);
112123
}
113124

114-
return { isLoading, data, unload };
125+
return { isLoading, data, error, unload };
115126
};

test/useFind.test.ts

+81-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { FeathersError, GeneralError } from '@feathersjs/errors';
12
import type { Application, Params } from '@feathersjs/feathers';
23
import { nextTick, ref } from 'vue';
34

@@ -107,7 +108,7 @@ describe('Find composition', () => {
107108
});
108109

109110
it('should indicate data finished loading', async () => {
110-
expect.assertions(2);
111+
expect.assertions(3);
111112

112113
// given
113114
let serviceFindPromiseResolve: (value: TestModel[] | PromiseLike<TestModel[]>) => void = jest.fn();
@@ -132,12 +133,53 @@ describe('Find composition', () => {
132133
findComposition = useFind('testModels');
133134
});
134135

136+
// before when
137+
expect(findComposition).toBeTruthy();
138+
expect(findComposition && findComposition.isLoading.value).toBeTruthy();
139+
135140
// when
136141
serviceFindPromiseResolve(testModels);
137142
await nextTick();
138143

139144
// then
145+
expect(findComposition && findComposition.isLoading.value).toBeFalsy();
146+
});
147+
148+
it('should indicate data finished loading even if an error occurred', async () => {
149+
expect.assertions(3);
150+
151+
// given
152+
let serviceFindPromiseReject: (reason: FeathersError) => void = jest.fn();
153+
const serviceFind = jest.fn(
154+
() =>
155+
new Promise<TestModel[]>((resolve, reject) => {
156+
serviceFindPromiseReject = reject;
157+
}),
158+
);
159+
const feathersMock = {
160+
service: () => ({
161+
find: serviceFind,
162+
on: jest.fn(),
163+
off: jest.fn(),
164+
}),
165+
on: jest.fn(),
166+
off: jest.fn(),
167+
} as unknown as Application;
168+
const useFind = useFindOriginal(feathersMock);
169+
let findComposition = null as UseFind<TestModel> | null;
170+
mountComposition(() => {
171+
findComposition = useFind('testModels');
172+
});
173+
174+
// before when
140175
expect(findComposition).toBeTruthy();
176+
expect(findComposition && findComposition.isLoading.value).toBeTruthy();
177+
178+
// when
179+
serviceFindPromiseReject(new GeneralError('test error'));
180+
await nextTick();
181+
182+
// then
141183
expect(findComposition && findComposition.isLoading.value).toBeFalsy();
142184
});
143185

@@ -341,6 +383,44 @@ describe('Find composition', () => {
341383
expect(findComposition && findComposition.data.value).toStrictEqual([additionalTestModel]);
342384
});
343385

386+
it('should set an error if something failed', async () => {
387+
expect.assertions(3);
388+
389+
// given
390+
let serviceFindPromiseReject: (reason: FeathersError) => void = jest.fn();
391+
const serviceFind = jest.fn(
392+
() =>
393+
new Promise<TestModel[]>((resolve, reject) => {
394+
serviceFindPromiseReject = reject;
395+
}),
396+
);
397+
const feathersMock = {
398+
service: () => ({
399+
find: serviceFind,
400+
on: jest.fn(),
401+
off: jest.fn(),
402+
}),
403+
on: jest.fn(),
404+
off: jest.fn(),
405+
} as unknown as Application;
406+
const useFind = useFindOriginal(feathersMock);
407+
let findComposition = null as UseFind<TestModel> | null;
408+
mountComposition(() => {
409+
findComposition = useFind('testModels');
410+
});
411+
412+
// before when
413+
expect(findComposition).toBeTruthy();
414+
expect(findComposition && findComposition.error.value).toBeFalsy();
415+
416+
// when
417+
serviceFindPromiseReject(new GeneralError('test error'));
418+
await nextTick();
419+
420+
// then
421+
expect(findComposition && findComposition.error.value).toBeTruthy();
422+
});
423+
344424
describe('Event Handlers', () => {
345425
it('should listen to "create" events', () => {
346426
expect.assertions(2);

test/useGet.test.ts

+81-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { FeathersError, GeneralError } from '@feathersjs/errors';
12
import type { Application } from '@feathersjs/feathers';
23
import { nextTick, ref } from 'vue';
34

@@ -104,7 +105,7 @@ describe('Get composition', () => {
104105
});
105106

106107
it('should indicate data finished loading', async () => {
107-
expect.assertions(2);
108+
expect.assertions(3);
108109

109110
// given
110111
let serviceGetPromiseResolve: (value: TestModel | PromiseLike<TestModel>) => void = jest.fn();
@@ -129,12 +130,53 @@ describe('Get composition', () => {
129130
getComposition = useGet('testModels', ref(testModel._id));
130131
});
131132

133+
// before when
134+
expect(getComposition).toBeTruthy();
135+
expect(getComposition && getComposition.isLoading.value).toBeTruthy();
136+
132137
// when
133138
serviceGetPromiseResolve(testModel);
134139
await nextTick();
135140

136141
// then
142+
expect(getComposition && getComposition.isLoading.value).toBeFalsy();
143+
});
144+
145+
it('should indicate data finished loading even if an error occurred', async () => {
146+
expect.assertions(3);
147+
148+
// given
149+
let serviceGetPromiseReject: (reason: FeathersError) => void = jest.fn();
150+
const serviceGet = jest.fn(
151+
() =>
152+
new Promise<TestModel>((resolve, reject) => {
153+
serviceGetPromiseReject = reject;
154+
}),
155+
);
156+
const feathersMock = {
157+
service: () => ({
158+
get: serviceGet,
159+
on: jest.fn(),
160+
off: jest.fn(),
161+
}),
162+
on: jest.fn(),
163+
off: jest.fn(),
164+
} as unknown as Application;
165+
const useGet = useGetOriginal(feathersMock);
166+
let getComposition = null as UseGet<TestModel> | null;
167+
mountComposition(() => {
168+
getComposition = useGet('testModels', ref(testModel._id));
169+
});
170+
171+
// before when
137172
expect(getComposition).toBeTruthy();
173+
expect(getComposition && getComposition.isLoading.value).toBeTruthy();
174+
175+
// when
176+
serviceGetPromiseReject(new GeneralError('test error'));
177+
await nextTick();
178+
179+
// then
138180
expect(getComposition && getComposition.isLoading.value).toBeFalsy();
139181
});
140182

@@ -260,6 +302,44 @@ describe('Get composition', () => {
260302
expect(getComposition && getComposition.data.value).toStrictEqual(additionalTestModel);
261303
});
262304

305+
it('should set an error if something failed', async () => {
306+
expect.assertions(3);
307+
308+
// given
309+
let serviceGetPromiseReject: (reason: FeathersError) => void = jest.fn();
310+
const serviceGet = jest.fn(
311+
() =>
312+
new Promise<TestModel>((resolve, reject) => {
313+
serviceGetPromiseReject = reject;
314+
}),
315+
);
316+
const feathersMock = {
317+
service: () => ({
318+
get: serviceGet,
319+
on: jest.fn(),
320+
off: jest.fn(),
321+
}),
322+
on: jest.fn(),
323+
off: jest.fn(),
324+
} as unknown as Application;
325+
const useGet = useGetOriginal(feathersMock);
326+
let getComposition = null as UseGet<TestModel> | null;
327+
mountComposition(() => {
328+
getComposition = useGet('testModels', ref(testModel._id));
329+
});
330+
331+
// before when
332+
expect(getComposition).toBeTruthy();
333+
expect(getComposition && getComposition.error.value).toBeFalsy();
334+
335+
// when
336+
serviceGetPromiseReject(new GeneralError('test error'));
337+
await nextTick();
338+
339+
// then
340+
expect(getComposition && getComposition.error.value).toBeTruthy();
341+
});
342+
263343
describe('Event Handlers', () => {
264344
it('should listen to "create" events', async () => {
265345
expect.assertions(3);

0 commit comments

Comments
 (0)