UNPKG

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