index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*! firebase-admin v12.1.1 */
  2. "use strict";
  3. /*!
  4. * @license
  5. * Copyright 2017 Google Inc.
  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. Object.defineProperty(exports, "__esModule", { value: true });
  20. exports.parseResourceName = exports.transformMillisecondsToSecondsString = exports.generateUpdateMask = exports.formatString = exports.toWebSafeBase64 = exports.findServiceAccountEmail = exports.getExplicitServiceAccountEmail = exports.findProjectId = exports.getExplicitProjectId = exports.addReadonlyGetter = exports.renameProperties = exports.getSdkVersion = void 0;
  21. const credential_internal_1 = require("../app/credential-internal");
  22. const validator = require("./validator");
  23. let sdkVersion;
  24. // TODO: Move to firebase-admin/app as an internal member.
  25. function getSdkVersion() {
  26. if (!sdkVersion) {
  27. const { version } = require('../../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires
  28. sdkVersion = version;
  29. }
  30. return sdkVersion;
  31. }
  32. exports.getSdkVersion = getSdkVersion;
  33. /**
  34. * Renames properties on an object given a mapping from old to new property names.
  35. *
  36. * For example, this can be used to map underscore_cased properties to camelCase.
  37. *
  38. * @param obj - The object whose properties to rename.
  39. * @param keyMap - The mapping from old to new property names.
  40. */
  41. function renameProperties(obj, keyMap) {
  42. Object.keys(keyMap).forEach((oldKey) => {
  43. if (oldKey in obj) {
  44. const newKey = keyMap[oldKey];
  45. // The old key's value takes precedence over the new key's value.
  46. obj[newKey] = obj[oldKey];
  47. delete obj[oldKey];
  48. }
  49. });
  50. }
  51. exports.renameProperties = renameProperties;
  52. /**
  53. * Defines a new read-only property directly on an object and returns the object.
  54. *
  55. * @param obj - The object on which to define the property.
  56. * @param prop - The name of the property to be defined or modified.
  57. * @param value - The value associated with the property.
  58. */
  59. function addReadonlyGetter(obj, prop, value) {
  60. Object.defineProperty(obj, prop, {
  61. value,
  62. // Make this property read-only.
  63. writable: false,
  64. // Include this property during enumeration of obj's properties.
  65. enumerable: true,
  66. });
  67. }
  68. exports.addReadonlyGetter = addReadonlyGetter;
  69. /**
  70. * Returns the Google Cloud project ID associated with a Firebase app, if it's explicitly
  71. * specified in either the Firebase app options, credentials or the local environment.
  72. * Otherwise returns null.
  73. *
  74. * @param app - A Firebase app to get the project ID from.
  75. *
  76. * @returns A project ID string or null.
  77. */
  78. function getExplicitProjectId(app) {
  79. const options = app.options;
  80. if (validator.isNonEmptyString(options.projectId)) {
  81. return options.projectId;
  82. }
  83. const credential = app.options.credential;
  84. if (credential instanceof credential_internal_1.ServiceAccountCredential) {
  85. return credential.projectId;
  86. }
  87. const projectId = process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
  88. if (validator.isNonEmptyString(projectId)) {
  89. return projectId;
  90. }
  91. return null;
  92. }
  93. exports.getExplicitProjectId = getExplicitProjectId;
  94. /**
  95. * Determines the Google Cloud project ID associated with a Firebase app. This method
  96. * first checks if a project ID is explicitly specified in either the Firebase app options,
  97. * credentials or the local environment in that order. If no explicit project ID is
  98. * configured, but the SDK has been initialized with ComputeEngineCredentials, this
  99. * method attempts to discover the project ID from the local metadata service.
  100. *
  101. * @param app - A Firebase app to get the project ID from.
  102. *
  103. * @returns A project ID string or null.
  104. */
  105. function findProjectId(app) {
  106. const projectId = getExplicitProjectId(app);
  107. if (projectId) {
  108. return Promise.resolve(projectId);
  109. }
  110. const credential = app.options.credential;
  111. if (credential instanceof credential_internal_1.ComputeEngineCredential) {
  112. return credential.getProjectId();
  113. }
  114. return Promise.resolve(null);
  115. }
  116. exports.findProjectId = findProjectId;
  117. /**
  118. * Returns the service account email associated with a Firebase app, if it's explicitly
  119. * specified in either the Firebase app options, credentials or the local environment.
  120. * Otherwise returns null.
  121. *
  122. * @param app - A Firebase app to get the service account email from.
  123. *
  124. * @returns A service account email string or null.
  125. */
  126. function getExplicitServiceAccountEmail(app) {
  127. const options = app.options;
  128. if (validator.isNonEmptyString(options.serviceAccountId)) {
  129. return options.serviceAccountId;
  130. }
  131. const credential = app.options.credential;
  132. if (credential instanceof credential_internal_1.ServiceAccountCredential) {
  133. return credential.clientEmail;
  134. }
  135. return null;
  136. }
  137. exports.getExplicitServiceAccountEmail = getExplicitServiceAccountEmail;
  138. /**
  139. * Determines the service account email associated with a Firebase app. This method first
  140. * checks if a service account email is explicitly specified in either the Firebase app options,
  141. * credentials or the local environment in that order. If no explicit service account email is
  142. * configured, but the SDK has been initialized with ComputeEngineCredentials, this
  143. * method attempts to discover the service account email from the local metadata service.
  144. *
  145. * @param app - A Firebase app to get the service account email from.
  146. *
  147. * @returns A service account email ID string or null.
  148. */
  149. function findServiceAccountEmail(app) {
  150. const accountId = getExplicitServiceAccountEmail(app);
  151. if (accountId) {
  152. return Promise.resolve(accountId);
  153. }
  154. const credential = app.options.credential;
  155. if (credential instanceof credential_internal_1.ComputeEngineCredential) {
  156. return credential.getServiceAccountEmail();
  157. }
  158. return Promise.resolve(null);
  159. }
  160. exports.findServiceAccountEmail = findServiceAccountEmail;
  161. /**
  162. * Encodes data using web-safe-base64.
  163. *
  164. * @param data - The raw data byte input.
  165. * @returns The base64-encoded result.
  166. */
  167. function toWebSafeBase64(data) {
  168. return data.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
  169. }
  170. exports.toWebSafeBase64 = toWebSafeBase64;
  171. /**
  172. * Formats a string of form 'project/{projectId}/{api}' and replaces
  173. * with corresponding arguments {projectId: '1234', api: 'resource'}
  174. * and returns output: 'project/1234/resource'.
  175. *
  176. * @param str - The original string where the param need to be
  177. * replaced.
  178. * @param params - The optional parameters to replace in the
  179. * string.
  180. * @returns The resulting formatted string.
  181. */
  182. function formatString(str, params) {
  183. let formatted = str;
  184. Object.keys(params || {}).forEach((key) => {
  185. formatted = formatted.replace(new RegExp('{' + key + '}', 'g'), params[key]);
  186. });
  187. return formatted;
  188. }
  189. exports.formatString = formatString;
  190. /**
  191. * Generates the update mask for the provided object.
  192. * Note this will ignore the last key with value undefined.
  193. *
  194. * @param obj - The object to generate the update mask for.
  195. * @param terminalPaths - The optional map of keys for maximum paths to traverse.
  196. * Nested objects beyond that path will be ignored. This is useful for
  197. * keys with variable object values.
  198. * @param root - The path so far.
  199. * @returns The computed update mask list.
  200. */
  201. function generateUpdateMask(obj, terminalPaths = [], root = '') {
  202. const updateMask = [];
  203. if (!validator.isNonNullObject(obj)) {
  204. return updateMask;
  205. }
  206. for (const key in obj) {
  207. if (typeof obj[key] !== 'undefined') {
  208. const nextPath = root ? `${root}.${key}` : key;
  209. // We hit maximum path.
  210. // Consider switching to Set<string> if the list grows too large.
  211. if (terminalPaths.indexOf(nextPath) !== -1) {
  212. // Add key and stop traversing this branch.
  213. updateMask.push(key);
  214. }
  215. else {
  216. const maskList = generateUpdateMask(obj[key], terminalPaths, nextPath);
  217. if (maskList.length > 0) {
  218. maskList.forEach((mask) => {
  219. updateMask.push(`${key}.${mask}`);
  220. });
  221. }
  222. else {
  223. updateMask.push(key);
  224. }
  225. }
  226. }
  227. }
  228. return updateMask;
  229. }
  230. exports.generateUpdateMask = generateUpdateMask;
  231. /**
  232. * Transforms milliseconds to a protobuf Duration type string.
  233. * Returns the duration in seconds with up to nine fractional
  234. * digits, terminated by 's'. Example: "3 seconds 0 nano seconds as 3s,
  235. * 3 seconds 1 nano seconds as 3.000000001s".
  236. *
  237. * @param milliseconds - The duration in milliseconds.
  238. * @returns The resulting formatted string in seconds with up to nine fractional
  239. * digits, terminated by 's'.
  240. */
  241. function transformMillisecondsToSecondsString(milliseconds) {
  242. let duration;
  243. const seconds = Math.floor(milliseconds / 1000);
  244. const nanos = Math.floor((milliseconds - seconds * 1000) * 1000000);
  245. if (nanos > 0) {
  246. let nanoString = nanos.toString();
  247. while (nanoString.length < 9) {
  248. nanoString = '0' + nanoString;
  249. }
  250. duration = `${seconds}.${nanoString}s`;
  251. }
  252. else {
  253. duration = `${seconds}s`;
  254. }
  255. return duration;
  256. }
  257. exports.transformMillisecondsToSecondsString = transformMillisecondsToSecondsString;
  258. /**
  259. * Parses the top level resources of a given resource name.
  260. * Supports both full and partial resources names, example:
  261. * `locations/{location}/functions/{functionName}`,
  262. * `projects/{project}/locations/{location}/functions/{functionName}`, or {functionName}
  263. * Does not support deeply nested resource names.
  264. *
  265. * @param resourceName - The resource name string.
  266. * @param resourceIdKey - The key of the resource name to be parsed.
  267. * @returns A parsed resource name object.
  268. */
  269. function parseResourceName(resourceName, resourceIdKey) {
  270. if (!resourceName.includes('/')) {
  271. return { resourceId: resourceName };
  272. }
  273. const CHANNEL_NAME_REGEX = new RegExp(`^(projects/([^/]+)/)?locations/([^/]+)/${resourceIdKey}/([^/]+)$`);
  274. const match = CHANNEL_NAME_REGEX.exec(resourceName);
  275. if (match === null) {
  276. throw new Error('Invalid resource name format.');
  277. }
  278. const projectId = match[2];
  279. const locationId = match[3];
  280. const resourceId = match[4];
  281. return { projectId, locationId, resourceId };
  282. }
  283. exports.parseResourceName = parseResourceName;