Skip to content

Commit

Permalink
fix(fetchAlgoliaResults): safely access searchClient credentials (#1133)
Browse files Browse the repository at this point in the history
It's possible to have a broken / badly copied search client without transporter, and that would throw from 1.9.2 onwards.

This PR makes that access safer and throws a clear error.

CR-3338
  • Loading branch information
Haroenv committed Apr 26, 2023
1 parent 48899d2 commit f0f7a62
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,59 @@ describe('fetchAlgoliaResults', () => {
'1.0.0'
);
});

describe('missing credentials', () => {
test('throws dev error when searchClient has no readable appId', () => {
const searchClient = {
search: createSearchClient().search,
};

expect(() => {
fetchAlgoliaResults({
// @ts-expect-error
searchClient,
queries: [],
userAgents: [{ segment: 'custom-ua', version: '1.0.0' }],
});
}).toThrowErrorMatchingInlineSnapshot(
`"[Autocomplete] The Algolia \`appId\` was not accessible from the searchClient passed."`
);
});

test('throws dev error when searchClient has no readable apiKey', () => {
const searchClient = {
search: createSearchClient().search,
transporter: {
headers: {
'x-algolia-application-id': 'appId',
},
},
};

expect(() => {
fetchAlgoliaResults({
// @ts-expect-error
searchClient,
queries: [],
userAgents: [{ segment: 'custom-ua', version: '1.0.0' }],
});
}).toThrowErrorMatchingInlineSnapshot(
`"[Autocomplete] The Algolia \`apiKey\` was not accessible from the searchClient passed."`
);
});

test('does not throw dev error when searchClient is copied', () => {
const searchClient = {
...createSearchClient(),
};

expect(() => {
fetchAlgoliaResults({
searchClient,
queries: [],
userAgents: [{ segment: 'custom-ua', version: '1.0.0' }],
});
}).not.toThrow();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
userAgents as coreUserAgents,
UserAgent,
invariant,
} from '@algolia/autocomplete-shared';

import { HIGHLIGHT_PRE_TAG, HIGHLIGHT_POST_TAG } from '../constants';
Expand Down Expand Up @@ -28,6 +29,15 @@ export function fetchAlgoliaResults<TRecord>({

const { appId, apiKey } = getAppIdAndApiKey(searchClient);

invariant(
Boolean(appId),
'The Algolia `appId` was not accessible from the searchClient passed.'
);
invariant(
Boolean(apiKey),
'The Algolia `apiKey` was not accessible from the searchClient passed.'
);

return searchClient
.search<TRecord>(
queries.map((searchParameters) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,14 @@ describe('getAppIdAndApiKey', () => {
expect(appId).toEqual(APP_ID);
expect(apiKey).toEqual(API_KEY);
});

it('gets undefined appId and apiKey from broken search client', () => {
const searchClient = {
search: algoliasearchV4(APP_ID, API_KEY).search,
};
// @ts-expect-error
const { appId, apiKey } = getAppIdAndApiKey(searchClient);
expect(appId).toEqual(undefined);
expect(apiKey).toEqual(undefined);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export function getAppIdAndApiKey(searchClient: SearchClient): {
appId: string;
apiKey: string;
} {
const { headers, queryParameters } = searchClient.transporter;
const { headers = {}, queryParameters = {} } = searchClient.transporter || {};
const APP_ID = 'x-algolia-application-id';
const API_KEY = 'x-algolia-api-key';
const appId = headers[APP_ID] || queryParameters[APP_ID];
Expand Down

0 comments on commit f0f7a62

Please sign in to comment.