1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.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 | */
|
19 | const promisify_1 = require("@google-cloud/promisify");
|
20 | const arrify = require("arrify");
|
21 | const events_1 = require("events");
|
22 | const extend = require("extend");
|
23 | const 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
|
36 | class 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 | }
|
277 | exports.ServiceObject = ServiceObject;
|
278 | (0, promisify_1.promisifyAll)(ServiceObject, { exclude: ['getRequestInterceptors'] });
|
279 | //# sourceMappingURL=service-object.js.map |
\ | No newline at end of file |