UNPKG

@donation-alerts/api

Version:
173 lines (172 loc) 7.2 kB
import { __decorate } from "tslib"; import { TimeBasedRateLimiter, TimedPassthruRateLimiter, } from '@d-fischer/rate-limiter'; import { callDonationAlertsApi, callDonationAlertsApiRaw, handleDonationAlertsApiResponseError, transformDonationAlertsResponse, } from '@donation-alerts/api-call'; import { extractUserId, ReadDocumentation } from '@donation-alerts/common'; import { createLogger } from '@stimulcross/logger'; import { nonenumerable } from '@stimulcross/shared-utils'; import { Memoize } from 'typescript-memoize'; import { DonationAlertsCentrifugoApi } from "./api/centrifugo/donation-alerts-centrifugo-api.mjs"; import { DonationAlertsCustomAlertsApi } from "./api/customAlerts/donation-alerts-custom-alerts-api.mjs"; import { DonationAlertsDonationsApi } from "./api/donations/donation-alerts-donations-api.mjs"; import { DonationAlertsMerchandiseApi } from "./api/merchandise/donation-alerts-merchandise-api.mjs"; import { DonationAlertsUsersApi } from "./api/users/donation-alerts-users-api.mjs"; /** * The client for interacting with the Donation Alerts API. */ let ApiClient = class ApiClient { /** * Creates a new instance of the API client. * * @param config The configuration options for the API client. * @throws Error if the `authProvider` is not supplied in the configuration. */ constructor(config) { if (!config.authProvider) { throw new Error('No auth provider given. Please supply the `authProvider` option.'); } this._config = config; this._limitReachedBehavior = config.rateLimiterOptions?.limitReachedBehavior ?? 'enqueue'; if (config.rateLimiterOptions?.limitToOneRequestPerSecond ?? true) { this._rateLimiter = new TimedPassthruRateLimiter(new TimeBasedRateLimiter({ timeFrame: 1000, bucketSize: 1, doRequest: async (req) => await callDonationAlertsApiRaw(req.options, req.accessToken, req.fetchOptions), logger: { minLevel: 'ERROR' }, }), { bucketSize: 60, timeFrame: 60000 }); } else { this._rateLimiter = new TimeBasedRateLimiter({ bucketSize: 60, timeFrame: 60000, doRequest: async (req) => await callDonationAlertsApiRaw(req.options, req.accessToken, req.fetchOptions), logger: { minLevel: 'ERROR' }, }); } this._logger = createLogger({ context: 'da:api', ...config.logger }); } /** * Users API namespace. * * This namespace allows you to fetch user details, such as information about the authenticated user. */ get users() { return new DonationAlertsUsersApi(this); } /** * Donations API namespace. * * This namespace provides methods for retrieving donation data. */ get donations() { return new DonationAlertsDonationsApi(this); } /** * Custom Alerts API namespace. * * This namespace provides methods for sending custom alerts. */ get customAlerts() { return new DonationAlertsCustomAlertsApi(this); } /** * * Centrifugo API namespace. * * This namespace provides methods for subscribing to Centrifugo channels. */ get centrifugo() { return new DonationAlertsCentrifugoApi(this); } /** * Merchandise API namespace. * * This namespace allows managing merchandise-related data. */ get merchandise() { return new DonationAlertsMerchandiseApi(this); } /** * Sends a request to the Donation Alerts API. * * @param user The ID of the user making the request. * @param options Options for the API call, including method, URL, and other details. * @param rateLimiterOptions Options for fine-tuning rate-limiting behavior. * * @throws {@link HttpError} If the response status code is outside the 200-299 range. * @throws {@link UnregisteredUserError} If the specified user is not registered in the authentication provider. * @throws {@link MissingScopeError} If the access token lacks the required scope to complete the request. */ async callApi(user, options, rateLimiterOptions = {}) { const userId = extractUserId(user); const { authProvider } = this._config; const shouldAuth = options.auth ?? true; let accessToken = shouldAuth ? await authProvider.getAccessTokenForUser(userId, options.scope ? [options.scope] : undefined) : null; if (!accessToken) { return await callDonationAlertsApi(options, undefined, this._config.fetchOptions); } let response = await this._callApiInternal(options, accessToken.accessToken, rateLimiterOptions); if (response.status === 401 && authProvider.refreshAccessTokenForUser) { accessToken = await authProvider.refreshAccessTokenForUser(userId); response = await this._callApiInternal(options, accessToken.accessToken, rateLimiterOptions); } await handleDonationAlertsApiResponseError(response, options); return await transformDonationAlertsResponse(response); } async _callApiInternal(options, accessToken, rateLimiterOptions = {}) { const { fetchOptions } = this._config; const type = options.type ?? 'api'; const limitReachedBehavior = rateLimiterOptions.limitReachedBehavior ?? this._limitReachedBehavior; this._logger.debug(`Calling ${type}: ${options.method ?? 'GET'} ${options.url}`); if (options.query) { this._logger.trace(`Query: ${JSON.stringify(options.query)}`); } if (options.jsonBody) { this._logger.trace(`Request JSON body: ${JSON.stringify(options.jsonBody)}`); } if (options.formBody) { this._logger.trace(`Request form body: ${JSON.stringify(options.formBody)}`); } const response = type === 'api' ? await this._rateLimiter.request({ options, accessToken, fetchOptions, }, { limitReachedBehavior }) : await callDonationAlertsApiRaw(options, accessToken, fetchOptions); this._logger.debug(`Called API: ${options.method ?? 'GET'} ${options.url} - result: ${response.status}`); return response; } }; __decorate([ nonenumerable ], ApiClient.prototype, "_config", void 0); __decorate([ nonenumerable ], ApiClient.prototype, "_logger", void 0); __decorate([ nonenumerable ], ApiClient.prototype, "_rateLimiter", void 0); __decorate([ nonenumerable ], ApiClient.prototype, "_limitReachedBehavior", void 0); __decorate([ Memoize() ], ApiClient.prototype, "users", null); __decorate([ Memoize() ], ApiClient.prototype, "donations", null); __decorate([ Memoize() ], ApiClient.prototype, "customAlerts", null); __decorate([ Memoize() ], ApiClient.prototype, "centrifugo", null); __decorate([ Memoize() ], ApiClient.prototype, "merchandise", null); ApiClient = __decorate([ ReadDocumentation('api') ], ApiClient); export { ApiClient };