import type { OptionsWithUri } from 'request'; import type { IExecuteFunctions, IHookFunctions } from 'n8n-core'; import type { IDataObject, JsonObject } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow'; import get from 'lodash.get'; /** * Make an API request to Spotify * */ export async function spotifyApiRequest( this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: object, query?: object, uri?: string, ): Promise { const options: OptionsWithUri = { method, headers: { 'User-Agent': 'n8n', 'Content-Type': 'text/plain', Accept: ' application/json', }, qs: query, uri: uri || `https://api.spotify.com/v1${endpoint}`, json: true, }; if (Object.keys(body).length > 0) { options.body = body; } try { return await this.helpers.requestOAuth2.call(this, 'spotifyOAuth2Api', options); } catch (error) { throw new NodeApiError(this.getNode(), error as JsonObject); } } export async function spotifyApiRequestAllItems( this: IHookFunctions | IExecuteFunctions, propertyName: string, method: string, endpoint: string, body: object, query?: object, ): Promise { const returnData: IDataObject[] = []; let responseData; let uri: string | undefined; do { responseData = await spotifyApiRequest.call(this, method, endpoint, body, query, uri); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument returnData.push.apply(returnData, get(responseData, propertyName)); uri = responseData.next || responseData[propertyName.split('.')[0]].next; //remove the query as the query parameters are already included in the next, else api throws error. query = {}; if (uri?.includes('offset=1000') && endpoint === '/search') { // The search endpoint has a limit of 1000 so step before it returns a 404 return returnData; } } while ( (responseData.next !== null && responseData.next !== undefined) || (responseData[propertyName.split('.')[0]].next !== null && responseData[propertyName.split('.')[0]].next !== undefined) ); return returnData; }