UNPKG

11.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.ServiceObject = void 0;
4/*!
5 * Copyright 2022 Google LLC. All Rights Reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
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.projectId = config.projectId;
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 if (self.id && instance.metadata) {
109 self.id = instance.metadata.id;
110 }
111 args[1] = self; // replace the created `instance` with this one.
112 }
113 callback(...args);
114 }
115 args.push(onCreate);
116 // eslint-disable-next-line prefer-spread
117 this.createMethod.apply(null, args);
118 }
119 delete(optionsOrCallback, cb) {
120 const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
121 const ignoreNotFound = options.ignoreNotFound;
122 delete options.ignoreNotFound;
123 const methodConfig = (typeof this.methods.delete === 'object' && this.methods.delete) || {};
124 const reqOpts = extend(true, {
125 method: 'DELETE',
126 uri: '',
127 }, methodConfig.reqOpts, {
128 qs: options,
129 });
130 // The `request` method may have been overridden to hold any special
131 // behavior. Ensure we call the original `request` method.
132 ServiceObject.prototype.request.call(this, reqOpts, (err, ...args) => {
133 if (err) {
134 if (err.code === 404 && ignoreNotFound) {
135 err = null;
136 }
137 }
138 // eslint-disable-next-line @typescript-eslint/no-explicit-any
139 callback(err, ...args);
140 });
141 }
142 exists(optionsOrCallback, cb) {
143 const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
144 this.get(options, err => {
145 if (err) {
146 if (err.code === 404) {
147 callback(null, false);
148 }
149 else {
150 callback(err);
151 }
152 return;
153 }
154 callback(null, true);
155 });
156 }
157 get(optionsOrCallback, cb) {
158 // eslint-disable-next-line @typescript-eslint/no-this-alias
159 const self = this;
160 const [opts, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
161 const options = Object.assign({}, opts);
162 const autoCreate = options.autoCreate && typeof this.create === 'function';
163 delete options.autoCreate;
164 function onCreate(err, instance, apiResponse) {
165 if (err) {
166 if (err.code === 409) {
167 self.get(options, callback);
168 return;
169 }
170 callback(err, null, apiResponse);
171 return;
172 }
173 callback(null, instance, apiResponse);
174 }
175 this.getMetadata(options, (err, metadata) => {
176 if (err) {
177 if (err.code === 404 && autoCreate) {
178 const args = [];
179 if (Object.keys(options).length > 0) {
180 args.push(options);
181 }
182 args.push(onCreate);
183 self.create(...args);
184 return;
185 }
186 callback(err, null, metadata);
187 return;
188 }
189 callback(null, self, metadata);
190 });
191 }
192 getMetadata(optionsOrCallback, cb) {
193 const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
194 const methodConfig = (typeof this.methods.getMetadata === 'object' &&
195 this.methods.getMetadata) ||
196 {};
197 const reqOpts = extend(true, {
198 uri: '',
199 }, methodConfig.reqOpts, {
200 qs: options,
201 });
202 // The `request` method may have been overridden to hold any special
203 // behavior. Ensure we call the original `request` method.
204 ServiceObject.prototype.request.call(this, reqOpts, (err, body, res) => {
205 this.metadata = body;
206 callback(err, this.metadata, res);
207 });
208 }
209 /**
210 * Return the user's custom request interceptors.
211 */
212 getRequestInterceptors() {
213 // Interceptors should be returned in the order they were assigned.
214 const localInterceptors = this.interceptors
215 .filter(interceptor => typeof interceptor.request === 'function')
216 .map(interceptor => interceptor.request);
217 return this.parent.getRequestInterceptors().concat(localInterceptors);
218 }
219 setMetadata(metadata, optionsOrCallback, cb) {
220 const [options, callback] = util_1.util.maybeOptionsOrCallback(optionsOrCallback, cb);
221 const methodConfig = (typeof this.methods.setMetadata === 'object' &&
222 this.methods.setMetadata) ||
223 {};
224 const reqOpts = extend(true, {}, {
225 method: 'PATCH',
226 uri: '',
227 }, methodConfig.reqOpts, {
228 json: metadata,
229 qs: options,
230 });
231 // The `request` method may have been overridden to hold any special
232 // behavior. Ensure we call the original `request` method.
233 ServiceObject.prototype.request.call(this, reqOpts, (err, body, res) => {
234 this.metadata = body;
235 callback(err, this.metadata, res);
236 });
237 }
238 request_(reqOpts, callback) {
239 reqOpts = extend(true, {}, reqOpts);
240 if (this.projectId) {
241 reqOpts.projectId = this.projectId;
242 }
243 const isAbsoluteUrl = reqOpts.uri.indexOf('http') === 0;
244 const uriComponents = [this.baseUrl, this.id || '', reqOpts.uri];
245 if (isAbsoluteUrl) {
246 uriComponents.splice(0, uriComponents.indexOf(reqOpts.uri));
247 }
248 reqOpts.uri = uriComponents
249 .filter(x => x.trim()) // Limit to non-empty strings.
250 .map(uriComponent => {
251 const trimSlashesRegex = /^\/*|\/*$/g;
252 return uriComponent.replace(trimSlashesRegex, '');
253 })
254 .join('/');
255 const childInterceptors = arrify(reqOpts.interceptors_);
256 const localInterceptors = [].slice.call(this.interceptors);
257 reqOpts.interceptors_ = childInterceptors.concat(localInterceptors);
258 if (reqOpts.shouldReturnStream) {
259 return this.parent.requestStream(reqOpts);
260 }
261 this.parent.request(reqOpts, callback);
262 }
263 request(reqOpts, callback) {
264 this.request_(reqOpts, callback);
265 }
266 /**
267 * Make an authenticated API request.
268 *
269 * @param {object} reqOpts - Request options that are passed to `request`.
270 * @param {string} reqOpts.uri - A URI relative to the baseUrl.
271 */
272 requestStream(reqOpts) {
273 const opts = extend(true, reqOpts, { shouldReturnStream: true });
274 return this.request_(opts);
275 }
276}
277exports.ServiceObject = ServiceObject;
278(0, promisify_1.promisifyAll)(ServiceObject, { exclude: ['getRequestInterceptors'] });
279//# sourceMappingURL=service-object.js.map
\No newline at end of file