Skip to content

Commit 9b80dac

Browse files
authored
Check item exists before filtering in default find (#794)
* failing unit test * check item exists before filtering * improve test
1 parent af3bf56 commit 9b80dac

File tree

2 files changed

+89
-11
lines changed

2 files changed

+89
-11
lines changed

src/core/middleware/resources.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -346,16 +346,25 @@ export function defaultFilter(query: ResourceQuery<any>, item: any, type: string
346346
}
347347

348348
export function defaultFind(request: ResourceFindRequest<any>, { put, get }: ResourceControls<any>) {
349-
const { start, type, options } = request;
349+
const { type, options } = request;
350350
const { query } = options;
351351
const { data } = get({ query });
352-
for (let i = start; i < data.length; i++) {
352+
let found: ResourceFindResponse<any> | undefined;
353+
for (let i = 0; i < data.length; i++) {
353354
const item = data[i];
354-
if (defaultFilter(request.query, item, type)) {
355-
put({ item, index: i }, request);
356-
break;
355+
if (item && defaultFilter(request.query, item, type)) {
356+
if (!found || i >= request.start) {
357+
found = {
358+
item,
359+
index: i
360+
};
361+
if (i >= request.start) {
362+
break;
363+
}
364+
}
357365
}
358366
}
367+
put(found, request);
359368
}
360369

361370
export const memoryTemplate: ResourceTemplateWithInit<any, { data: any }> = Object.freeze({
@@ -366,19 +375,21 @@ export const memoryTemplate: ResourceTemplateWithInit<any, { data: any }> = Obje
366375
const { data } = get();
367376
const { offset, size } = request;
368377
const filteredData = Object.keys(request.query).length
369-
? data.filter((i) => defaultFilter(request.query, i, 'contains'))
378+
? data.filter((item) => item && defaultFilter(request.query, item, 'contains'))
370379
: data;
371380
put({ data: filteredData.slice(offset, offset + size), total: filteredData.length }, request);
372381
},
373382
find: (request, { get, put }) => {
374383
const { type, options } = request;
375384
const { query } = options;
376-
const { data } = get();
377-
const filteredData = data.filter((item) => defaultFilter(query, item));
385+
let { data } = get({ query });
386+
if (!data.length) {
387+
data = get().data.filter((item) => defaultFilter(query, item));
388+
}
378389
let found: ResourceFindResponse<any> | undefined;
379-
for (let i = 0; i < filteredData.length; i++) {
380-
const item = filteredData[i];
381-
if (defaultFilter(request.query, item, type)) {
390+
for (let i = 0; i < data.length; i++) {
391+
const item = data[i];
392+
if (item && defaultFilter(request.query, item, type)) {
382393
if (!found || i >= request.start) {
383394
found = {
384395
item,

tests/core/unit/middleware/resources.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,73 @@ describe('Resources Middleware', () => {
12181218
'<div><div>{"item":{"value":"world"},"page":1,"index":10,"pageIndex":10}</div></div>'
12191219
);
12201220
});
1221+
1222+
it('should only try and filter against known items', () => {
1223+
const data: any = {};
1224+
for (let i = 0; i < 200; i++) {
1225+
const page = Math.floor(i / 30) + 1;
1226+
const item = { value: `Item ${i}` };
1227+
const pageData = data[page] || [];
1228+
pageData.push(item);
1229+
data[page] = pageData;
1230+
}
1231+
const template = createResourceTemplate<{ value: string }>({
1232+
find: defaultFind,
1233+
read: (request, { put }) => {
1234+
const { offset, size } = request;
1235+
const page = Math.floor(offset / size) + 1;
1236+
const pageData = data[page];
1237+
put({ data: pageData, total: 200 }, request);
1238+
}
1239+
});
1240+
const factory = create({ icache, resource: createResourceMiddleware<{ value: string }>() });
1241+
1242+
const Widget = factory(({ id, properties, middleware: { icache, resource } }) => {
1243+
const searchTerm = icache.getOrSet('search', 'Item 65');
1244+
const { createOptions, find, getOrRead } = resource;
1245+
const {
1246+
resource: { template, options = createOptions(id) }
1247+
} = properties();
1248+
getOrRead(template, options({ page: 1 }));
1249+
getOrRead(template, options({ page: 2 }));
1250+
getOrRead(template, options({ page: 3 }));
1251+
const item = find(template, {
1252+
options: options(),
1253+
start: 60,
1254+
type: 'exact',
1255+
query: { value: searchTerm }
1256+
});
1257+
return (
1258+
<div>
1259+
<button
1260+
onclick={() => {
1261+
icache.set('search', 'Item 1');
1262+
}}
1263+
/>
1264+
<div>{JSON.stringify(item)}</div>
1265+
</div>
1266+
);
1267+
});
1268+
1269+
const App = create({ resource: createResourceMiddleware() })(({ middleware: { resource } }) => {
1270+
return <Widget resource={resource({ template })} />;
1271+
});
1272+
1273+
const r = renderer(() => <App />);
1274+
const root = document.createElement('div');
1275+
r.mount({ domNode: root });
1276+
assert.strictEqual(
1277+
root.innerHTML,
1278+
'<div><button></button><div>{"item":{"value":"Item 65"},"index":65,"page":3,"pageIndex":5}</div></div>'
1279+
);
1280+
(root.children[0].children[0] as any).click();
1281+
resolvers.resolveRAF();
1282+
assert.strictEqual(
1283+
root.innerHTML,
1284+
'<div><button></button><div>{"item":{"value":"Item 1"},"index":1,"page":1,"pageIndex":1}</div></div>'
1285+
);
1286+
});
1287+
12211288
describe('contains (default)', () => {
12221289
it('Should find the first matching item', () => {
12231290
const root = document.createElement('div');

0 commit comments

Comments
 (0)