diff --git a/__tests__/getGamesAmerica.test.ts b/__tests__/getGamesAmerica.test.ts index 1ea3a3e8..b1d765e6 100644 --- a/__tests__/getGamesAmerica.test.ts +++ b/__tests__/getGamesAmerica.test.ts @@ -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(); diff --git a/src/constants.ts b/src/constants.ts index f157a0bc..35c4eda3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -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`; @@ -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', diff --git a/src/nintendo-switch-eshop.ts b/src/nintendo-switch-eshop.ts index be3bdfa2..0f156872 100644 --- a/src/nintendo-switch-eshop.ts +++ b/src/nintendo-switch-eshop.ts @@ -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 => { - 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 => { + 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 = []; + 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()))