UNPKG

8.57 kBJavaScriptView Raw
1"use strict";
2/*!
3 * Copyright 2015 Google Inc. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.ResourceStream = exports.paginator = exports.Paginator = void 0;
19/*!
20 * @module common/paginator
21 */
22const arrify = require("arrify");
23const extend = require("extend");
24const resource_stream_1 = require("./resource-stream");
25Object.defineProperty(exports, "ResourceStream", { enumerable: true, get: function () { return resource_stream_1.ResourceStream; } });
26/*! Developer Documentation
27 *
28 * paginator is used to auto-paginate `nextQuery` methods as well as
29 * streamifying them.
30 *
31 * Before:
32 *
33 * search.query('done=true', function(err, results, nextQuery) {
34 * search.query(nextQuery, function(err, results, nextQuery) {});
35 * });
36 *
37 * After:
38 *
39 * search.query('done=true', function(err, results) {});
40 *
41 * Methods to extend should be written to accept callbacks and return a
42 * `nextQuery`.
43 */
44class Paginator {
45 /**
46 * Cache the original method, then overwrite it on the Class's prototype.
47 *
48 * @param {function} Class - The parent class of the methods to extend.
49 * @param {string|string[]} methodNames - Name(s) of the methods to extend.
50 */
51 // tslint:disable-next-line:variable-name
52 extend(Class, methodNames) {
53 methodNames = arrify(methodNames);
54 methodNames.forEach(methodName => {
55 const originalMethod = Class.prototype[methodName];
56 // map the original method to a private member
57 Class.prototype[methodName + '_'] = originalMethod;
58 // overwrite the original to auto-paginate
59 /* eslint-disable @typescript-eslint/no-explicit-any */
60 Class.prototype[methodName] = function (...args) {
61 const parsedArguments = paginator.parseArguments_(args);
62 return paginator.run_(parsedArguments, originalMethod.bind(this));
63 };
64 });
65 }
66 /**
67 * Wraps paginated API calls in a readable object stream.
68 *
69 * This method simply calls the nextQuery recursively, emitting results to a
70 * stream. The stream ends when `nextQuery` is null.
71 *
72 * `maxResults` will act as a cap for how many results are fetched and emitted
73 * to the stream.
74 *
75 * @param {string} methodName - Name of the method to streamify.
76 * @return {function} - Wrapped function.
77 */
78 /* eslint-disable @typescript-eslint/no-explicit-any */
79 streamify(methodName) {
80 return function (
81 /* eslint-disable @typescript-eslint/no-explicit-any */
82 ...args) {
83 const parsedArguments = paginator.parseArguments_(args);
84 const originalMethod = this[methodName + '_'] || this[methodName];
85 return paginator.runAsStream_(parsedArguments, originalMethod.bind(this));
86 };
87 }
88 /**
89 * Parse a pseudo-array `arguments` for a query and callback.
90 *
91 * @param {array} args - The original `arguments` pseduo-array that the original
92 * method received.
93 */
94 /* eslint-disable @typescript-eslint/no-explicit-any */
95 parseArguments_(args) {
96 let query;
97 let autoPaginate = true;
98 let maxApiCalls = -1;
99 let maxResults = -1;
100 let callback;
101 const firstArgument = args[0];
102 const lastArgument = args[args.length - 1];
103 if (typeof firstArgument === 'function') {
104 callback = firstArgument;
105 }
106 else {
107 query = firstArgument;
108 }
109 if (typeof lastArgument === 'function') {
110 callback = lastArgument;
111 }
112 if (typeof query === 'object') {
113 query = extend(true, {}, query);
114 // Check if the user only asked for a certain amount of results.
115 if (query.maxResults && typeof query.maxResults === 'number') {
116 // `maxResults` is used API-wide.
117 maxResults = query.maxResults;
118 }
119 else if (typeof query.pageSize === 'number') {
120 // `pageSize` is Pub/Sub's `maxResults`.
121 maxResults = query.pageSize;
122 }
123 if (query.maxApiCalls && typeof query.maxApiCalls === 'number') {
124 maxApiCalls = query.maxApiCalls;
125 delete query.maxApiCalls;
126 }
127 // maxResults is the user specified limit.
128 if (maxResults !== -1 || query.autoPaginate === false) {
129 autoPaginate = false;
130 }
131 }
132 const parsedArguments = {
133 query: query || {},
134 autoPaginate,
135 maxApiCalls,
136 maxResults,
137 callback,
138 };
139 parsedArguments.streamOptions = extend(true, {}, parsedArguments.query);
140 delete parsedArguments.streamOptions.autoPaginate;
141 delete parsedArguments.streamOptions.maxResults;
142 delete parsedArguments.streamOptions.pageSize;
143 return parsedArguments;
144 }
145 /**
146 * This simply checks to see if `autoPaginate` is set or not, if it's true
147 * then we buffer all results, otherwise simply call the original method.
148 *
149 * @param {array} parsedArguments - Parsed arguments from the original method
150 * call.
151 * @param {object=|string=} parsedArguments.query - Query object. This is most
152 * commonly an object, but to make the API more simple, it can also be a
153 * string in some places.
154 * @param {function=} parsedArguments.callback - Callback function.
155 * @param {boolean} parsedArguments.autoPaginate - Auto-pagination enabled.
156 * @param {boolean} parsedArguments.maxApiCalls - Maximum API calls to make.
157 * @param {number} parsedArguments.maxResults - Maximum results to return.
158 * @param {function} originalMethod - The cached method that accepts a callback
159 * and returns `nextQuery` to receive more results.
160 */
161 run_(parsedArguments, originalMethod) {
162 const query = parsedArguments.query;
163 const callback = parsedArguments.callback;
164 if (!parsedArguments.autoPaginate) {
165 return originalMethod(query, callback);
166 }
167 const results = new Array();
168 const promise = new Promise((resolve, reject) => {
169 paginator
170 .runAsStream_(parsedArguments, originalMethod)
171 .on('error', reject)
172 .on('data', (data) => results.push(data))
173 .on('end', () => resolve(results));
174 });
175 if (!callback) {
176 return promise.then(results => [results]);
177 }
178 promise.then(results => callback(null, results), (err) => callback(err));
179 }
180 /**
181 * This method simply calls the nextQuery recursively, emitting results to a
182 * stream. The stream ends when `nextQuery` is null.
183 *
184 * `maxResults` will act as a cap for how many results are fetched and emitted
185 * to the stream.
186 *
187 * @param {object=|string=} parsedArguments.query - Query object. This is most
188 * commonly an object, but to make the API more simple, it can also be a
189 * string in some places.
190 * @param {function=} parsedArguments.callback - Callback function.
191 * @param {boolean} parsedArguments.autoPaginate - Auto-pagination enabled.
192 * @param {boolean} parsedArguments.maxApiCalls - Maximum API calls to make.
193 * @param {number} parsedArguments.maxResults - Maximum results to return.
194 * @param {function} originalMethod - The cached method that accepts a callback
195 * and returns `nextQuery` to receive more results.
196 * @return {stream} - Readable object stream.
197 */
198 /* eslint-disable @typescript-eslint/no-explicit-any */
199 runAsStream_(parsedArguments, originalMethod) {
200 return new resource_stream_1.ResourceStream(parsedArguments, originalMethod);
201 }
202}
203exports.Paginator = Paginator;
204const paginator = new Paginator();
205exports.paginator = paginator;
206//# sourceMappingURL=index.js.map
\No newline at end of file