firebase-tools 13.13.2 → 13.14.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.
- package/lib/api.js +4 -2
- package/lib/apphosting/githubConnections.js +108 -34
- package/lib/auth.js +2 -7
- package/lib/command.js +1 -1
- package/lib/commands/dataconnect-sdk-generate.js +2 -7
- package/lib/commands/firestore-backups-schedules-create.js +11 -14
- package/lib/commands/firestore-backups-schedules-list.js +1 -1
- package/lib/commands/firestore-backups-schedules-update.js +4 -3
- package/lib/commands/firestore-databases-create.js +5 -6
- package/lib/commands/firestore-databases-restore.js +4 -4
- package/lib/commands/firestore-databases-update.js +5 -6
- package/lib/commands/init.js +2 -2
- package/lib/commands/login.js +1 -2
- package/lib/dataconnect/fileUtils.js +2 -2
- package/lib/dataconnect/filters.js +12 -1
- package/lib/dataconnect/load.js +6 -5
- package/lib/dataconnect/schemaMigration.js +22 -0
- package/lib/deploy/dataconnect/prepare.js +16 -2
- package/lib/emulator/constants.js +1 -0
- package/lib/emulator/controller.js +2 -5
- package/lib/emulator/dataconnectEmulator.js +3 -2
- package/lib/emulator/downloadableEmulators.js +12 -12
- package/lib/emulator/eventarcEmulator.js +68 -18
- package/lib/emulator/functionsEmulator.js +79 -0
- package/lib/emulator/functionsEmulatorShared.js +4 -0
- package/lib/emulator/pubsubEmulator.js +1 -1
- package/lib/experiments.js +5 -0
- package/lib/frameworks/angular/utils.js +13 -5
- package/lib/functions/events/v2.js +2 -1
- package/lib/gcp/devConnect.js +13 -1
- package/lib/init/features/account.js +1 -1
- package/lib/init/features/dataconnect/index.js +69 -19
- package/lib/init/features/dataconnect/sdk.js +2 -3
- package/lib/init/features/hosting/github.js +44 -21
- package/lib/requireAuth.js +2 -2
- package/package.json +8 -8
- package/templates/init/dataconnect/dataconnect.yaml +1 -1
|
@@ -11,10 +11,12 @@ const logger_1 = require("../logger");
|
|
|
11
11
|
const error_1 = require("../error");
|
|
12
12
|
const projectUtils_1 = require("../projectUtils");
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
|
+
const experiments = require("../experiments");
|
|
14
15
|
const errors = require("./errors");
|
|
15
16
|
async function diffSchema(schema) {
|
|
16
17
|
const { serviceName, instanceName, databaseId } = getIdentifiers(schema);
|
|
17
18
|
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, false);
|
|
19
|
+
setCompatibleMode(schema, databaseId, instanceName);
|
|
18
20
|
try {
|
|
19
21
|
await (0, client_1.upsertSchema)(schema, true);
|
|
20
22
|
(0, utils_1.logLabeledSuccess)("dataconnect", `Database schema is up to date.`);
|
|
@@ -43,6 +45,7 @@ async function migrateSchema(args) {
|
|
|
43
45
|
const { options, schema, validateOnly } = args;
|
|
44
46
|
const { serviceName, instanceId, instanceName, databaseId } = getIdentifiers(schema);
|
|
45
47
|
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, true);
|
|
48
|
+
setCompatibleMode(schema, databaseId, instanceName);
|
|
46
49
|
try {
|
|
47
50
|
await (0, client_1.upsertSchema)(schema, validateOnly);
|
|
48
51
|
logger_1.logger.debug(`Database schema was up to date for ${instanceId}:${databaseId}`);
|
|
@@ -79,6 +82,25 @@ async function migrateSchema(args) {
|
|
|
79
82
|
return [];
|
|
80
83
|
}
|
|
81
84
|
exports.migrateSchema = migrateSchema;
|
|
85
|
+
function setCompatibleMode(schema, databaseId, instanceName) {
|
|
86
|
+
var _a;
|
|
87
|
+
if (experiments.isEnabled("fdccompatiblemode")) {
|
|
88
|
+
if ((_a = schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.schemaValidation) {
|
|
89
|
+
schema.primaryDatasource.postgresql.schemaValidation = "COMPATIBLE";
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
schema.primaryDatasource = {
|
|
93
|
+
postgresql: {
|
|
94
|
+
database: databaseId,
|
|
95
|
+
cloudSql: {
|
|
96
|
+
instance: instanceName,
|
|
97
|
+
},
|
|
98
|
+
schemaValidation: "COMPATIBLE",
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
82
104
|
function getIdentifiers(schema) {
|
|
83
105
|
var _a, _b;
|
|
84
106
|
const databaseId = (_a = schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.database;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const clc = require("colorette");
|
|
4
4
|
const load_1 = require("../../dataconnect/load");
|
|
5
5
|
const fileUtils_1 = require("../../dataconnect/fileUtils");
|
|
6
6
|
const logger_1 = require("../../logger");
|
|
@@ -11,6 +11,7 @@ const build_1 = require("../../dataconnect/build");
|
|
|
11
11
|
const ensureApis_1 = require("../../dataconnect/ensureApis");
|
|
12
12
|
const requireTosAcceptance_1 = require("../../requireTosAcceptance");
|
|
13
13
|
const firedata_1 = require("../../gcp/firedata");
|
|
14
|
+
const error_1 = require("../../error");
|
|
14
15
|
async function default_1(context, options) {
|
|
15
16
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
16
17
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
@@ -18,10 +19,23 @@ async function default_1(context, options) {
|
|
|
18
19
|
const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(options.config);
|
|
19
20
|
utils.logLabeledBullet("dataconnect", `Preparing to deploy`);
|
|
20
21
|
const filters = (0, filters_1.getResourceFilters)(options);
|
|
21
|
-
const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId,
|
|
22
|
+
const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, options.config, c.source)));
|
|
22
23
|
for (const si of serviceInfos) {
|
|
23
24
|
si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory);
|
|
24
25
|
}
|
|
26
|
+
const unmatchedFilters = filters === null || filters === void 0 ? void 0 : filters.filter((f) => {
|
|
27
|
+
const serviceMatched = serviceInfos.some((s) => s.dataConnectYaml.serviceId === f.serviceId);
|
|
28
|
+
const connectorMatched = f.connectorId
|
|
29
|
+
? serviceInfos.some((s) => {
|
|
30
|
+
return (s.dataConnectYaml.serviceId === f.serviceId &&
|
|
31
|
+
s.connectorInfo.some((c) => c.connectorYaml.connectorId === f.connectorId));
|
|
32
|
+
})
|
|
33
|
+
: true;
|
|
34
|
+
return !serviceMatched || !connectorMatched;
|
|
35
|
+
});
|
|
36
|
+
if (unmatchedFilters === null || unmatchedFilters === void 0 ? void 0 : unmatchedFilters.length) {
|
|
37
|
+
throw new error_1.FirebaseError(`The following filters were specified in --only but didn't match anything in this project: ${unmatchedFilters.map(filters_1.toString).map(clc.bold).join(", ")}`);
|
|
38
|
+
}
|
|
25
39
|
context.dataconnect = {
|
|
26
40
|
serviceInfos,
|
|
27
41
|
filters,
|
|
@@ -106,6 +106,7 @@ Constants.SERVICE_FIRESTORE = "firestore.googleapis.com";
|
|
|
106
106
|
Constants.SERVICE_REALTIME_DATABASE = "firebaseio.com";
|
|
107
107
|
Constants.SERVICE_PUBSUB = "pubsub.googleapis.com";
|
|
108
108
|
Constants.SERVICE_EVENTARC = "eventarc.googleapis.com";
|
|
109
|
+
Constants.SERVICE_FIREALERTS = "firebasealerts.googleapis.com";
|
|
109
110
|
Constants.SERVICE_ANALYTICS = "app-measurement.com";
|
|
110
111
|
Constants.SERVICE_AUTH = "firebaseauth.googleapis.com";
|
|
111
112
|
Constants.SERVICE_CRASHLYTICS = "fabric.io";
|
|
@@ -526,17 +526,14 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
526
526
|
else if (config.length > 1) {
|
|
527
527
|
logger_1.logger.warn(`TODO: Add support for multiple services in the Data Connect emulator. Currently emulating first service ${config[0].source}`);
|
|
528
528
|
}
|
|
529
|
-
|
|
530
|
-
if (!path.isAbsolute(configDir)) {
|
|
531
|
-
const cwd = options.cwd || process.cwd();
|
|
532
|
-
configDir = path.resolve(path.join(cwd), configDir);
|
|
533
|
-
}
|
|
529
|
+
const configDir = config[0].source;
|
|
534
530
|
const dataConnectEmulator = new dataconnectEmulator_1.DataConnectEmulator({
|
|
535
531
|
listen: listenForEmulator.dataconnect,
|
|
536
532
|
projectId,
|
|
537
533
|
auto_download: true,
|
|
538
534
|
configDir,
|
|
539
535
|
rc: options.rc,
|
|
536
|
+
config: options.config,
|
|
540
537
|
});
|
|
541
538
|
await startEmulator(dataConnectEmulator);
|
|
542
539
|
}
|
|
@@ -27,7 +27,8 @@ class DataConnectEmulator {
|
|
|
27
27
|
}
|
|
28
28
|
async start() {
|
|
29
29
|
try {
|
|
30
|
-
const
|
|
30
|
+
const resolvedConfigDir = this.args.config.path(this.args.configDir);
|
|
31
|
+
const info = await DataConnectEmulator.build({ configDir: resolvedConfigDir });
|
|
31
32
|
if ((0, types_2.requiresVector)(info.metadata)) {
|
|
32
33
|
if (constants_1.Constants.isDemoProject(this.args.projectId)) {
|
|
33
34
|
this.logger.logLabeled("WARN", "Data Connect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
|
|
@@ -143,7 +144,7 @@ class DataConnectEmulator {
|
|
|
143
144
|
if (!emuInfo) {
|
|
144
145
|
return false;
|
|
145
146
|
}
|
|
146
|
-
const serviceInfo = await (0, load_1.load)(this.args.projectId, this.args.configDir);
|
|
147
|
+
const serviceInfo = await (0, load_1.load)(this.args.projectId, this.args.config, this.args.configDir);
|
|
147
148
|
const sameService = emuInfo.services.find((s) => serviceInfo.dataConnectYaml.serviceId === s.serviceId);
|
|
148
149
|
if (!sameService) {
|
|
149
150
|
throw new error_1.FirebaseError(`There is a Data Connect emulator already running on ${this.args.listen[0].address}:${this.args.listen[0].port}, but it is emulating a different service. Please stop that instance of the Data Connect emulator, or specify a different port in 'firebase.json'`);
|
|
@@ -35,9 +35,9 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
35
35
|
ui: experiments.isEnabled("emulatoruisnapshot")
|
|
36
36
|
? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
|
|
37
37
|
: {
|
|
38
|
-
version: "1.
|
|
39
|
-
expectedSize:
|
|
40
|
-
expectedChecksum: "
|
|
38
|
+
version: "1.13.0",
|
|
39
|
+
expectedSize: 3605485,
|
|
40
|
+
expectedChecksum: "ec0aa91592c56af9ff7df18168d58459",
|
|
41
41
|
},
|
|
42
42
|
pubsub: {
|
|
43
43
|
version: "0.8.14",
|
|
@@ -46,20 +46,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
46
46
|
},
|
|
47
47
|
dataconnect: process.platform === "darwin"
|
|
48
48
|
? {
|
|
49
|
-
version: "1.
|
|
50
|
-
expectedSize:
|
|
51
|
-
expectedChecksum: "
|
|
49
|
+
version: "1.3.0",
|
|
50
|
+
expectedSize: 24175424,
|
|
51
|
+
expectedChecksum: "e0aefd484499308434a0405a5a55574c",
|
|
52
52
|
}
|
|
53
53
|
: process.platform === "win32"
|
|
54
54
|
? {
|
|
55
|
-
version: "1.
|
|
56
|
-
expectedSize:
|
|
57
|
-
expectedChecksum: "
|
|
55
|
+
version: "1.3.0",
|
|
56
|
+
expectedSize: 24585728,
|
|
57
|
+
expectedChecksum: "c7f9fad2025d9f2c9d2dff44a3edebbe",
|
|
58
58
|
}
|
|
59
59
|
: {
|
|
60
|
-
version: "1.
|
|
61
|
-
expectedSize:
|
|
62
|
-
expectedChecksum: "
|
|
60
|
+
version: "1.3.0",
|
|
61
|
+
expectedSize: 24084632,
|
|
62
|
+
expectedChecksum: "f332b33b67680a32ea76bea866540656",
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
exports.DownloadDetails = {
|
|
@@ -9,22 +9,36 @@ const emulatorLogger_1 = require("./emulatorLogger");
|
|
|
9
9
|
const registry_1 = require("./registry");
|
|
10
10
|
const error_1 = require("../error");
|
|
11
11
|
const eventarcEmulatorUtils_1 = require("./eventarcEmulatorUtils");
|
|
12
|
+
const cors = require("cors");
|
|
13
|
+
const GOOGLE_CHANNEL = "google";
|
|
12
14
|
class EventarcEmulator {
|
|
13
15
|
constructor(args) {
|
|
14
16
|
this.args = args;
|
|
15
17
|
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.EVENTARC);
|
|
16
|
-
this.
|
|
18
|
+
this.events = {};
|
|
17
19
|
}
|
|
18
20
|
createHubServer() {
|
|
19
21
|
const registerTriggerRoute = `/emulator/v1/projects/:project_id/triggers/:trigger_name(*)`;
|
|
20
22
|
const registerTriggerHandler = (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const { projectId, triggerName, eventTrigger, key } = getTriggerIdentifiers(req);
|
|
25
|
+
this.logger.logLabeled("BULLET", "eventarc", `Registering Eventarc event trigger for ${key} with trigger name ${triggerName}.`);
|
|
26
|
+
const eventTriggers = this.events[key] || [];
|
|
27
|
+
eventTriggers.push({ projectId, triggerName, eventTrigger });
|
|
28
|
+
this.events[key] = eventTriggers;
|
|
29
|
+
res.status(200).send({ res: "OK" });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
res.status(400).send({ error });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const getTriggerIdentifiers = (req) => {
|
|
21
36
|
const projectId = req.params.project_id;
|
|
22
37
|
const triggerName = req.params.trigger_name;
|
|
23
38
|
if (!projectId || !triggerName) {
|
|
24
39
|
const error = "Missing project ID or trigger name.";
|
|
25
40
|
this.logger.log("ERROR", error);
|
|
26
|
-
|
|
27
|
-
return;
|
|
41
|
+
throw error;
|
|
28
42
|
}
|
|
29
43
|
const bodyString = req.rawBody.toString();
|
|
30
44
|
const substituted = bodyString.replaceAll("${PROJECT_ID}", projectId);
|
|
@@ -33,27 +47,56 @@ class EventarcEmulator {
|
|
|
33
47
|
if (!eventTrigger) {
|
|
34
48
|
const error = `Missing event trigger for ${triggerName}.`;
|
|
35
49
|
this.logger.log("ERROR", error);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
const channel = eventTrigger.channel || GOOGLE_CHANNEL;
|
|
53
|
+
const key = `${eventTrigger.eventType}-${channel}`;
|
|
54
|
+
return { projectId, triggerName, eventTrigger, key };
|
|
55
|
+
};
|
|
56
|
+
const removeTriggerRoute = `/emulator/v1/remove/projects/:project_id/triggers/:trigger_name`;
|
|
57
|
+
const removeTriggerHandler = (req, res) => {
|
|
58
|
+
try {
|
|
59
|
+
const { projectId, triggerName, eventTrigger, key } = getTriggerIdentifiers(req);
|
|
60
|
+
this.logger.logLabeled("BULLET", "eventarc", `Removing Eventarc event trigger for ${key} with trigger name ${triggerName}.`);
|
|
61
|
+
const eventTriggers = this.events[key] || [];
|
|
62
|
+
const triggerIdentifier = { projectId, triggerName, eventTrigger };
|
|
63
|
+
const removeIdx = eventTriggers.findIndex((e) => JSON.stringify(triggerIdentifier) === JSON.stringify(e));
|
|
64
|
+
if (removeIdx === -1) {
|
|
65
|
+
this.logger.logLabeled("ERROR", "eventarc", "Tried to remove nonexistent trigger");
|
|
66
|
+
throw new Error(`Unable to delete function trigger ${triggerName}`);
|
|
67
|
+
}
|
|
68
|
+
eventTriggers.splice(removeIdx, 1);
|
|
69
|
+
if (eventTriggers.length === 0) {
|
|
70
|
+
delete this.events[key];
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.events[key] = eventTriggers;
|
|
74
|
+
}
|
|
75
|
+
res.status(200).send({ res: "OK" });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
36
78
|
res.status(400).send({ error });
|
|
37
|
-
return;
|
|
38
79
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.customEvents[key] = customEventTriggers;
|
|
44
|
-
res.status(200).send({ res: "OK" });
|
|
80
|
+
};
|
|
81
|
+
const getTriggersRoute = `/google/getTriggers`;
|
|
82
|
+
const getTriggersHandler = (req, res) => {
|
|
83
|
+
res.status(200).send(this.events);
|
|
45
84
|
};
|
|
46
85
|
const publishEventsRoute = `/projects/:project_id/locations/:location/channels/:channel::publishEvents`;
|
|
86
|
+
const publishNativeEventsRoute = `/google/publishEvents`;
|
|
47
87
|
const publishEventsHandler = (req, res) => {
|
|
48
|
-
const
|
|
88
|
+
const isCustom = req.params.project_id && req.params.channel;
|
|
89
|
+
const channel = isCustom
|
|
90
|
+
? `projects/${req.params.project_id}/locations/${req.params.location}/channels/${req.params.channel}`
|
|
91
|
+
: GOOGLE_CHANNEL;
|
|
49
92
|
const body = JSON.parse(req.rawBody.toString());
|
|
50
93
|
for (const event of body.events) {
|
|
51
94
|
if (!event.type) {
|
|
52
95
|
res.sendStatus(400);
|
|
53
96
|
return;
|
|
54
97
|
}
|
|
55
|
-
this.logger.log("INFO", `Received
|
|
56
|
-
this.
|
|
98
|
+
this.logger.log("INFO", `Received event at channel ${channel}: ${JSON.stringify(event, null, 2)}`);
|
|
99
|
+
this.triggerEventFunction(channel, event);
|
|
57
100
|
}
|
|
58
101
|
res.sendStatus(200);
|
|
59
102
|
};
|
|
@@ -70,27 +113,34 @@ class EventarcEmulator {
|
|
|
70
113
|
const hub = express();
|
|
71
114
|
hub.post([registerTriggerRoute], dataMiddleware, registerTriggerHandler);
|
|
72
115
|
hub.post([publishEventsRoute], dataMiddleware, publishEventsHandler);
|
|
116
|
+
hub.post([publishNativeEventsRoute], dataMiddleware, cors({ origin: true }), publishEventsHandler);
|
|
117
|
+
hub.post([removeTriggerRoute], dataMiddleware, removeTriggerHandler);
|
|
118
|
+
hub.get([getTriggersRoute], cors({ origin: true }), getTriggersHandler);
|
|
73
119
|
hub.all("*", (req, res) => {
|
|
74
120
|
this.logger.log("DEBUG", `Eventarc emulator received unknown request at path ${req.path}`);
|
|
75
121
|
res.sendStatus(404);
|
|
76
122
|
});
|
|
77
123
|
return hub;
|
|
78
124
|
}
|
|
79
|
-
async
|
|
125
|
+
async triggerEventFunction(channel, event) {
|
|
80
126
|
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FUNCTIONS)) {
|
|
81
127
|
this.logger.log("INFO", "Functions emulator not found. This should not happen.");
|
|
82
128
|
return Promise.reject();
|
|
83
129
|
}
|
|
84
130
|
const key = `${event.type}-${channel}`;
|
|
85
|
-
const triggers = this.
|
|
131
|
+
const triggers = this.events[key] || [];
|
|
132
|
+
const eventPayload = channel === GOOGLE_CHANNEL ? event : (0, eventarcEmulatorUtils_1.cloudEventFromProtoToJson)(event);
|
|
86
133
|
return await Promise.all(triggers
|
|
87
134
|
.filter((trigger) => !trigger.eventTrigger.eventFilters ||
|
|
88
135
|
this.matchesAll(event, trigger.eventTrigger.eventFilters))
|
|
89
|
-
.map((trigger) =>
|
|
136
|
+
.map((trigger) => this.callFunctionTrigger(trigger, eventPayload)));
|
|
137
|
+
}
|
|
138
|
+
callFunctionTrigger(trigger, event) {
|
|
139
|
+
return registry_1.EmulatorRegistry.client(types_1.Emulators.FUNCTIONS)
|
|
90
140
|
.request({
|
|
91
141
|
method: "POST",
|
|
92
142
|
path: `/functions/projects/${trigger.projectId}/triggers/${trigger.triggerName}`,
|
|
93
|
-
body: JSON.stringify(
|
|
143
|
+
body: JSON.stringify(event),
|
|
94
144
|
responseType: "stream",
|
|
95
145
|
resolveOnHTTPError: true,
|
|
96
146
|
})
|
|
@@ -101,7 +151,7 @@ class EventarcEmulator {
|
|
|
101
151
|
})
|
|
102
152
|
.catch((err) => {
|
|
103
153
|
this.logger.log("ERROR", `Failed to trigger Functions emulator for ${trigger.triggerName}: ${err}`);
|
|
104
|
-
})
|
|
154
|
+
});
|
|
105
155
|
}
|
|
106
156
|
matchesAll(event, eventFilters) {
|
|
107
157
|
return Object.entries(eventFilters).every(([key, value]) => {
|
|
@@ -344,6 +344,15 @@ class FunctionsEmulator {
|
|
|
344
344
|
else {
|
|
345
345
|
this.workerPools[emulatableBackend.codebase].refresh();
|
|
346
346
|
}
|
|
347
|
+
const toRemove = Object.keys(this.triggers).filter((recordKey) => {
|
|
348
|
+
const record = this.getTriggerRecordByKey(recordKey);
|
|
349
|
+
if (force) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
return !triggerDefinitions.some((def) => record.def.entryPoint === def.entryPoint &&
|
|
353
|
+
JSON.stringify(record.def.eventTrigger) === JSON.stringify(def.eventTrigger));
|
|
354
|
+
});
|
|
355
|
+
await this.removeTriggers(toRemove);
|
|
347
356
|
const toSetup = triggerDefinitions.filter((definition) => {
|
|
348
357
|
if (force) {
|
|
349
358
|
return true;
|
|
@@ -394,6 +403,9 @@ class FunctionsEmulator {
|
|
|
394
403
|
case constants_1.Constants.SERVICE_STORAGE:
|
|
395
404
|
added = this.addStorageTrigger(this.args.projectId, key, definition.eventTrigger);
|
|
396
405
|
break;
|
|
406
|
+
case constants_1.Constants.SERVICE_FIREALERTS:
|
|
407
|
+
added = await this.addFirealertsTrigger(this.args.projectId, key, definition.eventTrigger);
|
|
408
|
+
break;
|
|
397
409
|
default:
|
|
398
410
|
this.logger.log("DEBUG", `Unsupported trigger: ${JSON.stringify(definition)}`);
|
|
399
411
|
break;
|
|
@@ -442,6 +454,25 @@ class FunctionsEmulator {
|
|
|
442
454
|
}
|
|
443
455
|
}
|
|
444
456
|
}
|
|
457
|
+
async removeTriggers(toRemove) {
|
|
458
|
+
for (const triggerKey of toRemove) {
|
|
459
|
+
const definition = this.triggers[triggerKey].def;
|
|
460
|
+
const service = (0, functionsEmulatorShared_1.getFunctionService)(definition);
|
|
461
|
+
const key = this.getTriggerKey(definition);
|
|
462
|
+
switch (service) {
|
|
463
|
+
case constants_1.Constants.SERVICE_EVENTARC:
|
|
464
|
+
await this.removeEventarcTrigger(this.args.projectId, key, definition.eventTrigger);
|
|
465
|
+
delete this.triggers[key];
|
|
466
|
+
break;
|
|
467
|
+
case constants_1.Constants.SERVICE_FIREALERTS:
|
|
468
|
+
await this.removeFirealertsTrigger(this.args.projectId, key, definition.eventTrigger);
|
|
469
|
+
delete this.triggers[key];
|
|
470
|
+
break;
|
|
471
|
+
default:
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
445
476
|
addEventarcTrigger(projectId, key, eventTrigger) {
|
|
446
477
|
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
|
|
447
478
|
return Promise.resolve(false);
|
|
@@ -458,6 +489,54 @@ class FunctionsEmulator {
|
|
|
458
489
|
return false;
|
|
459
490
|
});
|
|
460
491
|
}
|
|
492
|
+
removeEventarcTrigger(projectId, key, eventTrigger) {
|
|
493
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
|
|
494
|
+
return Promise.resolve(false);
|
|
495
|
+
}
|
|
496
|
+
const bundle = {
|
|
497
|
+
eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "eventarc.googleapis.com" }),
|
|
498
|
+
};
|
|
499
|
+
logger_1.logger.debug(`removeEventarcTrigger`, JSON.stringify(bundle));
|
|
500
|
+
return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
|
|
501
|
+
.post(`/emulator/v1/remove/projects/${projectId}/triggers/${key}`, bundle)
|
|
502
|
+
.then(() => true)
|
|
503
|
+
.catch((err) => {
|
|
504
|
+
this.logger.log("WARN", "Error removing Eventarc function: " + err);
|
|
505
|
+
return false;
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
addFirealertsTrigger(projectId, key, eventTrigger) {
|
|
509
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
|
|
510
|
+
return Promise.resolve(false);
|
|
511
|
+
}
|
|
512
|
+
const bundle = {
|
|
513
|
+
eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firebasealerts.googleapis.com" }),
|
|
514
|
+
};
|
|
515
|
+
logger_1.logger.debug(`addFirealertsTrigger`, JSON.stringify(bundle));
|
|
516
|
+
return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
|
|
517
|
+
.post(`/emulator/v1/projects/${projectId}/triggers/${key}`, bundle)
|
|
518
|
+
.then(() => true)
|
|
519
|
+
.catch((err) => {
|
|
520
|
+
this.logger.log("WARN", "Error adding FireAlerts function: " + err);
|
|
521
|
+
return false;
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
removeFirealertsTrigger(projectId, key, eventTrigger) {
|
|
525
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
|
|
526
|
+
return Promise.resolve(false);
|
|
527
|
+
}
|
|
528
|
+
const bundle = {
|
|
529
|
+
eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firebasealerts.googleapis.com" }),
|
|
530
|
+
};
|
|
531
|
+
logger_1.logger.debug(`removeFirealertsTrigger`, JSON.stringify(bundle));
|
|
532
|
+
return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
|
|
533
|
+
.post(`/emulator/v1/remove/projects/${projectId}/triggers/${key}`, bundle)
|
|
534
|
+
.then(() => true)
|
|
535
|
+
.catch((err) => {
|
|
536
|
+
this.logger.log("WARN", "Error removing FireAlerts function: " + err);
|
|
537
|
+
return false;
|
|
538
|
+
});
|
|
539
|
+
}
|
|
461
540
|
async performPostLoadOperations() {
|
|
462
541
|
if (!this.blockingFunctionsConfig.triggers &&
|
|
463
542
|
!this.blockingFunctionsConfig.forwardInboundCredentials) {
|
|
@@ -17,6 +17,7 @@ const events = require("../functions/events");
|
|
|
17
17
|
const utils_1 = require("../utils");
|
|
18
18
|
const V2_EVENTS = [
|
|
19
19
|
events.v2.PUBSUB_PUBLISH_EVENT,
|
|
20
|
+
events.v2.FIREALERTS_EVENT,
|
|
20
21
|
...events.v2.STORAGE_EVENTS,
|
|
21
22
|
...events.v2.DATABASE_EVENTS,
|
|
22
23
|
...events.v2.FIRESTORE_EVENTS,
|
|
@@ -204,6 +205,9 @@ function getServiceFromEventType(eventType) {
|
|
|
204
205
|
if (eventType.includes("storage")) {
|
|
205
206
|
return constants_1.Constants.SERVICE_STORAGE;
|
|
206
207
|
}
|
|
208
|
+
if (eventType.includes("firebasealerts")) {
|
|
209
|
+
return constants_1.Constants.SERVICE_FIREALERTS;
|
|
210
|
+
}
|
|
207
211
|
if (eventType.includes("analytics")) {
|
|
208
212
|
return constants_1.Constants.SERVICE_ANALYTICS;
|
|
209
213
|
}
|
|
@@ -132,7 +132,7 @@ class PubsubEmulator {
|
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
createCloudEventRequestBody(topic, message) {
|
|
135
|
-
const truncatedPublishTime = new Date(message.publishTime.
|
|
135
|
+
const truncatedPublishTime = new Date(message.publishTime.getTime()).toISOString();
|
|
136
136
|
const data = {
|
|
137
137
|
message: {
|
|
138
138
|
messageId: message.id,
|
package/lib/experiments.js
CHANGED
|
@@ -102,6 +102,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
102
102
|
default: true,
|
|
103
103
|
public: false,
|
|
104
104
|
},
|
|
105
|
+
fdccompatiblemode: {
|
|
106
|
+
shortDescription: "Enable Data Connect schema migrations in Compatible Mode",
|
|
107
|
+
fullDescription: "Enable Data Connect schema migrations in Compatible Mode",
|
|
108
|
+
public: false,
|
|
109
|
+
},
|
|
105
110
|
});
|
|
106
111
|
function isValidExperiment(name) {
|
|
107
112
|
return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getAngularVersion = exports.getBuildConfig = exports.getServerConfig = exports.getBrowserConfig = exports.getContext = exports.getAllTargets = void 0;
|
|
3
|
+
exports.tryToGetOptionsForTarget = exports.getAngularVersion = exports.getBuildConfig = exports.getServerConfig = exports.getBrowserConfig = exports.getContext = exports.getAllTargets = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const error_1 = require("../../error");
|
|
6
6
|
const path_1 = require("path");
|
|
@@ -144,8 +144,9 @@ async function getContext(dir, targetOrConfiguration) {
|
|
|
144
144
|
if (apps.length === 1)
|
|
145
145
|
project = apps[0];
|
|
146
146
|
}
|
|
147
|
-
if (!project)
|
|
148
|
-
|
|
147
|
+
if (!project) {
|
|
148
|
+
throwCannotDetermineTarget();
|
|
149
|
+
}
|
|
149
150
|
const workspaceProject = workspace.projects.get(project);
|
|
150
151
|
if (!workspaceProject)
|
|
151
152
|
throw new error_1.FirebaseError(`No project ${project} found.`);
|
|
@@ -335,14 +336,14 @@ async function getContext(dir, targetOrConfiguration) {
|
|
|
335
336
|
if (!buildOrBrowserTarget) {
|
|
336
337
|
throw new error_1.FirebaseError(`No build target on ${project}`);
|
|
337
338
|
}
|
|
338
|
-
const browserTargetOptions = await architectHost
|
|
339
|
+
const browserTargetOptions = await tryToGetOptionsForTarget(architectHost, buildOrBrowserTarget);
|
|
339
340
|
if (!browserTargetOptions) {
|
|
340
341
|
const targetString = targetStringFromTarget(buildOrBrowserTarget);
|
|
341
342
|
throw new error_1.FirebaseError(`Couldn't find options for ${targetString}.`);
|
|
342
343
|
}
|
|
343
344
|
const baseHref = browserTargetOptions.baseHref || "/";
|
|
344
345
|
(0, utils_2.assertIsString)(baseHref);
|
|
345
|
-
const buildTargetOptions = buildTarget && (await architectHost
|
|
346
|
+
const buildTargetOptions = buildTarget && (await tryToGetOptionsForTarget(architectHost, buildTarget));
|
|
346
347
|
const ssr = buildTarget ? !!(buildTargetOptions === null || buildTargetOptions === void 0 ? void 0 : buildTargetOptions.ssr) : !!serverTarget;
|
|
347
348
|
return {
|
|
348
349
|
architect,
|
|
@@ -467,3 +468,10 @@ function getAngularVersion(cwd) {
|
|
|
467
468
|
return angularVersionSemver.toString();
|
|
468
469
|
}
|
|
469
470
|
exports.getAngularVersion = getAngularVersion;
|
|
471
|
+
async function tryToGetOptionsForTarget(architectHost, target) {
|
|
472
|
+
return await architectHost.getOptionsForTarget(target).catch(throwCannotDetermineTarget);
|
|
473
|
+
}
|
|
474
|
+
exports.tryToGetOptionsForTarget = tryToGetOptionsForTarget;
|
|
475
|
+
function throwCannotDetermineTarget(error) {
|
|
476
|
+
throw new error_1.FirebaseError(`Unable to determine the application to deploy, specify a target via the FIREBASE_FRAMEWORKS_BUILD_TARGET environment variable.`, { original: error });
|
|
477
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
|
|
3
|
+
exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIREALERTS_EVENT = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
|
|
4
4
|
exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
|
|
5
5
|
exports.STORAGE_EVENTS = [
|
|
6
6
|
"google.cloud.storage.object.v1.finalized",
|
|
@@ -27,5 +27,6 @@ exports.FIRESTORE_EVENTS = [
|
|
|
27
27
|
"google.cloud.firestore.document.v1.updated.withAuthContext",
|
|
28
28
|
"google.cloud.firestore.document.v1.deleted.withAuthContext",
|
|
29
29
|
];
|
|
30
|
+
exports.FIREALERTS_EVENT = "google.firebase.firebasealerts.alerts.v1.published";
|
|
30
31
|
exports.FIRESTORE_EVENT_REGEX = /^google\.cloud\.firestore\.document\.v1\.[^\.]*$/;
|
|
31
32
|
exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = /^google\.cloud\.firestore\.document\.v1\..*\.withAuthContext$/;
|
package/lib/gcp/devConnect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateP4SA = exports.serviceAgentEmail = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.listAllBranches = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
|
|
3
|
+
exports.generateP4SA = exports.serviceAgentEmail = exports.sortConnectionsByCreateTime = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.fetchGitHubInstallations = exports.listAllBranches = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
|
|
4
4
|
const apiv2_1 = require("../apiv2");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
6
|
const serviceusage_1 = require("./serviceusage");
|
|
@@ -95,6 +95,12 @@ async function listAllBranches(repoLinkName) {
|
|
|
95
95
|
return branches;
|
|
96
96
|
}
|
|
97
97
|
exports.listAllBranches = listAllBranches;
|
|
98
|
+
async function fetchGitHubInstallations(projectId, location, connectionId) {
|
|
99
|
+
const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}:fetchGitHubInstallations`;
|
|
100
|
+
const res = await exports.client.get(name);
|
|
101
|
+
return res.body.installations;
|
|
102
|
+
}
|
|
103
|
+
exports.fetchGitHubInstallations = fetchGitHubInstallations;
|
|
98
104
|
async function createGitRepositoryLink(projectId, location, connectionId, gitRepositoryLinkId, cloneUri) {
|
|
99
105
|
const res = await exports.client.post(`projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}/gitRepositoryLinks`, { cloneUri }, { queryParams: { gitRepositoryLinkId } });
|
|
100
106
|
return res.body;
|
|
@@ -106,6 +112,12 @@ async function getGitRepositoryLink(projectId, location, connectionId, gitReposi
|
|
|
106
112
|
return res.body;
|
|
107
113
|
}
|
|
108
114
|
exports.getGitRepositoryLink = getGitRepositoryLink;
|
|
115
|
+
function sortConnectionsByCreateTime(connections) {
|
|
116
|
+
return connections.sort((a, b) => {
|
|
117
|
+
return Date.parse(a.createTime) - Date.parse(b.createTime);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
exports.sortConnectionsByCreateTime = sortConnectionsByCreateTime;
|
|
109
121
|
function serviceAgentEmail(projectNumber) {
|
|
110
122
|
return `service-${projectNumber}@${(0, api_1.developerConnectP4SADomain)()}`;
|
|
111
123
|
}
|
|
@@ -52,7 +52,7 @@ async function doSetup(setup, config, options) {
|
|
|
52
52
|
if (!account) {
|
|
53
53
|
throw new error_1.FirebaseError(`No account selected, have you run "firebase login"?`, { exit: 1 });
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
(0, auth_1.setActiveAccount)(options, account);
|
|
56
56
|
if (config.projectDir) {
|
|
57
57
|
(0, auth_1.setProjectAccount)(config.projectDir, account.user.email);
|
|
58
58
|
}
|