Ensure subsequent page reads are run through the proxy

Signed-off-by: Robert Bunning <rbunning@webstaurantstore.com>
This commit is contained in:
Robert Bunning
2023-08-01 11:20:31 -04:00
parent 2167b7eab0
commit f55b6fd1e2
2 changed files with 44 additions and 12 deletions
+30 -4
View File
@@ -126,7 +126,7 @@ describe('NewRelicClient', () => {
headers: new Map<string, string | null>([
[
'link',
'<https://next.page/page2>; rel="next", <https://badsite.dontgohere>; rel="next"',
'<https://next.page/page2?page=2>; rel="next", <https://badsite.dontgohere>; rel="next"',
],
['otherheader', 'otherValue'],
]),
@@ -135,7 +135,7 @@ describe('NewRelicClient', () => {
ok: true,
json: async () => ({ applications: [] }),
headers: new Map<string, string | null>([
['Link', '<https://next.page/page3>; rel="next",'],
['Link', '<https://next.page/page3?page=3>; rel="next",'],
]),
})
.mockResolvedValueOnce({
@@ -170,10 +170,10 @@ describe('NewRelicClient', () => {
'https://test.test/newrelic/apm/api/applications.json',
);
expect(mockedFetchApi.fetch).toHaveBeenCalledWith(
'https://next.page/page2',
'https://test.test/newrelic/apm/api/applications.json?page=2',
);
expect(mockedFetchApi.fetch).toHaveBeenCalledWith(
'https://next.page/page3',
'https://test.test/newrelic/apm/api/applications.json?page=3',
);
expect(actual).toStrictEqual(expected);
});
@@ -220,6 +220,7 @@ describe('NewRelicClient', () => {
['<>; rel:"value"'],
['<https://next.page>; rel: "next"'],
['ABCDE'],
['<https://next.page/page3?page=>; rel="next",'],
])(
'It does not attempt pagination when the link header value is invalid (%p)',
async linkHeaderValue => {
@@ -309,4 +310,29 @@ describe('NewRelicClient', () => {
expect(actual).toStrictEqual({ applications: [] });
});
it('Generates the base url only once', async () => {
const mockedDiscoveryApi: DiscoveryApi = {
getBaseUrl: jest.fn().mockResolvedValueOnce('https://test.test'),
};
const mockedFetchApi: FetchApi = {
fetch: jest.fn().mockResolvedValue({
ok: true,
json: async () => ({ applications: [] }),
}),
};
const client = new NewRelicClient({
discoveryApi: mockedDiscoveryApi,
fetchApi: mockedFetchApi,
});
await client.getApplications();
await client.getApplications();
await client.getApplications();
await client.getApplications();
expect(mockedDiscoveryApi.getBaseUrl).toHaveBeenCalledTimes(1);
});
});
+14 -8
View File
@@ -91,16 +91,22 @@ export class NewRelicClient implements NewRelicApi {
private readonly discoveryApi: DiscoveryApi;
private readonly fetchApi: FetchApi;
private readonly proxyPathBase: string;
private baseUrl: string;
constructor(options: Options) {
this.discoveryApi = options.discoveryApi;
this.fetchApi = options.fetchApi;
this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE;
this.baseUrl = '';
}
async getApplications(): Promise<NewRelicApplications> {
const proxyUrl = await this.discoveryApi.getBaseUrl('proxy');
let targetUrl = `${proxyUrl}${this.proxyPathBase}/apm/api/applications.json`;
if (!this.baseUrl) {
const proxyUrl = await this.discoveryApi.getBaseUrl('proxy');
this.baseUrl = `${proxyUrl}${this.proxyPathBase}/apm/api/applications.json`;
}
let targetUrl = this.baseUrl;
let hasNextPage = true;
try {
@@ -111,7 +117,7 @@ export class NewRelicClient implements NewRelicApi {
await this.fetchNewRelic(targetUrl);
hasNextPage = hasMoreApplications;
targetUrl = pagination!.nextPageLink;
targetUrl = hasNextPage ? pagination!.nextPageLink : '';
applications = applications.concat(applicationsFromReadPage);
} while (hasNextPage);
@@ -155,12 +161,12 @@ export class NewRelicClient implements NewRelicApi {
}
const nextRelevantLink = linkHeader.replaceAll(' ', '').split(',')[0];
// Link should be of the format <nextPageLink>;rel="relation"
const linkParts = nextRelevantLink.match(/^<(.+)>;rel="(.+)"$/);
const nextPageLink = linkParts?.[1];
const linkParts = nextRelevantLink.match(/^<.+(\?page=.+)>;rel="(.+)"$/);
const nextPageNumber = linkParts?.[1];
const relation = linkParts?.[2];
const isValidLink = !!(!!linkParts && nextPageLink && relation);
const nextPageLink = `${this.baseUrl}${nextPageNumber}`;
const isValidLink = !!(!!linkParts && nextPageNumber && relation);
if (!nextRelevantLink || !isValidLink) {
return undefined;