1 | ;
|
2 | // Copyright 2015 Google LLC
|
3 | //
|
4 | // Licensed under the Apache License, Version 2.0 (the "License");
|
5 | // you may not use this file except in compliance with the License.
|
6 | // You may obtain a copy of the License at
|
7 | //
|
8 | // http://www.apache.org/licenses/LICENSE-2.0
|
9 | //
|
10 | // Unless required by applicable law or agreed to in writing, software
|
11 | // distributed under the License is distributed on an "AS IS" BASIS,
|
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 | // See the License for the specific language governing permissions and
|
14 | // limitations under the License.
|
15 | Object.defineProperty(exports, "__esModule", { value: true });
|
16 | exports.ServiceObject = void 0;
|
17 | /*!
|
18 | * @module common/service-object
|
19 | */
|
20 | const promisify_1 = require("@google-cloud/promisify");
|
21 | const arrify = require("arrify");
|
22 | const events_1 = require("events");
|
23 | const extend = require("extend");
|
24 | const util_1 = require("./util");
|
25 | /**
|
26 | * ServiceObject is a base class, meant to be inherited from by a "service
|
27 | * object," like a BigQuery dataset or Storage bucket.
|
28 | *
|
29 | * Most of the time, these objects share common functionality; they can be
|
30 | * created or deleted, and you can get or set their metadata.
|
31 | *
|
32 | * By inheriting from this class, a service object will be extended with these
|
33 | * shared behaviors. Note that any method can be overridden when the service
|
34 | * object requires specific behavior.
|
35 | */
|
36 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
37 | class ServiceObject extends events_1.EventEmitter {
|
38 | /*
|
39 | * @constructor
|
40 | * @alias module:common/service-object
|
41 | *
|
42 | * @private
|
43 | *
|
44 | * @param {object} config - Configuration object.
|
45 | * @param {string} config.baseUrl - The base URL to make API requests to.
|
46 | * @param {string} config.createMethod - The method which creates this object.
|
47 | * @param {string=} config.id - The identifier of the object. For example, the
|
48 | * name of a Storage bucket or Pub/Sub topic.
|
49 | * @param {object=} config.methods - A map of each method name that should be inherited.
|
50 | * @param {object} config.methods[].reqOpts - Default request options for this
|
51 | * particular method. A common use case is when `setMetadata` requires a
|
52 | * `PUT` method to override the default `PATCH`.
|
53 | * @param {object} config.parent - The parent service instance. For example, an
|
54 | * instance of Storage if the object is Bucket.
|
55 | */
|
56 | constructor(config) {
|
57 | super();
|
58 | this.metadata = {};
|
59 | this.baseUrl = config.baseUrl;
|
60 | this.parent = config.parent; // Parent class.
|
61 | this.id = config.id; // Name or ID (e.g. dataset ID, bucket name, etc).
|
62 | this.createMethod = config.createMethod;
|
63 | this.methods = config.methods || {};
|
64 | this.interceptors = [];
|
65 | this.pollIntervalMs = config.pollIntervalMs;
|
66 | this.projectId = config.projectId;
|
67 | if (config.methods) {
|
68 | // This filters the ServiceObject instance (e.g. a "File") to only have
|
69 | // the configured methods. We make a couple of exceptions for core-
|
70 | // functionality ("request()" and "getRequestInterceptors()")
|
71 | Object.getOwnPropertyNames(ServiceObject.prototype)
|
72 | .filter(methodName => {
|
73 | return (
|
74 | // All ServiceObjects need `request` and `getRequestInterceptors`.
|
75 | // clang-format off
|
76 | !/^request/.test(methodName) &&
|
77 | !/^getRequestInterceptors/.test(methodName) &&
|
78 | // clang-format on
|
79 | // The ServiceObject didn't redefine the method.
|
80 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
81 | this[methodName] ===
|
82 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
83 | ServiceObject.prototype[methodName] &&
|
84 | // This method isn't wanted.
|
85 | !config.methods[methodName]);
|
86 | })
|
87 | .forEach(methodName => {
|
88 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
89 | this[methodName] = undefined;
|
90 | });
|
91 | }
|
92 | }
|
93 | create(optionsOrCallback, callback) {
|
94 | // eslint-disable-next-line @typescript-eslint/no-this-alias
|
95 | const self = this;
|
96 | const args = [this.id];
|
97 | if (typeof optionsOrCallback === 'function') {
|
98 | callback = optionsOrCallback;
|
99 | }
|
100 | if (typeof optionsOrCallback === 'object') {
|
101 | args.push(optionsOrCallback);
|
102 | }
|
103 | // Wrap the callback to return *this* instance of the object, not the
|
104 | // newly-created one.
|
105 | // tslint: disable-next-line no-any
|
106 | function onCreate(...args) {
|
107 | const [err, instance] = args;
|
108 | if (!err) {
|
109 | self.metadata = instance.metadata;
|
110 | args[1] = self; // replace the created `instance` with this one.
|
111 | }
|
112 | callback(...args);
|
113 | }
|
114 | args.push(onCreate);
|
115 | // eslint-disable-next-line prefer-spread
|
116 | this.createMethod.apply(null, args);
|
117 | }
|
118 | delete(optionsOrCallback, cb) {
|
119 | const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
|
120 | const ignoreNotFound = options.ignoreNotFound;
|
121 | delete options.ignoreNotFound;
|
122 | const methodConfig = (typeof this.methods.delete === 'object' && this.methods.delete) || {};
|
123 | const reqOpts = extend(true, {
|
124 | method: 'DELETE',
|
125 | uri: '',
|
126 | }, methodConfig.reqOpts, {
|
127 | qs: options,
|
128 | });
|
129 | // The `request` method may have been overridden to hold any special
|
130 | // behavior. Ensure we call the original `request` method.
|
131 | ServiceObject.prototype.request.call(this, reqOpts, (err, ...args) => {
|
132 | if (err) {
|
133 | if (err.code === 404 && ignoreNotFound) {
|
134 | err = null;
|
135 | }
|
136 | }
|
137 | callback(err, ...args);
|
138 | });
|
139 | }
|
140 | exists(optionsOrCallback, cb) {
|
141 | const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
|
142 | this.get(options, err => {
|
143 | if (err) {
|
144 | if (err.code === 404) {
|
145 | callback(null, false);
|
146 | }
|
147 | else {
|
148 | callback(err);
|
149 | }
|
150 | return;
|
151 | }
|
152 | callback(null, true);
|
153 | });
|
154 | }
|
155 | get(optionsOrCallback, cb) {
|
156 | // eslint-disable-next-line @typescript-eslint/no-this-alias
|
157 | const self = this;
|
158 | const [opts, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
|
159 | const options = Object.assign({}, opts);
|
160 | const autoCreate = options.autoCreate && typeof this.create === 'function';
|
161 | delete options.autoCreate;
|
162 | function onCreate(err, instance, apiResponse) {
|
163 | if (err) {
|
164 | if (err.code === 409) {
|
165 | self.get(options, callback);
|
166 | return;
|
167 | }
|
168 | callback(err, null, apiResponse);
|
169 | return;
|
170 | }
|
171 | callback(null, instance, apiResponse);
|
172 | }
|
173 | this.getMetadata(options, (err, metadata) => {
|
174 | if (err) {
|
175 | if (err.code === 404 && autoCreate) {
|
176 | const args = [];
|
177 | if (Object.keys(options).length > 0) {
|
178 | args.push(options);
|
179 | }
|
180 | args.push(onCreate);
|
181 | self.create(...args);
|
182 | return;
|
183 | }
|
184 | callback(err, null, metadata);
|
185 | return;
|
186 | }
|
187 | callback(null, self, metadata);
|
188 | });
|
189 | }
|
190 | getMetadata(optionsOrCallback, cb) {
|
191 | const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
|
192 | const methodConfig = (typeof this.methods.getMetadata === 'object' &&
|
193 | this.methods.getMetadata) ||
|
194 | {};
|
195 | const reqOpts = extend(true, {
|
196 | uri: '',
|
197 | }, methodConfig.reqOpts, {
|
198 | qs: options,
|
199 | });
|
200 | // The `request` method may have been overridden to hold any special
|
201 | // behavior. Ensure we call the original `request` method.
|
202 | ServiceObject.prototype.request.call(this, reqOpts, (err, body, res) => {
|
203 | this.metadata = body;
|
204 | callback(err, this.metadata, res);
|
205 | });
|
206 | }
|
207 | /**
|
208 | * Return the user's custom request interceptors.
|
209 | */
|
210 | getRequestInterceptors() {
|
211 | // Interceptors should be returned in the order they were assigned.
|
212 | const localInterceptors = this.interceptors
|
213 | .filter(interceptor => typeof interceptor.request === 'function')
|
214 | .map(interceptor => interceptor.request);
|
215 | return this.parent.getRequestInterceptors().concat(localInterceptors);
|
216 | }
|
217 | setMetadata(metadata, optionsOrCallback, cb) {
|
218 | const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
|
219 | const methodConfig = (typeof this.methods.setMetadata === 'object' &&
|
220 | this.methods.setMetadata) ||
|
221 | {};
|
222 | const reqOpts = extend(true, {}, {
|
223 | method: 'PATCH',
|
224 | uri: '',
|
225 | }, methodConfig.reqOpts, {
|
226 | json: metadata,
|
227 | qs: options,
|
228 | });
|
229 | // The `request` method may have been overridden to hold any special
|
230 | // behavior. Ensure we call the original `request` method.
|
231 | ServiceObject.prototype.request.call(this, reqOpts, (err, body, res) => {
|
232 | this.metadata = body;
|
233 | callback(err, this.metadata, res);
|
234 | });
|
235 | }
|
236 | request_(reqOpts, callback) {
|
237 | reqOpts = extend(true, {}, reqOpts);
|
238 | if (this.projectId) {
|
239 | reqOpts.projectId = this.projectId;
|
240 | }
|
241 | const isAbsoluteUrl = reqOpts.uri.indexOf('http') === 0;
|
242 | const uriComponents = [this.baseUrl, this.id || '', reqOpts.uri];
|
243 | if (isAbsoluteUrl) {
|
244 | uriComponents.splice(0, uriComponents.indexOf(reqOpts.uri));
|
245 | }
|
246 | reqOpts.uri = uriComponents
|
247 | .filter(x => x.trim()) // Limit to non-empty strings.
|
248 | .map(uriComponent => {
|
249 | const trimSlashesRegex = /^\/*|\/*$/g;
|
250 | return uriComponent.replace(trimSlashesRegex, '');
|
251 | })
|
252 | .join('/');
|
253 | const childInterceptors = arrify(reqOpts.interceptors_);
|
254 | const localInterceptors = [].slice.call(this.interceptors);
|
255 | reqOpts.interceptors_ = childInterceptors.concat(localInterceptors);
|
256 | if (reqOpts.shouldReturnStream) {
|
257 | return this.parent.requestStream(reqOpts);
|
258 | }
|
259 | this.parent.request(reqOpts, callback);
|
260 | }
|
261 | request(reqOpts, callback) {
|
262 | this.request_(reqOpts, callback);
|
263 | }
|
264 | /**
|
265 | * Make an authenticated API request.
|
266 | *
|
267 | * @param {object} reqOpts - Request options that are passed to `request`.
|
268 | * @param {string} reqOpts.uri - A URI relative to the baseUrl.
|
269 | */
|
270 | requestStream(reqOpts) {
|
271 | const opts = extend(true, reqOpts, { shouldReturnStream: true });
|
272 | return this.request_(opts);
|
273 | }
|
274 | }
|
275 | exports.ServiceObject = ServiceObject;
|
276 | (0, promisify_1.promisifyAll)(ServiceObject, { exclude: ['getRequestInterceptors'] });
|
277 | //# sourceMappingURL=service-object.js.map |
\ | No newline at end of file |