project-management-api-request-internal.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*! firebase-admin v12.1.1 */
  2. "use strict";
  3. /*!
  4. * Copyright 2018 Google Inc.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. Object.defineProperty(exports, "__esModule", { value: true });
  19. exports.ProjectManagementRequestHandler = exports.assertServerResponse = void 0;
  20. const api_request_1 = require("../utils/api-request");
  21. const error_1 = require("../utils/error");
  22. const index_1 = require("../utils/index");
  23. const validator = require("../utils/validator");
  24. /** Project management backend host and port. */
  25. const PROJECT_MANAGEMENT_HOST_AND_PORT = 'firebase.googleapis.com:443';
  26. /** Project management backend path. */
  27. const PROJECT_MANAGEMENT_PATH = '/v1/';
  28. /** Project management beta backend path. */
  29. const PROJECT_MANAGEMENT_BETA_PATH = '/v1beta1/';
  30. /** Project management request header. */
  31. const PROJECT_MANAGEMENT_HEADERS = {
  32. 'X-Client-Version': `Node/Admin/${(0, index_1.getSdkVersion)()}`,
  33. };
  34. /** Project management request timeout duration in milliseconds. */
  35. const PROJECT_MANAGEMENT_TIMEOUT_MILLIS = 10000;
  36. const LIST_APPS_MAX_PAGE_SIZE = 100;
  37. const CERT_TYPE_API_MAP = {
  38. sha1: 'SHA_1',
  39. sha256: 'SHA_256',
  40. };
  41. function assertServerResponse(condition, responseData, message) {
  42. if (!condition) {
  43. throw new error_1.FirebaseProjectManagementError('invalid-server-response', `${message} Response data: ${JSON.stringify(responseData, null, 2)}`);
  44. }
  45. }
  46. exports.assertServerResponse = assertServerResponse;
  47. /**
  48. * Class that provides mechanism to send requests to the Firebase project management backend
  49. * endpoints.
  50. *
  51. * @internal
  52. */
  53. class ProjectManagementRequestHandler {
  54. static wrapAndRethrowHttpError(errStatusCode, errText) {
  55. let errorCode;
  56. let errorMessage;
  57. switch (errStatusCode) {
  58. case 400:
  59. errorCode = 'invalid-argument';
  60. errorMessage = 'Invalid argument provided.';
  61. break;
  62. case 401:
  63. case 403:
  64. errorCode = 'authentication-error';
  65. errorMessage = 'An error occurred when trying to authenticate. Make sure the credential '
  66. + 'used to authenticate this SDK has the proper permissions. See '
  67. + 'https://firebase.google.com/docs/admin/setup for setup instructions.';
  68. break;
  69. case 404:
  70. errorCode = 'not-found';
  71. errorMessage = 'The specified entity could not be found.';
  72. break;
  73. case 409:
  74. errorCode = 'already-exists';
  75. errorMessage = 'The specified entity already exists.';
  76. break;
  77. case 500:
  78. errorCode = 'internal-error';
  79. errorMessage = 'An internal error has occurred. Please retry the request.';
  80. break;
  81. case 503:
  82. errorCode = 'service-unavailable';
  83. errorMessage = 'The server could not process the request in time. See the error '
  84. + 'documentation for more details.';
  85. break;
  86. default:
  87. errorCode = 'unknown-error';
  88. errorMessage = 'An unknown server error was returned.';
  89. }
  90. if (!errText) {
  91. errText = '<missing>';
  92. }
  93. throw new error_1.FirebaseProjectManagementError(errorCode, `${errorMessage} Status code: ${errStatusCode}. Raw server response: "${errText}".`);
  94. }
  95. /**
  96. * @param app - The app used to fetch access tokens to sign API requests.
  97. * @constructor
  98. */
  99. constructor(app) {
  100. this.baseUrl = `https://${PROJECT_MANAGEMENT_HOST_AND_PORT}${PROJECT_MANAGEMENT_PATH}`;
  101. this.baseBetaUrl = `https://${PROJECT_MANAGEMENT_HOST_AND_PORT}${PROJECT_MANAGEMENT_BETA_PATH}`;
  102. this.httpClient = new api_request_1.AuthorizedHttpClient(app);
  103. }
  104. /**
  105. * @param parentResourceName - Fully-qualified resource name of the project whose Android
  106. * apps you want to list.
  107. */
  108. listAndroidApps(parentResourceName) {
  109. return this.invokeRequestHandler('GET', `${parentResourceName}/androidApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`,
  110. /* requestData */ null, 'v1beta1');
  111. }
  112. /**
  113. * @param parentResourceName - Fully-qualified resource name of the project whose iOS apps
  114. * you want to list.
  115. */
  116. listIosApps(parentResourceName) {
  117. return this.invokeRequestHandler('GET', `${parentResourceName}/iosApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`,
  118. /* requestData */ null, 'v1beta1');
  119. }
  120. /**
  121. * @param parentResourceName - Fully-qualified resource name of the project whose iOS apps
  122. * you want to list.
  123. */
  124. listAppMetadata(parentResourceName) {
  125. return this.invokeRequestHandler('GET', `${parentResourceName}:searchApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`,
  126. /* requestData */ null, 'v1beta1');
  127. }
  128. /**
  129. * @param parentResourceName - Fully-qualified resource name of the project that you want
  130. * to create the Android app within.
  131. */
  132. createAndroidApp(parentResourceName, packageName, displayName) {
  133. const requestData = {
  134. packageName,
  135. };
  136. if (validator.isNonEmptyString(displayName)) {
  137. requestData.displayName = displayName;
  138. }
  139. return this
  140. .invokeRequestHandler('POST', `${parentResourceName}/androidApps`, requestData, 'v1beta1')
  141. .then((responseData) => {
  142. assertServerResponse(validator.isNonNullObject(responseData), responseData, 'createAndroidApp\'s responseData must be a non-null object.');
  143. assertServerResponse(validator.isNonEmptyString(responseData.name), responseData, 'createAndroidApp\'s responseData.name must be a non-empty string.');
  144. return this.pollRemoteOperationWithExponentialBackoff(responseData.name);
  145. });
  146. }
  147. /**
  148. * @param parentResourceName - Fully-qualified resource name of the project that you want
  149. * to create the iOS app within.
  150. */
  151. createIosApp(parentResourceName, bundleId, displayName) {
  152. const requestData = {
  153. bundleId,
  154. };
  155. if (validator.isNonEmptyString(displayName)) {
  156. requestData.displayName = displayName;
  157. }
  158. return this
  159. .invokeRequestHandler('POST', `${parentResourceName}/iosApps`, requestData, 'v1beta1')
  160. .then((responseData) => {
  161. assertServerResponse(validator.isNonNullObject(responseData), responseData, 'createIosApp\'s responseData must be a non-null object.');
  162. assertServerResponse(validator.isNonEmptyString(responseData.name), responseData, 'createIosApp\'s responseData.name must be a non-empty string.');
  163. return this.pollRemoteOperationWithExponentialBackoff(responseData.name);
  164. });
  165. }
  166. /**
  167. * @param resourceName - Fully-qualified resource name of the entity whose display name you
  168. * want to set.
  169. */
  170. setDisplayName(resourceName, newDisplayName) {
  171. const requestData = {
  172. displayName: newDisplayName,
  173. };
  174. return this
  175. .invokeRequestHandler('PATCH', `${resourceName}?update_mask=display_name`, requestData, 'v1beta1')
  176. .then(() => undefined);
  177. }
  178. /**
  179. * @param parentResourceName - Fully-qualified resource name of the Android app whose SHA
  180. * certificates you want to get.
  181. */
  182. getAndroidShaCertificates(parentResourceName) {
  183. return this.invokeRequestHandler('GET', `${parentResourceName}/sha`, /* requestData */ null, 'v1beta1');
  184. }
  185. /**
  186. * @param parentResourceName - Fully-qualified resource name of the Android app that you
  187. * want to add the given SHA certificate to.
  188. */
  189. addAndroidShaCertificate(parentResourceName, certificate) {
  190. const requestData = {
  191. shaHash: certificate.shaHash,
  192. certType: CERT_TYPE_API_MAP[certificate.certType],
  193. };
  194. return this
  195. .invokeRequestHandler('POST', `${parentResourceName}/sha`, requestData, 'v1beta1')
  196. .then(() => undefined);
  197. }
  198. /**
  199. * @param parentResourceName - Fully-qualified resource name of the app whose config you
  200. * want to get.
  201. */
  202. getConfig(parentResourceName) {
  203. return this.invokeRequestHandler('GET', `${parentResourceName}/config`, /* requestData */ null, 'v1beta1');
  204. }
  205. /**
  206. * @param parentResourceName - Fully-qualified resource name of the entity that you want to
  207. * get.
  208. */
  209. getResource(parentResourceName) {
  210. return this.invokeRequestHandler('GET', parentResourceName, /* requestData */ null, 'v1beta1');
  211. }
  212. /**
  213. * @param resourceName - Fully-qualified resource name of the entity that you want to
  214. * delete.
  215. */
  216. deleteResource(resourceName) {
  217. return this
  218. .invokeRequestHandler('DELETE', resourceName, /* requestData */ null, 'v1beta1')
  219. .then(() => undefined);
  220. }
  221. pollRemoteOperationWithExponentialBackoff(operationResourceName) {
  222. const poller = new api_request_1.ExponentialBackoffPoller();
  223. return poller.poll(() => {
  224. return this.invokeRequestHandler('GET', operationResourceName, /* requestData */ null)
  225. .then((responseData) => {
  226. if (responseData.error) {
  227. const errStatusCode = responseData.error.code || 500;
  228. const errText = responseData.error.message || JSON.stringify(responseData.error);
  229. ProjectManagementRequestHandler.wrapAndRethrowHttpError(errStatusCode, errText);
  230. }
  231. if (!responseData.done) {
  232. // Continue polling.
  233. return null;
  234. }
  235. // Polling complete. Resolve with operation response JSON.
  236. return responseData.response;
  237. });
  238. });
  239. }
  240. /**
  241. * Invokes the request handler with the provided request data.
  242. */
  243. invokeRequestHandler(method, path, requestData, apiVersion = 'v1') {
  244. const baseUrlToUse = (apiVersion === 'v1') ? this.baseUrl : this.baseBetaUrl;
  245. const request = {
  246. method,
  247. url: `${baseUrlToUse}${path}`,
  248. headers: PROJECT_MANAGEMENT_HEADERS,
  249. data: requestData,
  250. timeout: PROJECT_MANAGEMENT_TIMEOUT_MILLIS,
  251. };
  252. return this.httpClient.send(request)
  253. .then((response) => {
  254. // Send non-JSON responses to the catch() below, where they will be treated as errors.
  255. if (!response.isJson()) {
  256. throw new api_request_1.HttpError(response);
  257. }
  258. return response.data;
  259. })
  260. .catch((err) => {
  261. if (err instanceof api_request_1.HttpError) {
  262. ProjectManagementRequestHandler.wrapAndRethrowHttpError(err.response.status, err.response.text);
  263. }
  264. throw err;
  265. });
  266. }
  267. }
  268. exports.ProjectManagementRequestHandler = ProjectManagementRequestHandler;