UNPKG

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