zaccl
Version:
The Zoom App Complete Connection Library, a project that handles everything required to build a zoom-integrated app.
183 lines • 8.79 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Import shared helpers
const sendZoomRequest_1 = __importDefault(require("./sendZoomRequest"));
const ErrorCode_1 = __importDefault(require("../types/ErrorCode"));
const ThrottleHeader_1 = __importDefault(require("../types/ThrottleHeader"));
// Import shared classes
const ZACCLError_1 = __importDefault(require("../classes/ZACCLError"));
/**
* Generates a visit endpoint function
* @author Gabe Abrams
* @author Grace Whitney
* @param zoomAPIConfig the active api configuration
*/
const genVisitEndpoint = (zoomAPIConfig) => {
/**
* Visit a Zoom endpoint
* @author Gabe Abrams
* @param opts object containing all arguments
* @param opts.path Path of the endpoint
* @param opts.method http method to use for the request
* @param opts.action Human-readable description of the task
* @param opts.errorMap map of Zoom errors taken from the API docs
* @param [opts.postProcessor] function that processes the response before
* returning
* @param [opts.params] Parameters/args/body to send with request
* @param [opts.onNewPage] callback function that is called when a
* new page of results is received
* @param [opts.itemKey] the key in the response body where the list of items can be found
* @param [opts.minMsBetweenPageRequests] minimum time (in ms) to wait between
* paginated requests, for custom throttle control
* @returns response body
*/
return (opts) => __awaiter(void 0, void 0, void 0, function* () {
var _a;
const { path, params, action, errorMap, itemKey, onNewPage, minMsBetweenPageRequests } = opts;
const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
// Paging state
let nextPageToken = undefined;
let isFirstPage = true;
let isPaginated = false; // Inferred by responses, always starts false
const allItems = [];
let lastPageRequestTimestamp = 0;
// Fetch page by page
while (nextPageToken || isFirstPage) {
// Don't fetch another page unless we get a token
isFirstPage = false;
/* ---------- Send Request ---------- */
// If minMsBetweenPageRequests is set, ensure we wait at least that long between requests
if (minMsBetweenPageRequests) {
// Check how long since the last request
const now = Date.now();
const timeSinceLastRequest = now - lastPageRequestTimestamp;
// Wait extra time if we haven't waited long enough yet
if (timeSinceLastRequest < minMsBetweenPageRequests) {
yield new Promise((resolve) => {
setTimeout(resolve, minMsBetweenPageRequests - timeSinceLastRequest);
});
}
// Update timestamp of last page request
lastPageRequestTimestamp = Date.now();
}
// Send the request
const { status, headers, body } = yield (0, sendZoomRequest_1.default)({
path,
method,
params: Object.assign(Object.assign({}, params), { next_page_token: nextPageToken }),
zoomAPIConfig,
});
/* ----------- Rate Error ----------- */
if (status === 429) {
// Case-insensitive header lookup
const [rateLimitTypeHeader] = Object.keys(headers).filter((header) => {
return (header.toLowerCase() === 'x-ratelimit-type');
});
// Case-insensitive limit type lookup
const rateLimitType = (headers[rateLimitTypeHeader]
&& headers[rateLimitTypeHeader].toLowerCase());
if (rateLimitType === ThrottleHeader_1.default.DailyLimitHeader) {
// Daily limit
throw new ZACCLError_1.default({
message: 'Zoom is very busy today. Please try this operation again tomorrow.',
code: ErrorCode_1.default.DailyLimitError,
});
}
else if (rateLimitType === ThrottleHeader_1.default.RateLimitHeader) {
// Rate limit
throw new ZACCLError_1.default({
message: 'Zoom is very busy right now. Please try this operation again later.',
code: ErrorCode_1.default.RateLimitError,
});
}
else {
// Unknown rate limit
throw new ZACCLError_1.default({
message: 'Zoom is very busy right now. Please try this operation again.',
code: ErrorCode_1.default.UnknownLimitError,
});
}
}
/* -------- Custom Error Code ------- */
if (status < 200 || status >= 300) {
// A Zoom error occurred
// Check status to see if its in the error map
let zoomErrorMessage = 'An unknown Zoom error occurred.';
if (errorMap[status]) {
if (typeof errorMap[status] === 'string') {
// Found the error message
zoomErrorMessage = errorMap[status];
}
else if (body.code) {
// Check for nested error message
if (typeof errorMap[status][body.code] === 'string') {
// Found nested error message
zoomErrorMessage = errorMap[status][body.code];
}
else if (body.message) {
// errorMap[status][code] did not return err message
// so we check body
if (typeof body.message === 'string') {
zoomErrorMessage = body.message;
}
}
}
}
else if (body.message) {
// Error message not in the error map so check body
if (typeof body.message === 'string') {
zoomErrorMessage = body.message;
}
}
const errorMessage = `We couldn't ${action} because an error occurred: ${zoomErrorMessage}`;
const errorCode = `ZOOM${status}${body.code ? `-${body.code}` : ''}`;
throw new ZACCLError_1.default({
message: errorMessage,
code: errorCode,
});
}
/* ----------- Postprocess ---------- */
// Extract results
let results = itemKey ? body[itemKey] : body;
// Update next page token
nextPageToken = body.next_page_token;
if (nextPageToken) {
isPaginated = true;
}
// End if not paginated
if (!isPaginated) {
// Still call for the first page
if (onNewPage) {
onNewPage(results);
}
return results;
}
/* --------- Paging Handling -------- */
// Add items to allItems if paginated
if (Array.isArray(results)) {
allItems.push(...results);
}
// Call onNewPage callback if it exists
if (onNewPage) {
onNewPage(results);
}
}
;
// Return all items (if we got here, response is paginated)
return allItems;
});
};
exports.default = genVisitEndpoint;
//# sourceMappingURL=genVisitEndpoint.js.map