firebase-tools 10.1.2 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/lib/api.js +1 -0
  2. package/lib/apiv2.js +92 -48
  3. package/lib/archiveDirectory.js +63 -73
  4. package/lib/auth.js +62 -25
  5. package/lib/commands/ext-configure.js +1 -0
  6. package/lib/commands/ext-dev-usage.js +3 -8
  7. package/lib/commands/ext-install.js +1 -0
  8. package/lib/commands/ext-uninstall.js +1 -0
  9. package/lib/commands/ext-update.js +1 -0
  10. package/lib/commands/functions-secrets-access.js +17 -0
  11. package/lib/commands/functions-secrets-destroy.js +40 -0
  12. package/lib/commands/functions-secrets-get.js +21 -0
  13. package/lib/commands/functions-secrets-prune.js +50 -0
  14. package/lib/commands/functions-secrets-set.js +46 -0
  15. package/lib/commands/index.js +7 -3
  16. package/lib/commands/login.js +1 -1
  17. package/lib/database/metadata.js +16 -24
  18. package/lib/deploy/functions/backend.js +11 -1
  19. package/lib/deploy/functions/ensure.js +112 -0
  20. package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
  21. package/lib/deploy/functions/prepare.js +13 -19
  22. package/lib/deploy/functions/release/fabricator.js +4 -1
  23. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -0
  24. package/lib/deploy/functions/runtimes/node/parseTriggers.js +12 -0
  25. package/lib/deploy/functions/validate.js +83 -1
  26. package/lib/deploy/hosting/convertConfig.js +45 -24
  27. package/lib/deploy/hosting/prepare.js +1 -1
  28. package/lib/emulator/controller.js +3 -1
  29. package/lib/emulator/emulatorLogger.js +7 -0
  30. package/lib/emulator/functionsEmulator.js +113 -79
  31. package/lib/emulator/functionsEmulatorRuntime.js +100 -83
  32. package/lib/emulator/functionsEmulatorShared.js +51 -1
  33. package/lib/emulator/functionsEmulatorShell.js +1 -2
  34. package/lib/emulator/functionsRuntimeWorker.js +1 -1
  35. package/lib/emulator/storage/apis/gcloud.js +2 -2
  36. package/lib/emulator/storage/files.js +8 -3
  37. package/lib/extensions/askUserForParam.js +1 -1
  38. package/lib/extensions/diagnose.js +56 -0
  39. package/lib/extensions/extensionsApi.js +0 -1
  40. package/lib/extensions/extensionsHelper.js +10 -17
  41. package/lib/extensions/resolveSource.js +1 -53
  42. package/lib/extensions/secretsUtils.js +1 -1
  43. package/lib/extensions/updateHelper.js +0 -14
  44. package/lib/extensions/utils.js +4 -2
  45. package/lib/functions/env.js +5 -7
  46. package/lib/functions/secrets.js +112 -0
  47. package/lib/gcp/cloudbilling.js +8 -19
  48. package/lib/gcp/cloudfunctions.js +24 -48
  49. package/lib/gcp/cloudlogging.js +8 -11
  50. package/lib/gcp/cloudmonitoring.js +8 -5
  51. package/lib/gcp/cloudscheduler.js +7 -18
  52. package/lib/gcp/firedata.js +5 -4
  53. package/lib/gcp/firestore.js +5 -5
  54. package/lib/gcp/iam.js +18 -33
  55. package/lib/gcp/resourceManager.js +8 -13
  56. package/lib/gcp/runtimeconfig.js +31 -53
  57. package/lib/gcp/secretManager.js +137 -77
  58. package/lib/gcp/storage.js +25 -29
  59. package/lib/previews.js +1 -1
  60. package/lib/serve/functions.js +2 -2
  61. package/lib/utils.js +6 -1
  62. package/npm-shrinkwrap.json +962 -987
  63. package/package.json +5 -3
  64. package/schema/firebase-config.json +387 -12
  65. package/templates/init/hosting/index.html +1 -1
package/lib/gcp/iam.js CHANGED
@@ -1,72 +1,57 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.testIamPermissions = exports.testResourceIamPermissions = exports.getRole = exports.deleteServiceAccount = exports.createServiceAccountKey = exports.getServiceAccount = exports.createServiceAccount = void 0;
4
- const api = require("../api");
5
- const utils_1 = require("../utils");
4
+ const api_1 = require("../api");
6
5
  const lodash_1 = require("lodash");
7
6
  const logger_1 = require("../logger");
7
+ const apiv2_1 = require("../apiv2");
8
8
  const API_VERSION = "v1";
9
+ const apiClient = new apiv2_1.Client({ urlPrefix: api_1.iamOrigin, apiVersion: API_VERSION });
9
10
  async function createServiceAccount(projectId, accountId, description, displayName) {
10
- const response = await api.request("POST", `/${API_VERSION}/projects/${projectId}/serviceAccounts`, {
11
- auth: true,
12
- origin: api.iamOrigin,
13
- data: {
14
- accountId,
15
- serviceAccount: {
16
- displayName,
17
- description,
18
- },
11
+ const response = await apiClient.post(`/projects/${projectId}/serviceAccounts`, {
12
+ accountId,
13
+ serviceAccount: {
14
+ displayName,
15
+ description,
19
16
  },
20
17
  });
21
18
  return response.body;
22
19
  }
23
20
  exports.createServiceAccount = createServiceAccount;
24
21
  async function getServiceAccount(projectId, serviceAccountName) {
25
- const response = await api.request("GET", `/${API_VERSION}/projects/${projectId}/serviceAccounts/${serviceAccountName}@${projectId}.iam.gserviceaccount.com`, {
26
- auth: true,
27
- origin: api.iamOrigin,
28
- });
22
+ const response = await apiClient.get(`/projects/${projectId}/serviceAccounts/${serviceAccountName}@${projectId}.iam.gserviceaccount.com`);
29
23
  return response.body;
30
24
  }
31
25
  exports.getServiceAccount = getServiceAccount;
32
26
  async function createServiceAccountKey(projectId, serviceAccountName) {
33
- const response = await api.request("POST", `/${API_VERSION}/projects/${projectId}/serviceAccounts/${serviceAccountName}@${projectId}.iam.gserviceaccount.com/keys`, {
34
- auth: true,
35
- origin: api.iamOrigin,
36
- data: {
37
- keyAlgorithm: "KEY_ALG_UNSPECIFIED",
38
- privateKeyType: "TYPE_GOOGLE_CREDENTIALS_FILE",
39
- },
27
+ const response = await apiClient.post(`/projects/${projectId}/serviceAccounts/${serviceAccountName}@${projectId}.iam.gserviceaccount.com/keys`, {
28
+ keyAlgorithm: "KEY_ALG_UNSPECIFIED",
29
+ privateKeyType: "TYPE_GOOGLE_CREDENTIALS_FILE",
40
30
  });
41
31
  return response.body;
42
32
  }
43
33
  exports.createServiceAccountKey = createServiceAccountKey;
44
34
  function deleteServiceAccount(projectId, accountEmail) {
45
- return api.request("DELETE", `/${API_VERSION}/projects/${projectId}/serviceAccounts/${accountEmail}`, {
46
- auth: true,
47
- origin: api.iamOrigin,
35
+ return apiClient.delete(`/projects/${projectId}/serviceAccounts/${accountEmail}`, {
48
36
  resolveOnHTTPError: true,
49
37
  });
50
38
  }
51
39
  exports.deleteServiceAccount = deleteServiceAccount;
52
40
  async function getRole(role) {
53
- const response = await api.request("GET", (0, utils_1.endpoint)([API_VERSION, "roles", role]), {
54
- auth: true,
55
- origin: api.iamOrigin,
41
+ const response = await apiClient.get(`/roles/${role}`, {
56
42
  retryCodes: [500, 503],
57
43
  });
58
44
  return response.body;
59
45
  }
60
46
  exports.getRole = getRole;
61
47
  async function testResourceIamPermissions(origin, apiVersion, resourceName, permissions) {
48
+ const localClient = new apiv2_1.Client({ urlPrefix: origin, apiVersion });
62
49
  if (process.env.FIREBASE_SKIP_INFORMATIONAL_IAM) {
63
50
  logger_1.logger.debug("[iam] skipping informational check of permissions", JSON.stringify(permissions), "on resource", resourceName);
64
51
  return { allowed: permissions, missing: [], passed: true };
65
52
  }
66
- const response = await api.request("POST", `/${apiVersion}/${resourceName}:testIamPermissions`, {
67
- auth: true,
68
- data: { permissions },
69
- origin,
53
+ const response = await localClient.post(`/${resourceName}:testIamPermissions`, {
54
+ permissions,
70
55
  });
71
56
  const allowed = (response.body.permissions || []).sort();
72
57
  const missing = (0, lodash_1.difference)(permissions, allowed);
@@ -78,6 +63,6 @@ async function testResourceIamPermissions(origin, apiVersion, resourceName, perm
78
63
  }
79
64
  exports.testResourceIamPermissions = testResourceIamPermissions;
80
65
  async function testIamPermissions(projectId, permissions) {
81
- return testResourceIamPermissions(api.resourceManagerOrigin, "v1", `projects/${projectId}`, permissions);
66
+ return testResourceIamPermissions(api_1.resourceManagerOrigin, "v1", `projects/${projectId}`, permissions);
82
67
  }
83
68
  exports.testIamPermissions = testIamPermissions;
@@ -2,9 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addServiceAccountToRoles = exports.setIamPolicy = exports.getIamPolicy = exports.firebaseRoles = void 0;
4
4
  const lodash_1 = require("lodash");
5
- const api = require("../api");
5
+ const api_1 = require("../api");
6
+ const apiv2_1 = require("../apiv2");
6
7
  const iam_1 = require("./iam");
7
8
  const API_VERSION = "v1";
9
+ const apiClient = new apiv2_1.Client({ urlPrefix: api_1.resourceManagerOrigin, apiVersion: API_VERSION });
8
10
  exports.firebaseRoles = {
9
11
  apiKeysViewer: "roles/serviceusage.apiKeysViewer",
10
12
  authAdmin: "roles/firebaseauth.admin",
@@ -12,21 +14,14 @@ exports.firebaseRoles = {
12
14
  runViewer: "roles/run.viewer",
13
15
  };
14
16
  async function getIamPolicy(projectId) {
15
- const response = await api.request("POST", `/${API_VERSION}/projects/${projectId}:getIamPolicy`, {
16
- auth: true,
17
- origin: api.resourceManagerOrigin,
18
- });
17
+ const response = await apiClient.post(`/projects/${projectId}:getIamPolicy`);
19
18
  return response.body;
20
19
  }
21
20
  exports.getIamPolicy = getIamPolicy;
22
- async function setIamPolicy(projectId, newPolicy, updateMask) {
23
- const response = await api.request("POST", `/${API_VERSION}/projects/${projectId}:setIamPolicy`, {
24
- auth: true,
25
- origin: api.resourceManagerOrigin,
26
- data: {
27
- policy: newPolicy,
28
- updateMask: updateMask,
29
- },
21
+ async function setIamPolicy(projectId, newPolicy, updateMask = "") {
22
+ const response = await apiClient.post(`/projects/${projectId}:setIamPolicy`, {
23
+ policy: newPolicy,
24
+ updateMask: updateMask,
30
25
  });
31
26
  return response.body;
32
27
  }
@@ -1,30 +1,25 @@
1
1
  "use strict";
2
- var api = require("../api");
3
- var utils = require("../utils");
2
+ const { runtimeconfigOrigin } = require("../api");
3
+ const { Client } = require("../apiv2");
4
4
  const { logger } = require("../logger");
5
5
  var _ = require("lodash");
6
- var API_VERSION = "v1beta1";
6
+ const API_VERSION = "v1beta1";
7
+ const apiClient = new Client({ urlPrefix: runtimeconfigOrigin, apiVersion: API_VERSION });
7
8
  function _listConfigs(projectId) {
8
- return api
9
- .request("GET", utils.endpoint([API_VERSION, "projects", projectId, "configs"]), {
10
- auth: true,
11
- origin: api.runtimeconfigOrigin,
9
+ return apiClient
10
+ .get(`/projects/${projectId}/configs`, {
12
11
  retryCodes: [500, 503],
13
12
  })
14
13
  .then(function (resp) {
15
- return Promise.resolve(resp.body.configs);
14
+ return resp.body.configs;
16
15
  });
17
16
  }
18
17
  function _createConfig(projectId, configId) {
19
18
  var path = _.join(["projects", projectId, "configs"], "/");
20
- var endpoint = utils.endpoint([API_VERSION, path]);
21
- return api
22
- .request("POST", endpoint, {
23
- auth: true,
24
- origin: api.runtimeconfigOrigin,
25
- data: {
26
- name: path + "/" + configId,
27
- },
19
+ return apiClient
20
+ .post(`/projects/${projectId}/configs`, {
21
+ name: path + "/" + configId,
22
+ }, {
28
23
  retryCodes: [500, 503],
29
24
  })
30
25
  .catch(function (err) {
@@ -35,10 +30,8 @@ function _createConfig(projectId, configId) {
35
30
  });
36
31
  }
37
32
  function _deleteConfig(projectId, configId) {
38
- return api
39
- .request("DELETE", utils.endpoint([API_VERSION, "projects", projectId, "configs", configId]), {
40
- auth: true,
41
- origin: api.runtimeconfigOrigin,
33
+ return apiClient
34
+ .delete(`/projects/${projectId}/configs/${configId}`, {
42
35
  retryCodes: [500, 503],
43
36
  })
44
37
  .catch(function (err) {
@@ -50,10 +43,8 @@ function _deleteConfig(projectId, configId) {
50
43
  });
51
44
  }
52
45
  function _listVariables(configPath) {
53
- return api
54
- .request("GET", utils.endpoint([API_VERSION, configPath, "variables"]), {
55
- auth: true,
56
- origin: api.runtimeconfigOrigin,
46
+ return apiClient
47
+ .get(`${configPath}/variables`, {
57
48
  retryCodes: [500, 503],
58
49
  })
59
50
  .then(function (resp) {
@@ -61,10 +52,8 @@ function _listVariables(configPath) {
61
52
  });
62
53
  }
63
54
  function _getVariable(varPath) {
64
- return api
65
- .request("GET", utils.endpoint([API_VERSION, varPath]), {
66
- auth: true,
67
- origin: api.runtimeconfigOrigin,
55
+ return apiClient
56
+ .get(varPath, {
68
57
  retryCodes: [500, 503],
69
58
  })
70
59
  .then(function (resp) {
@@ -72,16 +61,12 @@ function _getVariable(varPath) {
72
61
  });
73
62
  }
74
63
  function _createVariable(projectId, configId, varId, value) {
75
- var path = _.join(["projects", projectId, "configs", configId, "variables"], "/");
76
- var endpoint = utils.endpoint([API_VERSION, path]);
77
- return api
78
- .request("POST", endpoint, {
79
- auth: true,
80
- origin: api.runtimeconfigOrigin,
81
- data: {
82
- name: path + "/" + varId,
83
- text: value,
84
- },
64
+ const path = `/projects/${projectId}/configs/${configId}/variables`;
65
+ return apiClient
66
+ .post(path, {
67
+ name: `${path}/${varId}`,
68
+ text: value,
69
+ }, {
85
70
  retryCodes: [500, 503],
86
71
  })
87
72
  .catch(function (err) {
@@ -94,15 +79,11 @@ function _createVariable(projectId, configId, varId, value) {
94
79
  });
95
80
  }
96
81
  function _updateVariable(projectId, configId, varId, value) {
97
- var path = _.join(["projects", projectId, "configs", configId, "variables", varId], "/");
98
- var endpoint = utils.endpoint([API_VERSION, path]);
99
- return api.request("PUT", endpoint, {
100
- auth: true,
101
- origin: api.runtimeconfigOrigin,
102
- data: {
103
- name: path,
104
- text: value,
105
- },
82
+ const path = `/projects/${projectId}/configs/${configId}/variables/${varId}`;
83
+ return apiClient.put(path, {
84
+ name: path,
85
+ text: value,
86
+ }, {
106
87
  retryCodes: [500, 503],
107
88
  });
108
89
  }
@@ -120,13 +101,10 @@ function _setVariable(projectId, configId, varId, value) {
120
101
  });
121
102
  }
122
103
  function _deleteVariable(projectId, configId, varId) {
123
- var endpoint = utils.endpoint([API_VERSION, "projects", projectId, "configs", configId, "variables", varId]) +
124
- "?recursive=true";
125
- return api
126
- .request("DELETE", endpoint, {
127
- auth: true,
128
- origin: api.runtimeconfigOrigin,
104
+ return apiClient
105
+ .delete(`/projects/${projectId}/configs/${configId}/variables/${varId}`, {
129
106
  retryCodes: [500, 503],
107
+ queryParams: { recursive: "true" },
130
108
  })
131
109
  .catch(function (err) {
132
110
  if (_.get(err, "context.response.statusCode") === 404) {
@@ -1,37 +1,88 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.grantServiceAgentRole = exports.addVersion = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.getSecretVersion = exports.getSecret = exports.listSecrets = exports.secretManagerConsoleUri = void 0;
3
+ exports.ensureServiceAgentRole = exports.setIamPolicy = exports.getIamPolicy = exports.addVersion = exports.deleteSecret = exports.patchSecret = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.destroySecretVersion = exports.accessSecretVersion = exports.getSecretVersion = exports.listSecretVersions = exports.listSecrets = exports.getSecret = exports.secretManagerConsoleUri = void 0;
4
4
  const utils_1 = require("../utils");
5
- const api = require("../api");
5
+ const error_1 = require("../error");
6
+ const apiv2_1 = require("../apiv2");
7
+ const api_1 = require("../api");
8
+ const SECRET_NAME_REGEX = new RegExp("projects\\/" +
9
+ "(?<project>(?:\\d+)|(?:[A-Za-z]+[A-Za-z\\d-]*[A-Za-z\\d]?))\\/" +
10
+ "secrets\\/" +
11
+ "(?<secret>[A-Za-z\\d\\-_]+)");
12
+ const SECRET_VERSION_NAME_REGEX = new RegExp(SECRET_NAME_REGEX.source + "\\/versions\\/" + "(?<version>latest|[0-9]+)");
6
13
  const secretManagerConsoleUri = (projectId) => `https://console.cloud.google.com/security/secret-manager?project=${projectId}`;
7
14
  exports.secretManagerConsoleUri = secretManagerConsoleUri;
8
- async function listSecrets(projectId) {
9
- const listRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets`, {
10
- auth: true,
11
- origin: api.secretManagerOrigin,
12
- });
13
- return listRes.body.secrets.map((s) => parseSecretResourceName(s.name));
14
- }
15
- exports.listSecrets = listSecrets;
15
+ const API_VERSION = "v1";
16
+ const client = new apiv2_1.Client({ urlPrefix: api_1.secretManagerOrigin, apiVersion: API_VERSION });
16
17
  async function getSecret(projectId, name) {
17
18
  var _a;
18
- const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}`, {
19
- auth: true,
20
- origin: api.secretManagerOrigin,
21
- });
19
+ const getRes = await client.get(`projects/${projectId}/secrets/${name}`);
22
20
  const secret = parseSecretResourceName(getRes.body.name);
23
21
  secret.labels = (_a = getRes.body.labels) !== null && _a !== void 0 ? _a : {};
24
22
  return secret;
25
23
  }
26
24
  exports.getSecret = getSecret;
25
+ async function listSecrets(projectId, filter) {
26
+ var _a;
27
+ const secrets = [];
28
+ const path = `projects/${projectId}/secrets`;
29
+ const baseOpts = filter ? { queryParams: { filter } } : {};
30
+ let pageToken = "";
31
+ while (true) {
32
+ const opts = pageToken === ""
33
+ ? baseOpts
34
+ : Object.assign(Object.assign({}, baseOpts), { queryParams: Object.assign(Object.assign({}, baseOpts === null || baseOpts === void 0 ? void 0 : baseOpts.queryParams), { pageToken }) });
35
+ const res = await client.get(path, opts);
36
+ for (const s of res.body.secrets) {
37
+ secrets.push(Object.assign(Object.assign({}, parseSecretResourceName(s.name)), { labels: (_a = s.labels) !== null && _a !== void 0 ? _a : {} }));
38
+ }
39
+ if (!res.body.nextPageToken) {
40
+ break;
41
+ }
42
+ pageToken = res.body.nextPageToken;
43
+ }
44
+ return secrets;
45
+ }
46
+ exports.listSecrets = listSecrets;
47
+ async function listSecretVersions(projectId, name, filter) {
48
+ const secrets = [];
49
+ const path = `projects/${projectId}/secrets/${name}/versions`;
50
+ const baseOpts = filter ? { queryParams: { filter } } : {};
51
+ let pageToken = "";
52
+ while (true) {
53
+ const opts = pageToken === ""
54
+ ? baseOpts
55
+ : Object.assign(Object.assign({}, baseOpts), { queryParams: Object.assign(Object.assign({}, baseOpts === null || baseOpts === void 0 ? void 0 : baseOpts.queryParams), { pageToken }) });
56
+ const res = await client.get(path, opts);
57
+ for (const s of res.body.versions || []) {
58
+ secrets.push(Object.assign(Object.assign({}, parseSecretVersionResourceName(s.name)), { state: s.state }));
59
+ }
60
+ if (!res.body.nextPageToken) {
61
+ break;
62
+ }
63
+ pageToken = res.body.nextPageToken;
64
+ }
65
+ return secrets;
66
+ }
67
+ exports.listSecretVersions = listSecretVersions;
27
68
  async function getSecretVersion(projectId, name, version) {
28
- const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}/versions/${version}`, {
29
- auth: true,
30
- origin: api.secretManagerOrigin,
31
- });
32
- return parseSecretVersionResourceName(getRes.body.name);
69
+ const getRes = await client.get(`projects/${projectId}/secrets/${name}/versions/${version}`);
70
+ return Object.assign(Object.assign({}, parseSecretVersionResourceName(getRes.body.name)), { state: getRes.body.state });
33
71
  }
34
72
  exports.getSecretVersion = getSecretVersion;
73
+ async function accessSecretVersion(projectId, name, version) {
74
+ const res = await client.get(`projects/${projectId}/secrets/${name}/versions/${version}:access`);
75
+ return Buffer.from(res.body.payload.data, "base64").toString();
76
+ }
77
+ exports.accessSecretVersion = accessSecretVersion;
78
+ async function destroySecretVersion(projectId, name, version) {
79
+ if (version === "latest") {
80
+ const sv = await getSecretVersion(projectId, name, "latest");
81
+ version = sv.versionId;
82
+ }
83
+ await client.post(`projects/${projectId}/secrets/${name}/versions/${version}:destroy`);
84
+ }
85
+ exports.destroySecretVersion = destroySecretVersion;
35
86
  async function secretExists(projectId, name) {
36
87
  try {
37
88
  await getSecret(projectId, name);
@@ -46,21 +97,27 @@ async function secretExists(projectId, name) {
46
97
  }
47
98
  exports.secretExists = secretExists;
48
99
  function parseSecretResourceName(resourceName) {
49
- const nameTokens = resourceName.split("/");
100
+ const match = SECRET_NAME_REGEX.exec(resourceName);
101
+ if (!(match === null || match === void 0 ? void 0 : match.groups)) {
102
+ throw new error_1.FirebaseError(`Invalid secret resource name [${resourceName}].`);
103
+ }
50
104
  return {
51
- projectId: nameTokens[1],
52
- name: nameTokens[3],
105
+ projectId: match.groups.project,
106
+ name: match.groups.secret,
53
107
  };
54
108
  }
55
109
  exports.parseSecretResourceName = parseSecretResourceName;
56
110
  function parseSecretVersionResourceName(resourceName) {
57
- const nameTokens = resourceName.split("/");
111
+ const match = resourceName.match(SECRET_VERSION_NAME_REGEX);
112
+ if (!(match === null || match === void 0 ? void 0 : match.groups)) {
113
+ throw new error_1.FirebaseError(`Invalid secret version resource name [${resourceName}].`);
114
+ }
58
115
  return {
59
116
  secret: {
60
- projectId: nameTokens[1],
61
- name: nameTokens[3],
117
+ projectId: match.groups.project,
118
+ name: match.groups.secret,
62
119
  },
63
- versionId: nameTokens[5],
120
+ versionId: match.groups.version,
64
121
  };
65
122
  }
66
123
  exports.parseSecretVersionResourceName = parseSecretVersionResourceName;
@@ -69,65 +126,68 @@ function toSecretVersionResourceName(secretVersion) {
69
126
  }
70
127
  exports.toSecretVersionResourceName = toSecretVersionResourceName;
71
128
  async function createSecret(projectId, name, labels) {
72
- const createRes = await api.request("POST", `/v1beta1/projects/${projectId}/secrets?secretId=${name}`, {
73
- auth: true,
74
- origin: api.secretManagerOrigin,
75
- data: {
76
- replication: {
77
- automatic: {},
78
- },
79
- labels,
129
+ const createRes = await client.post(`projects/${projectId}/secrets`, {
130
+ name,
131
+ replication: {
132
+ automatic: {},
80
133
  },
81
- });
134
+ labels,
135
+ }, { queryParams: { secretId: name } });
82
136
  return parseSecretResourceName(createRes.body.name);
83
137
  }
84
138
  exports.createSecret = createSecret;
85
- async function addVersion(secret, payloadData) {
86
- const res = await api.request("POST", `/v1beta1/projects/${secret.projectId}/secrets/${secret.name}:addVersion`, {
87
- auth: true,
88
- origin: api.secretManagerOrigin,
89
- data: {
90
- payload: {
91
- data: Buffer.from(payloadData).toString("base64"),
92
- },
139
+ async function patchSecret(projectId, name, labels) {
140
+ const fullName = `projects/${projectId}/secrets/${name}`;
141
+ const res = await client.patch(fullName, { name: fullName, labels }, { queryParams: { updateMask: "labels" } });
142
+ return parseSecretResourceName(res.body.name);
143
+ }
144
+ exports.patchSecret = patchSecret;
145
+ async function deleteSecret(projectId, name) {
146
+ const path = `projects/${projectId}/secrets/${name}`;
147
+ await client.delete(path);
148
+ }
149
+ exports.deleteSecret = deleteSecret;
150
+ async function addVersion(projectId, name, payloadData) {
151
+ const res = await client.post(`projects/${projectId}/secrets/${name}:addVersion`, {
152
+ payload: {
153
+ data: Buffer.from(payloadData).toString("base64"),
93
154
  },
94
155
  });
95
- const nameTokens = res.body.name.split("/");
96
- return {
97
- secret: {
98
- projectId: nameTokens[1],
99
- name: nameTokens[3],
100
- },
101
- versionId: nameTokens[5],
102
- };
156
+ return Object.assign(Object.assign({}, parseSecretVersionResourceName(res.body.name)), { state: res.body.state });
103
157
  }
104
158
  exports.addVersion = addVersion;
105
- async function grantServiceAgentRole(secret, serviceAccountEmail, role) {
106
- const getPolicyRes = await api.request("GET", `/v1beta1/projects/${secret.projectId}/secrets/${secret.name}:getIamPolicy`, {
107
- auth: true,
108
- origin: api.secretManagerOrigin,
109
- });
110
- const bindings = getPolicyRes.body.bindings || [];
111
- if (bindings.find((b) => b.role == role &&
112
- b.members.find((m) => m == `serviceAccount:${serviceAccountEmail}`))) {
113
- return;
114
- }
115
- bindings.push({
116
- role: role,
117
- members: [`serviceAccount:${serviceAccountEmail}`],
118
- });
119
- await api.request("POST", `/v1beta1/projects/${secret.projectId}/secrets/${secret.name}:setIamPolicy`, {
120
- auth: true,
121
- origin: api.secretManagerOrigin,
122
- data: {
123
- policy: {
124
- bindings,
125
- },
126
- updateMask: {
127
- paths: "bindings",
128
- },
159
+ async function getIamPolicy(secret) {
160
+ const res = await client.get(`projects/${secret.projectId}/secrets/${secret.name}:getIamPolicy`);
161
+ return res.body;
162
+ }
163
+ exports.getIamPolicy = getIamPolicy;
164
+ async function setIamPolicy(secret, bindings) {
165
+ await client.post(`projects/${secret.projectId}/secrets/${secret.name}:setIamPolicy`, {
166
+ policy: {
167
+ bindings,
129
168
  },
169
+ updateMask: "bindings",
130
170
  });
131
- (0, utils_1.logLabeledSuccess)("SecretManager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmail}`);
132
171
  }
133
- exports.grantServiceAgentRole = grantServiceAgentRole;
172
+ exports.setIamPolicy = setIamPolicy;
173
+ async function ensureServiceAgentRole(secret, serviceAccountEmails, role) {
174
+ const policy = await module.exports.getIamPolicy(secret);
175
+ const bindings = policy.bindings || [];
176
+ let binding = bindings.find((b) => b.role === role);
177
+ if (!binding) {
178
+ binding = { role, members: [] };
179
+ bindings.push(binding);
180
+ }
181
+ let shouldShortCircuit = true;
182
+ for (const serviceAccount of serviceAccountEmails) {
183
+ if (!binding.members.find((m) => m === `serviceAccount:${serviceAccount}`)) {
184
+ binding.members.push(`serviceAccount:${serviceAccount}`);
185
+ shouldShortCircuit = false;
186
+ }
187
+ }
188
+ if (shouldShortCircuit)
189
+ return;
190
+ await module.exports.setIamPolicy(secret, bindings);
191
+ (0, utils_1.logLabeledSuccess)("secretmanager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmails.join(", ")}`);
192
+ }
193
+ exports.ensureServiceAgentRole = ensureServiceAgentRole;
@@ -2,15 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getServiceAccount = exports.getBucket = exports.deleteObject = exports.uploadObject = exports.upload = exports.getDefaultBucket = void 0;
4
4
  const path = require("path");
5
- const api = require("../api");
5
+ const api_1 = require("../api");
6
+ const apiv2_1 = require("../apiv2");
6
7
  const logger_1 = require("../logger");
7
8
  const error_1 = require("../error");
8
9
  async function getDefaultBucket(projectId) {
9
10
  try {
10
- const resp = await api.request("GET", "/v1/apps/" + projectId, {
11
- auth: true,
12
- origin: api.appengineOrigin,
13
- });
11
+ const appengineClient = new apiv2_1.Client({ urlPrefix: api_1.appengineOrigin, apiVersion: "v1" });
12
+ const resp = await appengineClient.get(`/apps/${projectId}`);
14
13
  if (resp.body.defaultBucket === "undefined") {
15
14
  logger_1.logger.debug("Default storage bucket is undefined.");
16
15
  throw new error_1.FirebaseError("Your project is being set up. Please wait a minute before deploying again.");
@@ -25,15 +24,17 @@ async function getDefaultBucket(projectId) {
25
24
  exports.getDefaultBucket = getDefaultBucket;
26
25
  async function upload(source, uploadUrl, extraHeaders) {
27
26
  const url = new URL(uploadUrl);
28
- const result = await api.request("PUT", url.pathname + url.search, {
29
- data: source.stream,
30
- headers: Object.assign({ "Content-Type": "application/zip" }, extraHeaders),
31
- json: false,
32
- origin: url.origin,
33
- logOptions: { skipRequestBody: true },
27
+ const localAPIClient = new apiv2_1.Client({ urlPrefix: url.origin, auth: false });
28
+ const res = await localAPIClient.request({
29
+ method: "PUT",
30
+ path: url.pathname,
31
+ queryParams: url.searchParams,
32
+ headers: Object.assign({ "content-type": "application/zip" }, extraHeaders),
33
+ body: source.stream,
34
+ skipLog: { resBody: true },
34
35
  });
35
36
  return {
36
- generation: result.response.headers["x-goog-generation"],
37
+ generation: res.response.headers.get("x-goog-generation"),
37
38
  };
38
39
  }
39
40
  exports.upload = upload;
@@ -41,38 +42,35 @@ async function uploadObject(source, bucketName) {
41
42
  if (path.extname(source.file) !== ".zip") {
42
43
  throw new error_1.FirebaseError(`Expected a file name ending in .zip, got ${source.file}`);
43
44
  }
45
+ const localAPIClient = new apiv2_1.Client({ urlPrefix: api_1.storageOrigin });
44
46
  const location = `/${bucketName}/${path.basename(source.file)}`;
45
- const result = await api.request("PUT", location, {
46
- auth: true,
47
- data: source.stream,
47
+ const res = await localAPIClient.request({
48
+ method: "PUT",
49
+ path: location,
48
50
  headers: {
49
51
  "Content-Type": "application/zip",
50
52
  "x-goog-content-length-range": "0,123289600",
51
53
  },
52
- json: false,
53
- origin: api.storageOrigin,
54
- logOptions: { skipRequestBody: true },
54
+ body: source.stream,
55
55
  });
56
56
  return {
57
57
  bucket: bucketName,
58
58
  object: path.basename(source.file),
59
- generation: result.response.headers["x-goog-generation"],
59
+ generation: res.response.headers.get("x-goog-generation"),
60
60
  };
61
61
  }
62
62
  exports.uploadObject = uploadObject;
63
63
  function deleteObject(location) {
64
- return api.request("DELETE", location, {
64
+ return api_1.default.request("DELETE", location, {
65
65
  auth: true,
66
- origin: api.storageOrigin,
66
+ origin: api_1.default.storageOrigin,
67
67
  });
68
68
  }
69
69
  exports.deleteObject = deleteObject;
70
70
  async function getBucket(bucketName) {
71
71
  try {
72
- const result = await api.request("GET", `/storage/v1/b/${bucketName}`, {
73
- auth: true,
74
- origin: api.storageOrigin,
75
- });
72
+ const localAPIClient = new apiv2_1.Client({ urlPrefix: api_1.storageOrigin });
73
+ const result = await localAPIClient.get(`/storage/v1/b/${bucketName}`);
76
74
  return result.body;
77
75
  }
78
76
  catch (err) {
@@ -85,10 +83,8 @@ async function getBucket(bucketName) {
85
83
  exports.getBucket = getBucket;
86
84
  async function getServiceAccount(projectId) {
87
85
  try {
88
- const response = await api.request("GET", `/storage/v1/projects/${projectId}/serviceAccount`, {
89
- auth: true,
90
- origin: api.storageOrigin,
91
- });
86
+ const localAPIClient = new apiv2_1.Client({ urlPrefix: api_1.storageOrigin });
87
+ const response = await localAPIClient.get(`/storage/v1/projects/${projectId}/serviceAccount`);
92
88
  return response.body;
93
89
  }
94
90
  catch (err) {
package/lib/previews.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.previews = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  const configstore_1 = require("./configstore");
6
- exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, dotenv: false, artifactregistry: false }, configstore_1.configstore.get("previews"));
6
+ exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, artifactregistry: false }, configstore_1.configstore.get("previews"));
7
7
  if (process.env.FIREBASE_CLI_PREVIEWS) {
8
8
  process.env.FIREBASE_CLI_PREVIEWS.split(",").forEach((feature) => {
9
9
  if ((0, lodash_1.has)(exports.previews, feature)) {