Skip to content

Commit 93038c2

Browse files
committed
Report a reduced page size again, now that nothing asks for an impossible one
Filtering the page size out of the self link comparison treated the symptom: the frontend went on asking for 9999, the API went on reducing it to 1000, and the code here just stopped saying so. With every call site now within MAX_PAGE_SIZE, a page size the API reduced means a caller asked for something it was never going to get - which is exactly what this warning exists to surface. Drops isReducedPageSize, getPageSizes and PAGE_SIZE_PARAM, and with them the need to pass the response payload into isUnexpectedSelfLink at all. What stays is the part that is a genuine comparison bug rather than a filter: embed params and percent encoding are two ways of writing the same request, so both sides are brought to the same form before being compared. A difference in an actual value is still reported. Spec goes from 25 cases to 17: the nine that pinned the page size filtering are gone, replaced by one asserting a reduced page size warns.
1 parent 8866ec2 commit 93038c2

2 files changed

Lines changed: 19 additions & 128 deletions

File tree

src/app/core/data/dspace-rest-response-parsing.service.spec.ts

Lines changed: 6 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,6 @@ describe('DspaceRestResponseParsingService', () => {
7272
expect(response.payload._links.self.href).toBe('https://rest.api/core/items/eba1c085/bundles');
7373
});
7474

75-
it('should not warn when the REST API reduced the requested page size', () => {
76-
// https://github.com/dataquest-dev/dspace-customers/issues/862
77-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?embed=primaryBitstream&size=9999');
78-
const response = service.callEnsureSelfLink(request, responseWithSelfLink(
79-
'https://rest.api/core/items/eba1c085/bundles?embed=primaryBitstream&size=1000',
80-
{ number: 0, size: 1000, totalPages: 1, totalElements: 2 }));
81-
82-
expect(console.warn).not.toHaveBeenCalled();
83-
expect(response.payload._links.self.href).toBe('https://rest.api/core/items/eba1c085/bundles?size=9999');
84-
});
85-
86-
it('should not warn when the REST API reduced the page size and no embeds are involved', () => {
87-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=9999');
88-
const response = service.callEnsureSelfLink(request, responseWithSelfLink(
89-
'https://rest.api/core/items/eba1c085/bundles?size=1000',
90-
{ number: 0, size: 1000, totalPages: 1, totalElements: 2 }));
91-
92-
expect(console.warn).not.toHaveBeenCalled();
93-
expect(response.payload._links.self.href).toBe('https://rest.api/core/items/eba1c085/bundles?size=9999');
94-
});
95-
9675
it('should not warn when the self link only percent decoded a param value', () => {
9776
// https://github.com/dataquest-dev/dspace-customers/issues/862
9877
const request = requestFor('https://rest.api/statistics/usagereports/search/object?page=-1&size=10&uri=https%3A%2F%2Frest.api%2Fcore%2Fsites%2F8f842a80');
@@ -118,20 +97,13 @@ describe('DspaceRestResponseParsingService', () => {
11897

11998
describe('differences that point at a problem with the endpoint', () => {
12099

121-
it('should warn when the page size shrank but the page block contradicts the self link', () => {
122-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=100');
100+
it('should warn when the REST API reduced the requested page size', () => {
101+
// callers are expected to stay within MAX_PAGE_SIZE, so a reduced size means a caller asked
102+
// for a page the API was never going to serve
103+
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=9999');
123104
service.callEnsureSelfLink(request, responseWithSelfLink(
124-
'https://rest.api/core/items/eba1c085/bundles?size=20',
125-
{ number: 0, size: 100, totalPages: 1, totalElements: 2 }));
126-
127-
expect(console.warn).toHaveBeenCalledTimes(1);
128-
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
129-
});
130-
131-
it('should warn when the page size shrank but the response has no page block to confirm it', () => {
132-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=100');
133-
service.callEnsureSelfLink(request,
134-
responseWithSelfLink('https://rest.api/core/items/eba1c085/bundles?size=20'));
105+
'https://rest.api/core/items/eba1c085/bundles?size=1000',
106+
{ number: 0, size: 1000, totalPages: 1, totalElements: 2 }));
135107

136108
expect(console.warn).toHaveBeenCalledTimes(1);
137109
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
@@ -147,16 +119,6 @@ describe('DspaceRestResponseParsingService', () => {
147119
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
148120
});
149121

150-
it('should warn when the url is ambiguous about the page size', () => {
151-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=5&size=10');
152-
service.callEnsureSelfLink(request, responseWithSelfLink(
153-
'https://rest.api/core/items/eba1c085/bundles?size=3',
154-
{ number: 0, size: 3, totalPages: 1, totalElements: 2 }));
155-
156-
expect(console.warn).toHaveBeenCalledTimes(1);
157-
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
158-
});
159-
160122
it('should still warn when a param value differs beyond its encoding', () => {
161123
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?uri=https%3A%2F%2Frest.api%2Fcore%2Fsites%2Faaa');
162124
service.callEnsureSelfLink(request,
@@ -166,46 +128,6 @@ describe('DspaceRestResponseParsingService', () => {
166128
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
167129
});
168130

169-
it('should warn when a reduced page size hides another param that differs', () => {
170-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?page=0&size=9999');
171-
service.callEnsureSelfLink(request, responseWithSelfLink(
172-
'https://rest.api/core/items/eba1c085/bundles?page=3&size=1000',
173-
{ number: 3, size: 1000, totalPages: 4, totalElements: 3200 }));
174-
175-
expect(console.warn).toHaveBeenCalledTimes(1);
176-
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
177-
});
178-
179-
it('should warn when the self link claims an empty page', () => {
180-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=10');
181-
service.callEnsureSelfLink(request, responseWithSelfLink(
182-
'https://rest.api/core/items/eba1c085/bundles?size=0',
183-
{ number: 0, size: 0, totalPages: 0, totalElements: 0 }));
184-
185-
expect(console.warn).toHaveBeenCalledTimes(1);
186-
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
187-
});
188-
189-
it('should not treat a param that merely ends in `size` as the page size', () => {
190-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?pagesize=9999');
191-
service.callEnsureSelfLink(request, responseWithSelfLink(
192-
'https://rest.api/core/items/eba1c085/bundles?pagesize=1000',
193-
{ number: 0, size: 1000, totalPages: 1, totalElements: 2 }));
194-
195-
expect(console.warn).toHaveBeenCalledTimes(1);
196-
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
197-
});
198-
199-
it('should not accept a page block that confirms the reduced size only as a string', () => {
200-
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?size=9999');
201-
service.callEnsureSelfLink(request, responseWithSelfLink(
202-
'https://rest.api/core/items/eba1c085/bundles?size=1000',
203-
{ number: 0, size: '1000', totalPages: 1, totalElements: 2 }));
204-
205-
expect(console.warn).toHaveBeenCalledTimes(1);
206-
expect(console.warn).toHaveBeenCalledWith(MISMATCH);
207-
});
208-
209131
it('should report the normalized request url and the raw self link in the warning', () => {
210132
const request = requestFor('https://rest.api/core/items/eba1c085/bundles?page=0&embed=primaryBitstream&size=5');
211133
service.callEnsureSelfLink(request,

src/app/core/data/dspace-rest-response-parsing.service.ts

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,6 @@ const urlPartsDiffer = (expected: string[], actual: string[]): boolean => {
6262
|| actual.some((part: string) => !expected.includes(part));
6363
};
6464

65-
/**
66-
* Matches a page size query param, e.g. `size=100`
67-
*/
68-
const PAGE_SIZE_PARAM = /^size=(\d+)$/;
69-
7065
/**
7166
* Percent decode each url part, so `uri=http%3A%2F%2Fx` and `uri=http://x` compare equal. Parts are
7267
* decoded one by one, after the url was split, so a decoded `&` can't merge two params.
@@ -82,46 +77,20 @@ const decodeUrlParts = (parts: string[]): string[] => {
8277
};
8378

8479
/**
85-
* Return the page sizes among the given url parts. More than one means the url is ambiguous
86-
*/
87-
const getPageSizes = (parts: string[]): number[] => {
88-
return parts
89-
.map((part: string) => part.match(PAGE_SIZE_PARAM))
90-
.filter((matches) => hasValue(matches))
91-
.map((matches) => Number(matches[1]));
92-
};
93-
94-
/**
95-
* Return true if the self link only shrank the page size to a value the response confirms itself.
80+
* Return true if the self link differs from the requested url in a way that isn't just a different
81+
* way of writing the same request.
9682
*
97-
* The REST API silently resets a `size` over its configured maximum. The frontend can't know that
98-
* maximum, so it requires the `page` block to corroborate the smaller size, and reports a self link
99-
* that contradicts the payload it describes. Zero is never a maximum, so an empty page isn't a
100-
* clamp either.
83+
* Both sides are brought to the same form first: `embed`/`embed.size` params are stripped, because
84+
* the frontend treats them as not part of a resource's identity and indexes without them, and both
85+
* are percent decoded. Anything still differing is a real difference between what was asked for and
86+
* what came back, including a page size the API reduced — callers are expected to stay within
87+
* `MAX_PAGE_SIZE` rather than have that reported difference filtered out here.
10188
*/
102-
const isReducedPageSize = (expected: string[], actual: string[], payload: any): boolean => {
103-
const requestedSizes = getPageSizes(expected);
104-
const effectiveSizes = getPageSizes(actual);
105-
return requestedSizes.length === 1 && effectiveSizes.length === 1
106-
&& effectiveSizes[0] > 0 && effectiveSizes[0] < requestedSizes[0]
107-
&& hasValue(payload.page) && payload.page.size === effectiveSizes[0];
108-
};
109-
110-
/**
111-
* Return true if the self link differs from the requested url in a way the REST API isn't expected
112-
* to introduce by itself. Not reported: `embed` params the API echoes back (they are stripped from
113-
* the requested url but not from the self link), percent encoding, and a confirmed page size clamp.
114-
*/
115-
const isUnexpectedSelfLink = (requestedUrl: string, selfLink: string, payload: any): boolean => {
116-
const expected = decodeUrlParts(splitUrlInParts(requestedUrl));
117-
const actual = decodeUrlParts(splitUrlInParts(getUrlWithoutEmbedParams(selfLink)));
118-
119-
if (isReducedPageSize(expected, actual, payload)) {
120-
const withoutPageSize = (parts: string[]): string[] =>
121-
parts.filter((part: string) => !PAGE_SIZE_PARAM.test(part));
122-
return urlPartsDiffer(withoutPageSize(expected), withoutPageSize(actual));
123-
}
124-
return urlPartsDiffer(expected, actual);
89+
const isUnexpectedSelfLink = (requestedUrl: string, selfLink: string): boolean => {
90+
return urlPartsDiffer(
91+
decodeUrlParts(splitUrlInParts(requestedUrl)),
92+
decodeUrlParts(splitUrlInParts(getUrlWithoutEmbedParams(selfLink))),
93+
);
12594
};
12695

12796
@Injectable({ providedIn: 'root' })
@@ -231,7 +200,7 @@ export class DspaceRestResponseParsingService implements ResponseParsingService
231200
const actual = splitUrlInParts(selfLink);
232201
if (expected[0] === actual[0] && urlPartsDiffer(expected, actual)) {
233202
// the self link is normalized either way, only the warning is filtered
234-
if (isUnexpectedSelfLink(urlWithoutEmbedParams, selfLink, response.payload)) {
203+
if (isUnexpectedSelfLink(urlWithoutEmbedParams, selfLink)) {
235204
console.warn(`The response for '${urlWithoutEmbedParams}' has the self link '${selfLink}'. These don't match. This could mean there's an issue with the REST endpoint`);
236205
}
237206
response.payload._links = Object.assign({}, response.payload._links, {

0 commit comments

Comments
 (0)