Skip to content
This repository has been archived by the owner on Nov 6, 2021. It is now read-only.

Commit

Permalink
fix(api): updated getgamesamerica algolia request to retrieve all gam…
Browse files Browse the repository at this point in the history
…es (#281)


getGamesAmerica will now always get all data and it has lost some
parameters in the process.

BREAKING CHANGE: getGamesAmerica no longer takes any parameters and
should always return all games.

Co-authored-by: Jeroen Claassens <support@favware.tech>
  • Loading branch information
bvanzile and favna authored Oct 31, 2020
1 parent 6b4cfb1 commit 2ace2cc
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 127 deletions.
6 changes: 0 additions & 6 deletions __tests__/getGamesAmerica.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import defaultGetGamesAmerica, { getGamesAmerica } from '../src';

describe('getGamesAmerica', () => {
test('should allow custom limit', async () => {
const data = await getGamesAmerica({ limit: 1 });
expect(data).toBeInstanceOf(Object);
expect(data).toHaveLength(1);
});

test('should allow no options', async () => {
jest.setTimeout(60000);
const data = await getGamesAmerica();
Expand Down
32 changes: 30 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const US_GET_GAMES_OPTIONS = { system: 'platform:Nintendo Switch', sort:
export const US_ALGOLIA_ID = 'U3B6GR4UA3';

/** Algolia Key for getting US games */
export const US_ALGOLIA_KEY = '9a20c93440cf63cf1a7008d75f7438bf';
export const US_ALGOLIA_KEY = 'c4da8be7fd29f0f5bfa42920b0a99dc7';

/** URL for getting US Games */
export const US_GET_GAMES_URL = `https://${US_ALGOLIA_ID}-dsn.algolia.net/1/indexes/*/queries`;
Expand All @@ -17,11 +17,39 @@ export const US_GAME_CHECK_CODE = '70010000000185';
export const US_GAME_CODE_REGEX = /HAC\w(\w{4})/;

/** Default limit for getting US games - Defaults to 200 */
export const US_GAME_LIST_LIMIT = 200;
export const US_GAME_LIST_LIMIT = 1000;

/** Index names for querying all games by both ascending and descending title */
export const US_INDEX_TITLE_ASC = 'ncom_game_en_us_title_asc';
export const US_INDEX_TITLE_DES = 'ncom_game_en_us_title_des';

/** Static query parameters for facets/filters in US Algolia calls */
export const US_FACETS = [
'generalFilters',
'platform',
'availability',
'genres',
'howToShop',
'virtualConsole',
'franchises',
'priceRange',
'esrbRating',
'playerFilters'
];
export const US_PLATFORM_FACET_FILTER = 'platform:Nintendo Switch';

/** Price ranges for US games */
export const US_PRICE_RANGES = ['Free to start', '$0 - $4.99', '$5 - $9.99', '$10 - $19.99', '$20 - $39.99', '$40+'];

/** ESRB options and Coming Soon facet filters for querying all games in one request */
export const US_ESRB_RATINGS_FILTERS = {
everyone: 'esrbRating:Everyone',
everyone10: 'esrbRating:Everyone 10+',
teen: 'esrbRating:Teen',
mature: 'esrbRating:Mature'
};
export const US_AVAILABILITY_FILTER = 'availability:Coming soon';

/** Request headers for US games */
export const US_ALGOLIA_HEADERS = {
'Content-Type': 'application/json',
Expand Down
216 changes: 97 additions & 119 deletions src/nintendo-switch-eshop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,147 +24,125 @@ const arrayRemoveDuplicates = (array: any[], property: string) => {
};

/**
* Fetches all games on american eshops
* Fetches all games on american e-shops
*
* @remarks
* Paginates every 200 games, _(maximum item count per request)_
* Currently ONLY returns all games in the e-shop
*
* @param options _(Optional)_ Options for the request
* @param offset _(Optional)_ Offset to start searching at
* @param games _(Optional)_ Array of games to filter by
* @returns Promise containing all the games
*/
export const getGamesAmerica = async (
options: interfaces.RequestOptions = {},
offset = 0,
games: interfaces.GameUS[] = []
): Promise<interfaces.GameUS[]> => {
const limit = (Reflect.get(options, 'limit') as number) ?? constants.US_GAME_LIST_LIMIT;
const page = Math.floor(offset / limit);

const sortingOptions = {
direction: constants.US_GET_GAMES_OPTIONS.direction,
sortBy: constants.US_GET_GAMES_OPTIONS.sort
};
export const getGamesAmerica = async (): Promise<interfaces.GameUS[]> => {
const limit = constants.US_GAME_LIST_LIMIT;
const page = 0;

const body = {
body: JSON.stringify({
requests: [
{
indexName: `noa_aem_game_en_us${
sortingOptions.sortBy && sortingOptions.direction
? `_${sortingOptions.sortBy}_${sortingOptions.direction}`
: ''
}`,
indexName: constants.US_INDEX_TITLE_ASC,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.everyone}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_DES,
params: stringify({
facetFilters: [[constants.US_GET_GAMES_OPTIONS.system] as any],
query: '',
hitsPerPage: limit,
page
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.everyone}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_ASC,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.everyone10}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_DES,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.everyone10}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_ASC,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.teen}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_DES,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.teen}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_ASC,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_ESRB_RATINGS_FILTERS.mature}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
},
{
indexName: constants.US_INDEX_TITLE_ASC,
params: stringify({
query: '',
hitsPerPage: limit,
page: page,
analytics: false,
facets: JSON.stringify(constants.US_FACETS),
facetFilters: `[["${constants.US_AVAILABILITY_FILTER}"],["${constants.US_PLATFORM_FACET_FILTER}"]]`
})
}
]
}),
headers: constants.US_ALGOLIA_HEADERS,
method: 'POST'
method: 'POST',
headers: constants.US_ALGOLIA_HEADERS
};

try {
if (Reflect.has(options, 'limit')) {
const gamesUS = await fetch(constants.US_GET_GAMES_URL, body);

if (!gamesUS.ok) throw new Error('US_games_request_failed');
const allGamesResponse = await fetch(constants.US_GET_GAMES_URL, body);
if (!allGamesResponse.ok) throw new Error('US_games_request_failed');
const gamesResponse: interfaces.AlgoliaResponse = await allGamesResponse.json();

const filteredResponse: interfaces.AlgoliaResponse = await gamesUS.json();
const accumulatedGames: interfaces.GameUS[] = arrayRemoveDuplicates(
games.concat(filteredResponse.results[0].hits),
'slug'
);

if (
!Reflect.has(options, 'limit') &&
filteredResponse.results[0].hits.length + offset < filteredResponse.results[0].nbHits
) {
return await getGamesAmerica(options, offset + limit, accumulatedGames);
}

return accumulatedGames;
let allGames: any[] | PromiseLike<interfaces.GameUS[]> = [];
for (const results of gamesResponse.results) {
allGames = allGames.concat(results.hits);
}
/**
* Using a workaround to get all the games.
* Basically, fetch all the games from the different categories one by one,
* if one category has > 100 games, fetch all the games in each price range one by one.
*
* Get the counts of all the games in the different categories.
*/
const categoriesRequestOptions = {
body: JSON.stringify({
requests: [
{
indexName: 'noa_aem_game_en_us',
params: stringify({
facetFilters: [[constants.US_GET_GAMES_OPTIONS.system] as any],
facets: ['categories'],
hitsPerPage: 0
})
}
]
}),
headers: constants.US_ALGOLIA_HEADERS,
method: 'POST'
};

const gamesToCount = await fetch(constants.US_GET_GAMES_URL, categoriesRequestOptions);
if (!gamesToCount.ok) throw new Error('US_games_request_failed');

const response: interfaces.AlgoliaResponse = await gamesToCount.json();
const categoryCount = response.results[0].facets.categories;

// Loop through all the categories and fetch the games.
const allGamesPromises = Object.entries(categoryCount).map(async ([category, count]) => {
const normalRequest = [
{
indexName: 'noa_aem_game_en_us',
params: stringify({
facetFilters: JSON.stringify([[constants.US_GET_GAMES_OPTIONS.system], [`categories:${category}`]]),
hitsPerPage: 100
})
}
];

const manyPriceRangeRequests = constants.US_PRICE_RANGES.map((priceRange) => ({
indexName: 'noa_aem_game_en_us',
params: stringify({
facetFilters: JSON.stringify([
[constants.US_GET_GAMES_OPTIONS.system],
[`categories:${category}`],
[`priceRange:${priceRange}`]
]),
facets: ['platform', 'categories'],
hitsPerPage: 100
})
}));

const allGamesRequestOptions = {
body: JSON.stringify({ requests: count > 100 ? manyPriceRangeRequests : normalRequest }),
headers: constants.US_ALGOLIA_HEADERS,
method: 'POST'
};

const allGamesResponse = await fetch(constants.US_GET_GAMES_URL, allGamesRequestOptions);

if (!allGamesResponse.ok) throw new Error('US_games_request_failed');

const gamesResponse: interfaces.AlgoliaResponse = await allGamesResponse.json();

return count > 100
? gamesResponse.results.map((result) => result.hits).reduce((a, b) => a.concat(b, []))
: gamesResponse.results[0].hits;
});

// Finally fetch all the games and remove duplicates
let allGames = (await Promise.all(allGamesPromises)).reduce((a, b) => a.concat(b, []));
allGames = arrayRemoveDuplicates(allGames, 'slug');

allGames = arrayRemoveDuplicates(allGames, 'slug');
return allGames;
} catch (err) {
if (/(?:US_games_request_failed)/i.test(err.toString()))
Expand Down

0 comments on commit 2ace2cc

Please sign in to comment.