firebase-tools 15.14.0 → 15.15.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 +2 -6
- package/lib/command.js +23 -29
- package/lib/commands/dataconnect-compile.js +6 -6
- package/lib/commands/dataconnect-execute.js +1 -1
- package/lib/commands/dataconnect-sdk-generate.js +3 -3
- package/lib/commands/dataconnect-services-list.js +1 -1
- package/lib/commands/dataconnect-sql-diff.js +3 -3
- package/lib/commands/dataconnect-sql-grant.js +2 -2
- package/lib/commands/dataconnect-sql-migrate.js +4 -4
- package/lib/commands/dataconnect-sql-setup.js +2 -2
- package/lib/commands/dataconnect-sql-shell.js +4 -4
- package/lib/commands/ext-dev-usage.js +0 -2
- package/lib/commands/init.js +3 -3
- package/lib/commands/open.js +1 -1
- package/lib/commands/setup-emulators-dataconnect.js +1 -1
- package/lib/dataconnect/freeTrial.js +2 -2
- package/lib/dataconnect/load.js +2 -2
- package/lib/dataconnect/provisionCloudSql.js +3 -3
- package/lib/dataconnect/schemaMigration.js +11 -11
- package/lib/dataconnect/webhook.js +1 -1
- package/lib/deploy/functions/backend.js +1 -1
- package/lib/deploy/functions/build.js +3 -3
- package/lib/deploy/functions/deploy.js +5 -2
- package/lib/deploy/functions/prepare.js +53 -0
- package/lib/deploy/functions/release/fabricator.js +59 -46
- package/lib/deploy/functions/release/planner.js +1 -1
- package/lib/deploy/functions/runtimes/dart/index.js +3 -1
- package/lib/deploy/functions/runtimes/dart/triggerSupport.js +123 -0
- package/lib/deploy/functions/runtimes/index.js +1 -4
- package/lib/deploy/functions/services/ailogic.js +12 -1
- package/lib/deploy/functions/services/dataconnect.js +1 -1
- package/lib/emulator/constants.js +1 -1
- package/lib/emulator/controller.js +2 -2
- package/lib/emulator/dataconnectEmulator.js +14 -14
- package/lib/emulator/dataconnectToolkitController.js +2 -2
- package/lib/emulator/downloadableEmulatorInfo.json +31 -31
- package/lib/emulator/extensionsEmulator.js +1 -2
- package/lib/emulator/functionsEmulator.js +15 -0
- package/lib/emulator/hub.js +3 -3
- package/lib/emulator/hubExport.js +2 -2
- package/lib/emulator/initEmulators.js +1 -1
- package/lib/experiments.js +10 -5
- package/lib/gcp/ailogic.js +6 -3
- package/lib/gcp/runv2.js +9 -2
- package/lib/init/features/dataconnect/index.js +7 -7
- package/lib/init/features/dataconnect/resolver.js +2 -2
- package/lib/init/features/dataconnect/sdk.js +8 -8
- package/lib/init/features/functions/index.js +1 -1
- package/lib/mcp/index.js +0 -1
- package/lib/mcp/prompts/dataconnect/schema.js +6 -6
- package/lib/mcp/resources/guides/init_data_connect.js +2 -2
- package/lib/mcp/resources/guides/init_firestore.js +1 -1
- package/lib/mcp/tools/core/init.js +9 -9
- package/lib/mcp/tools/dataconnect/compile.js +5 -5
- package/lib/mcp/tools/dataconnect/execute.js +6 -6
- package/lib/mcp/tools/dataconnect/list_services.js +7 -7
- package/lib/mcp/util/dataconnect/content.js +19 -19
- package/lib/tsconfig.compile.tsbuildinfo +1 -1
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +16 -4
- package/templates/init/functions/dart/pubspec.yaml +6 -7
- package/templates/init/functions/dart/server.dart +3 -1
- package/lib/shortenUrl.js +0 -28
|
@@ -68,68 +68,81 @@ class Fabricator {
|
|
|
68
68
|
totalTime: 0,
|
|
69
69
|
results: [],
|
|
70
70
|
};
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const changesets = Object.values(plan);
|
|
72
|
+
const scraperV1 = new sourceTokenScraper_1.SourceTokenScraper();
|
|
73
|
+
const scraperV2 = new sourceTokenScraper_1.SourceTokenScraper();
|
|
74
|
+
const createAndUpdatePromises = changesets.map((changes) => {
|
|
75
|
+
return this.applyUpserts(changes, scraperV1, scraperV2);
|
|
75
76
|
});
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
logger_1.logger.debug("Fabricator.
|
|
82
|
-
|
|
77
|
+
const createAndUpdateResultsArray = await Promise.allSettled(createAndUpdatePromises);
|
|
78
|
+
summary.results = createAndUpdateResultsArray.reduce((acc, r) => {
|
|
79
|
+
if (r.status === "fulfilled") {
|
|
80
|
+
return [...acc, ...r.value];
|
|
81
|
+
}
|
|
82
|
+
logger_1.logger.debug("Fabricator.applyUpserts returned an unhandled exception.", JSON.stringify(r.reason, null, 2));
|
|
83
|
+
return acc;
|
|
84
|
+
}, []);
|
|
85
|
+
const hasFailures = summary.results.some((r) => r.error);
|
|
86
|
+
if (hasFailures) {
|
|
87
|
+
utils.logLabeledWarning("functions", "Deploys failed. Skipping deletes.");
|
|
88
|
+
summary.results = changesets.reduce((accum, changes) => {
|
|
89
|
+
const currentAborts = changes.endpointsToDelete.map((endpoint) => ({
|
|
90
|
+
endpoint,
|
|
91
|
+
durationMs: 0,
|
|
92
|
+
error: new reporter.AbortedDeploymentError(endpoint),
|
|
93
|
+
}));
|
|
94
|
+
return [...accum, ...currentAborts];
|
|
95
|
+
}, summary.results);
|
|
96
|
+
summary.totalTime = timer.stop();
|
|
97
|
+
return summary;
|
|
98
|
+
}
|
|
99
|
+
const deleteResultsArray = await Promise.allSettled(changesets.map((changes) => this.applyDeletes(changes)));
|
|
100
|
+
const deleteResults = deleteResultsArray.reduce((acc, r) => {
|
|
101
|
+
if (r.status === "fulfilled") {
|
|
102
|
+
return [...acc, ...r.value];
|
|
103
|
+
}
|
|
104
|
+
logger_1.logger.debug("Fabricator.applyDeletes returned an unhandled exception. This should never happen", JSON.stringify(r.reason, null, 2));
|
|
105
|
+
return acc;
|
|
106
|
+
}, []);
|
|
107
|
+
summary.results.push(...deleteResults);
|
|
83
108
|
summary.totalTime = timer.stop();
|
|
84
109
|
return summary;
|
|
85
110
|
}
|
|
86
|
-
async
|
|
87
|
-
const
|
|
88
|
-
const handle = async (op, endpoint, fn) => {
|
|
89
|
-
const timer = new timer_1.Timer();
|
|
90
|
-
const result = { endpoint };
|
|
91
|
-
try {
|
|
92
|
-
await fn();
|
|
93
|
-
this.logOpSuccess(op, endpoint);
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
result.error = err;
|
|
97
|
-
}
|
|
98
|
-
result.durationMs = timer.stop();
|
|
99
|
-
deployResults.push(result);
|
|
100
|
-
};
|
|
101
|
-
const upserts = [];
|
|
102
|
-
const scraperV1 = new sourceTokenScraper_1.SourceTokenScraper();
|
|
103
|
-
const scraperV2 = new sourceTokenScraper_1.SourceTokenScraper();
|
|
111
|
+
async applyUpserts(changes, scraperV1, scraperV2) {
|
|
112
|
+
const ops = [];
|
|
104
113
|
for (const endpoint of changes.endpointsToCreate) {
|
|
105
114
|
this.logOpStart("creating", endpoint);
|
|
106
|
-
|
|
115
|
+
ops.push(this.wrapOperation("create", endpoint, () => this.createEndpoint(endpoint, scraperV1, scraperV2)));
|
|
107
116
|
}
|
|
108
117
|
for (const endpoint of changes.endpointsToSkip) {
|
|
109
118
|
utils.logSuccess(this.getLogSuccessMessage("skip", endpoint));
|
|
110
119
|
}
|
|
111
120
|
for (const update of changes.endpointsToUpdate) {
|
|
112
121
|
this.logOpStart("updating", update.endpoint);
|
|
113
|
-
|
|
122
|
+
ops.push(this.wrapOperation("update", update.endpoint, () => this.updateEndpoint(update, scraperV1, scraperV2)));
|
|
114
123
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
endpoint,
|
|
120
|
-
durationMs: 0,
|
|
121
|
-
error: new reporter.AbortedDeploymentError(endpoint),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
return deployResults;
|
|
125
|
-
}
|
|
126
|
-
const deletes = [];
|
|
124
|
+
return Promise.all(ops);
|
|
125
|
+
}
|
|
126
|
+
async applyDeletes(changes) {
|
|
127
|
+
const ops = [];
|
|
127
128
|
for (const endpoint of changes.endpointsToDelete) {
|
|
128
129
|
this.logOpStart("deleting", endpoint);
|
|
129
|
-
|
|
130
|
+
ops.push(this.wrapOperation("delete", endpoint, () => this.deleteEndpoint(endpoint)));
|
|
131
|
+
}
|
|
132
|
+
return Promise.all(ops);
|
|
133
|
+
}
|
|
134
|
+
async wrapOperation(op, endpoint, fn) {
|
|
135
|
+
const timer = new timer_1.Timer();
|
|
136
|
+
const result = { endpoint };
|
|
137
|
+
try {
|
|
138
|
+
await fn();
|
|
139
|
+
this.logOpSuccess(op, endpoint);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
result.error = err;
|
|
130
143
|
}
|
|
131
|
-
|
|
132
|
-
return
|
|
144
|
+
result.durationMs = timer.stop();
|
|
145
|
+
return result;
|
|
133
146
|
}
|
|
134
147
|
async createEndpoint(endpoint, scraperV1, scraperV2) {
|
|
135
148
|
endpoint.labels = { ...endpoint.labels, ...deploymentTool.labels() };
|
|
@@ -178,7 +178,7 @@ function checkForIllegalUpdate(want, have) {
|
|
|
178
178
|
return "an HTTPS";
|
|
179
179
|
}
|
|
180
180
|
else if (backend.isDataConnectGraphqlTriggered(e)) {
|
|
181
|
-
return "a
|
|
181
|
+
return "a SQL Connect HTTPS";
|
|
182
182
|
}
|
|
183
183
|
else if (backend.isCallableTriggered(e)) {
|
|
184
184
|
return "a callable";
|
|
@@ -13,12 +13,14 @@ const error_1 = require("../../../../error");
|
|
|
13
13
|
const utils_1 = require("../../../../utils");
|
|
14
14
|
const registry_1 = require("../../../../emulator/registry");
|
|
15
15
|
const types_1 = require("../../../../emulator/types");
|
|
16
|
+
const experiments = require("../../../../experiments");
|
|
16
17
|
async function tryCreateDelegate(context) {
|
|
17
18
|
const pubspecYamlPath = path.join(context.sourceDir, "pubspec.yaml");
|
|
18
19
|
if (!(await (0, util_1.promisify)(fs.exists)(pubspecYamlPath))) {
|
|
19
20
|
logger_1.logger.debug("Customer code is not Dart code.");
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
23
|
+
experiments.assertEnabled("dartfunctions", "use Dart functions");
|
|
22
24
|
const runtime = context.runtime ?? supported.latest("dart");
|
|
23
25
|
if (!supported.isRuntime(runtime)) {
|
|
24
26
|
throw new error_1.FirebaseError(`Runtime ${runtime} is not a valid Dart runtime`);
|
|
@@ -28,7 +30,7 @@ async function tryCreateDelegate(context) {
|
|
|
28
30
|
}
|
|
29
31
|
return Promise.resolve(new Delegate(context.projectId, context.sourceDir, runtime));
|
|
30
32
|
}
|
|
31
|
-
const MIN_DART_SDK_VERSION = "3.
|
|
33
|
+
const MIN_DART_SDK_VERSION = "3.9.0";
|
|
32
34
|
exports.DART_ENTRY_POINT = "bin/server.dart";
|
|
33
35
|
class Delegate {
|
|
34
36
|
constructor(projectId, sourceDir, runtime) {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.endpointSupportLevel = endpointSupportLevel;
|
|
4
|
+
exports.isDartEndpoint = isDartEndpoint;
|
|
5
|
+
exports.classifyNonProductionEndpoints = classifyNonProductionEndpoints;
|
|
6
|
+
exports.triggerTypeLabel = triggerTypeLabel;
|
|
7
|
+
exports.groupByTriggerLabel = groupByTriggerLabel;
|
|
8
|
+
const backend = require("../../backend");
|
|
9
|
+
const supported = require("../supported");
|
|
10
|
+
const constants_1 = require("../../../../emulator/constants");
|
|
11
|
+
const EMULATOR_ONLY_SERVICES = new Set([
|
|
12
|
+
constants_1.Constants.SERVICE_FIRESTORE,
|
|
13
|
+
constants_1.Constants.SERVICE_REALTIME_DATABASE,
|
|
14
|
+
constants_1.Constants.SERVICE_STORAGE,
|
|
15
|
+
]);
|
|
16
|
+
const EXPERIMENTAL_SERVICES = new Set([
|
|
17
|
+
constants_1.Constants.SERVICE_PUBSUB,
|
|
18
|
+
constants_1.Constants.SERVICE_EVENTARC,
|
|
19
|
+
constants_1.Constants.SERVICE_AUTH,
|
|
20
|
+
constants_1.Constants.SERVICE_FIREALERTS,
|
|
21
|
+
constants_1.Constants.SERVICE_REMOTE_CONFIG,
|
|
22
|
+
constants_1.Constants.SERVICE_TEST_LAB,
|
|
23
|
+
constants_1.Constants.SERVICE_CLOUD_TASKS,
|
|
24
|
+
]);
|
|
25
|
+
function endpointSupportLevel(ep) {
|
|
26
|
+
if (backend.isHttpsTriggered(ep) || backend.isCallableTriggered(ep)) {
|
|
27
|
+
if (backend.isTaskQueueTriggered(ep)) {
|
|
28
|
+
return "experimental";
|
|
29
|
+
}
|
|
30
|
+
return "production";
|
|
31
|
+
}
|
|
32
|
+
if (backend.isScheduleTriggered(ep)) {
|
|
33
|
+
return "experimental";
|
|
34
|
+
}
|
|
35
|
+
if (backend.isBlockingTriggered(ep)) {
|
|
36
|
+
return "experimental";
|
|
37
|
+
}
|
|
38
|
+
if (backend.isEventTriggered(ep)) {
|
|
39
|
+
if (ep.eventTrigger.channel) {
|
|
40
|
+
return "experimental";
|
|
41
|
+
}
|
|
42
|
+
const service = ep.eventTrigger.eventType
|
|
43
|
+
? serviceFromEventType(ep.eventTrigger.eventType)
|
|
44
|
+
: undefined;
|
|
45
|
+
if (service && EMULATOR_ONLY_SERVICES.has(service)) {
|
|
46
|
+
return "emulatorOnly";
|
|
47
|
+
}
|
|
48
|
+
if (service && EXPERIMENTAL_SERVICES.has(service)) {
|
|
49
|
+
return "experimental";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return "experimental";
|
|
53
|
+
}
|
|
54
|
+
function isDartEndpoint(ep) {
|
|
55
|
+
return supported.runtimeIsLanguage(ep.runtime, "dart");
|
|
56
|
+
}
|
|
57
|
+
function classifyNonProductionEndpoints(endpoints) {
|
|
58
|
+
const emulatorOnly = [];
|
|
59
|
+
const experimental = [];
|
|
60
|
+
for (const ep of endpoints) {
|
|
61
|
+
switch (endpointSupportLevel(ep)) {
|
|
62
|
+
case "production":
|
|
63
|
+
break;
|
|
64
|
+
case "emulatorOnly":
|
|
65
|
+
emulatorOnly.push(ep);
|
|
66
|
+
break;
|
|
67
|
+
case "experimental":
|
|
68
|
+
experimental.push(ep);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { emulatorOnly, experimental };
|
|
73
|
+
}
|
|
74
|
+
function triggerTypeLabel(ep) {
|
|
75
|
+
if (backend.isScheduleTriggered(ep))
|
|
76
|
+
return "scheduler";
|
|
77
|
+
if (backend.isTaskQueueTriggered(ep))
|
|
78
|
+
return "tasks";
|
|
79
|
+
if (backend.isBlockingTriggered(ep))
|
|
80
|
+
return "identity";
|
|
81
|
+
if (backend.isCallableTriggered(ep))
|
|
82
|
+
return "callable";
|
|
83
|
+
if (backend.isHttpsTriggered(ep))
|
|
84
|
+
return "https";
|
|
85
|
+
if (backend.isEventTriggered(ep)) {
|
|
86
|
+
if (ep.eventTrigger.channel)
|
|
87
|
+
return "eventarc";
|
|
88
|
+
const svc = serviceFromEventType(ep.eventTrigger.eventType);
|
|
89
|
+
return svc ? constants_1.Constants.getServiceName(svc) : ep.eventTrigger.eventType;
|
|
90
|
+
}
|
|
91
|
+
return "unknown";
|
|
92
|
+
}
|
|
93
|
+
function groupByTriggerLabel(endpoints) {
|
|
94
|
+
const groups = new Map();
|
|
95
|
+
for (const ep of endpoints) {
|
|
96
|
+
const label = triggerTypeLabel(ep);
|
|
97
|
+
const ids = groups.get(label) ?? [];
|
|
98
|
+
ids.push(ep.id);
|
|
99
|
+
groups.set(label, ids);
|
|
100
|
+
}
|
|
101
|
+
return groups;
|
|
102
|
+
}
|
|
103
|
+
function serviceFromEventType(eventType) {
|
|
104
|
+
if (eventType.includes("firestore"))
|
|
105
|
+
return constants_1.Constants.SERVICE_FIRESTORE;
|
|
106
|
+
if (eventType.includes("database"))
|
|
107
|
+
return constants_1.Constants.SERVICE_REALTIME_DATABASE;
|
|
108
|
+
if (eventType.includes("pubsub"))
|
|
109
|
+
return constants_1.Constants.SERVICE_PUBSUB;
|
|
110
|
+
if (eventType.includes("storage"))
|
|
111
|
+
return constants_1.Constants.SERVICE_STORAGE;
|
|
112
|
+
if (eventType.includes("eventarc"))
|
|
113
|
+
return constants_1.Constants.SERVICE_EVENTARC;
|
|
114
|
+
if (eventType.includes("firebasealerts"))
|
|
115
|
+
return constants_1.Constants.SERVICE_FIREALERTS;
|
|
116
|
+
if (eventType.includes("auth"))
|
|
117
|
+
return constants_1.Constants.SERVICE_AUTH;
|
|
118
|
+
if (eventType.includes("remoteconfig"))
|
|
119
|
+
return constants_1.Constants.SERVICE_REMOTE_CONFIG;
|
|
120
|
+
if (eventType.includes("testlab") || eventType.includes("testing"))
|
|
121
|
+
return constants_1.Constants.SERVICE_TEST_LAB;
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
@@ -7,13 +7,10 @@ const python = require("./python");
|
|
|
7
7
|
const validate = require("../validate");
|
|
8
8
|
const error_1 = require("../../../error");
|
|
9
9
|
const supported = require("./supported");
|
|
10
|
-
const experiments = require("../../../experiments");
|
|
11
10
|
const factories = [
|
|
12
11
|
node.tryCreateDelegate,
|
|
13
12
|
python.tryCreateDelegate,
|
|
14
|
-
|
|
15
|
-
? dart.tryCreateDelegate(ctx)
|
|
16
|
-
: Promise.resolve(undefined),
|
|
13
|
+
dart.tryCreateDelegate,
|
|
17
14
|
];
|
|
18
15
|
async function getRuntimeDelegate(context) {
|
|
19
16
|
const { projectDir, sourceDir, runtime } = context;
|
|
@@ -8,6 +8,7 @@ const ailogicApi = require("../../../gcp/ailogic");
|
|
|
8
8
|
const ailogic_1 = require("../../../gcp/ailogic");
|
|
9
9
|
Object.defineProperty(exports, "AI_LOGIC_BEFORE_GENERATE_CONTENT", { enumerable: true, get: function () { return ailogic_1.AI_LOGIC_BEFORE_GENERATE_CONTENT; } });
|
|
10
10
|
Object.defineProperty(exports, "AI_LOGIC_AFTER_GENERATE_CONTENT", { enumerable: true, get: function () { return ailogic_1.AI_LOGIC_AFTER_GENERATE_CONTENT; } });
|
|
11
|
+
const utils_1 = require("../../../utils");
|
|
11
12
|
exports.AI_LOGIC_EVENTS = [
|
|
12
13
|
ailogic_1.AI_LOGIC_BEFORE_GENERATE_CONTENT,
|
|
13
14
|
ailogic_1.AI_LOGIC_AFTER_GENERATE_CONTENT,
|
|
@@ -63,7 +64,17 @@ class AILogicService {
|
|
|
63
64
|
if (!isAILogicEvent(ep)) {
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
+
try {
|
|
68
|
+
await ailogicApi.deleteBlockingFunction(ep);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if ((0, error_1.getErrStatus)(err) === 404) {
|
|
72
|
+
(0, utils_1.logLabeledWarning)("functions", `Tried deleting trigger registration for function ${ep.id} but it is not currently registered`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
67
78
|
}
|
|
68
79
|
}
|
|
69
80
|
exports.AILogicService = AILogicService;
|
|
@@ -12,7 +12,7 @@ function ensureDataConnectTriggerRegion(endpoint) {
|
|
|
12
12
|
endpoint.eventTrigger.region = endpoint.region;
|
|
13
13
|
}
|
|
14
14
|
if (endpoint.eventTrigger.region !== endpoint.region) {
|
|
15
|
-
throw new error_1.FirebaseError("The Firebase
|
|
15
|
+
throw new error_1.FirebaseError("The Firebase SQL Connect trigger location must match the function region.");
|
|
16
16
|
}
|
|
17
17
|
return Promise.resolve();
|
|
18
18
|
}
|
|
@@ -49,7 +49,7 @@ exports.EMULATOR_DESCRIPTION = {
|
|
|
49
49
|
storage: "Storage Emulator",
|
|
50
50
|
extensions: "Extensions Emulator",
|
|
51
51
|
eventarc: "Eventarc Emulator",
|
|
52
|
-
dataconnect: "
|
|
52
|
+
dataconnect: "SQL Connect Emulator",
|
|
53
53
|
tasks: "Cloud Tasks Emulator",
|
|
54
54
|
};
|
|
55
55
|
exports.DEFAULT_HOST = "localhost";
|
|
@@ -567,10 +567,10 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
567
567
|
if (listenForEmulator.dataconnect) {
|
|
568
568
|
const config = (0, load_1.readFirebaseJson)(options.config);
|
|
569
569
|
if (!config.length) {
|
|
570
|
-
throw new error_1.FirebaseError("No
|
|
570
|
+
throw new error_1.FirebaseError("No SQL Connect service found in firebase.json");
|
|
571
571
|
}
|
|
572
572
|
else if (config.length > 1) {
|
|
573
|
-
logger_1.logger.warn(`TODO: Add support for multiple services in the
|
|
573
|
+
logger_1.logger.warn(`TODO: Add support for multiple services in the SQL Connect emulator. Currently emulating first service ${config[0].source}`);
|
|
574
574
|
}
|
|
575
575
|
const args = {
|
|
576
576
|
listen: listenForEmulator.dataconnect,
|
|
@@ -87,10 +87,10 @@ class DataConnectEmulator {
|
|
|
87
87
|
connStr = `postgres://${connectableHost}:${pgPort}/${dbId}?sslmode=disable`;
|
|
88
88
|
server.on("error", (err) => {
|
|
89
89
|
if (err instanceof error_1.FirebaseError) {
|
|
90
|
-
this.logger.logLabeled("ERROR", "
|
|
90
|
+
this.logger.logLabeled("ERROR", "SQL Connect", `${err}`);
|
|
91
91
|
}
|
|
92
92
|
else {
|
|
93
|
-
this.logger.logLabeled("ERROR", "dataconnect", `Postgres threw an unexpected error, shutting down the
|
|
93
|
+
this.logger.logLabeled("ERROR", "dataconnect", `Postgres threw an unexpected error, shutting down the SQL Connect emulator: ${err}`);
|
|
94
94
|
}
|
|
95
95
|
void (0, controller_1.cleanShutdown)();
|
|
96
96
|
});
|
|
@@ -103,14 +103,14 @@ class DataConnectEmulator {
|
|
|
103
103
|
async connect() {
|
|
104
104
|
const emuInfo = await this.emulatorClient.getInfo();
|
|
105
105
|
if (!emuInfo) {
|
|
106
|
-
this.logger.logLabeled("ERROR", "dataconnect", "Could not connect to
|
|
106
|
+
this.logger.logLabeled("ERROR", "dataconnect", "Could not connect to SQL Connect emulator. Check dataconnect-debug.log for more details.");
|
|
107
107
|
return Promise.reject();
|
|
108
108
|
}
|
|
109
109
|
return Promise.resolve();
|
|
110
110
|
}
|
|
111
111
|
async stop() {
|
|
112
112
|
if (this.usingExistingEmulator) {
|
|
113
|
-
this.logger.logLabeled("INFO", "dataconnect", "Skipping cleanup of
|
|
113
|
+
this.logger.logLabeled("INFO", "dataconnect", "Skipping cleanup of SQL Connect emulator, as it was not started by this process.");
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
if (this.postgresServer) {
|
|
@@ -149,7 +149,7 @@ class DataConnectEmulator {
|
|
|
149
149
|
await this.postgresServer.exportData(path.join(this.args.config.path(exportPath), "postgres.tar.gz"));
|
|
150
150
|
}
|
|
151
151
|
else {
|
|
152
|
-
throw new error_1.FirebaseError("The
|
|
152
|
+
throw new error_1.FirebaseError("The SQL Connect emulator is currently connected to a separate Postgres instance. Export is not supported.");
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
static async generate(args) {
|
|
@@ -176,7 +176,7 @@ class DataConnectEmulator {
|
|
|
176
176
|
}
|
|
177
177
|
catch (e) {
|
|
178
178
|
if ((0, downloadableEmulators_1.isIncomaptibleArchError)(e)) {
|
|
179
|
-
reject(new error_1.FirebaseError(`Unknown system error when running the
|
|
179
|
+
reject(new error_1.FirebaseError(`Unknown system error when running the SQL Connect toolkit. ` +
|
|
180
180
|
`You may be able to fix this by installing Rosetta: ` +
|
|
181
181
|
`softwareupdate --install-rosetta`));
|
|
182
182
|
}
|
|
@@ -195,17 +195,17 @@ class DataConnectEmulator {
|
|
|
195
195
|
const env = await DataConnectEmulator.getEnv(args.account);
|
|
196
196
|
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8", env });
|
|
197
197
|
if ((0, downloadableEmulators_1.isIncomaptibleArchError)(res.error)) {
|
|
198
|
-
throw new error_1.FirebaseError(`Unkown system error when running the
|
|
198
|
+
throw new error_1.FirebaseError(`Unkown system error when running the SQL Connect toolkit. ` +
|
|
199
199
|
`You may be able to fix this by installing Rosetta: ` +
|
|
200
200
|
`softwareupdate --install-rosetta`);
|
|
201
201
|
}
|
|
202
202
|
if (res.error) {
|
|
203
|
-
throw new error_1.FirebaseError(`Error starting up
|
|
203
|
+
throw new error_1.FirebaseError(`Error starting up SQL Connect build: ${res.error.message}`, {
|
|
204
204
|
original: res.error,
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
if (res.status !== 0) {
|
|
208
|
-
throw new error_1.FirebaseError(`Unable to build your
|
|
208
|
+
throw new error_1.FirebaseError(`Unable to build your SQL Connect schema and connectors (exit code ${res.status}): ${res.stderr}`);
|
|
209
209
|
}
|
|
210
210
|
if (res.stderr) {
|
|
211
211
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).log("DEBUG", res.stderr);
|
|
@@ -219,27 +219,27 @@ class DataConnectEmulator {
|
|
|
219
219
|
}
|
|
220
220
|
async connectToPostgres(connectionString, database, serviceId) {
|
|
221
221
|
if (!connectionString) {
|
|
222
|
-
const msg = `No Postgres connection found. The
|
|
222
|
+
const msg = `No Postgres connection found. The SQL Connect emulator will not be able to execute operations.`;
|
|
223
223
|
throw new error_1.FirebaseError(msg);
|
|
224
224
|
}
|
|
225
225
|
const MAX_RETRIES = 3;
|
|
226
226
|
for (let i = 1; i <= MAX_RETRIES; i++) {
|
|
227
227
|
try {
|
|
228
|
-
this.logger.logLabeled("DEBUG", "
|
|
228
|
+
this.logger.logLabeled("DEBUG", "SQL Connect", `Connecting to ${connectionString}}...`);
|
|
229
229
|
connectionString.toString();
|
|
230
230
|
await this.emulatorClient.configureEmulator({
|
|
231
231
|
connectionString: connectionString.toString(),
|
|
232
232
|
database,
|
|
233
233
|
serviceId,
|
|
234
234
|
});
|
|
235
|
-
this.logger.logLabeled("DEBUG", "
|
|
235
|
+
this.logger.logLabeled("DEBUG", "SQL Connect", `Successfully connected to ${connectionString}}`);
|
|
236
236
|
return true;
|
|
237
237
|
}
|
|
238
238
|
catch (err) {
|
|
239
239
|
if (i === MAX_RETRIES) {
|
|
240
240
|
throw err;
|
|
241
241
|
}
|
|
242
|
-
this.logger.logLabeled("DEBUG", "
|
|
242
|
+
this.logger.logLabeled("DEBUG", "SQL Connect", `Retrying connectToPostgress call (${i} of ${MAX_RETRIES} attempts): ${err}`);
|
|
243
243
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
244
244
|
}
|
|
245
245
|
}
|
|
@@ -271,7 +271,7 @@ class DataConnectEmulatorClient {
|
|
|
271
271
|
}
|
|
272
272
|
catch (err) {
|
|
273
273
|
if (err.status === 500) {
|
|
274
|
-
throw new error_1.FirebaseError(`
|
|
274
|
+
throw new error_1.FirebaseError(`SQL Connect emulator: ${err?.context?.body?.message}`);
|
|
275
275
|
}
|
|
276
276
|
throw err;
|
|
277
277
|
}
|
|
@@ -7,7 +7,7 @@ const portUtils = require("./portUtils");
|
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
const dataconnectEmulator_1 = require("./dataconnectEmulator");
|
|
9
9
|
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
10
|
-
const name = "
|
|
10
|
+
const name = "SQL Connect Toolkit";
|
|
11
11
|
class DataConnectToolkitController {
|
|
12
12
|
static async start(args) {
|
|
13
13
|
if (this.isRunning || this.instance) {
|
|
@@ -28,7 +28,7 @@ class DataConnectToolkitController {
|
|
|
28
28
|
this.isRunning = false;
|
|
29
29
|
}
|
|
30
30
|
catch (e) {
|
|
31
|
-
throw new error_1.FirebaseError(`
|
|
31
|
+
throw new error_1.FirebaseError(`SQL Connect Toolkit failed to stop with error: ${e}`);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
static getVersion() {
|
|
@@ -44,46 +44,46 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"pubsub": {
|
|
47
|
-
"version": "0.8.
|
|
48
|
-
"expectedSize":
|
|
49
|
-
"expectedChecksum": "
|
|
50
|
-
"expectedChecksumSHA256": "
|
|
51
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.
|
|
52
|
-
"downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.
|
|
53
|
-
"binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.
|
|
47
|
+
"version": "0.8.30",
|
|
48
|
+
"expectedSize": 52917173,
|
|
49
|
+
"expectedChecksum": "f7935320d11894d0c4cd26dd064ee42f",
|
|
50
|
+
"expectedChecksumSHA256": "aabf028a55ec3f06fe2223471dad26623be542848abebd7563f8f0b8697dd2a3",
|
|
51
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.30.zip",
|
|
52
|
+
"downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.30.zip",
|
|
53
|
+
"binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.30/pubsub-emulator/bin/cloud-pubsub-emulator"
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "3.4.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
57
|
+
"version": "3.4.5",
|
|
58
|
+
"expectedSize": 32269984,
|
|
59
|
+
"expectedChecksum": "5c50dbd6f508998f748195691b5b0678",
|
|
60
|
+
"expectedChecksumSHA256": "2053e5c47dbe52c923be196cfd7ff70b31f9e2fbde0e7eaeb20c31ee91e8a8b2",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.5",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "3.4.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
65
|
+
"version": "3.4.5",
|
|
66
|
+
"expectedSize": 30437250,
|
|
67
|
+
"expectedChecksum": "40f531afa3664284bc319f9721388816",
|
|
68
|
+
"expectedChecksumSHA256": "8dd59223a43b0e07ac2c7e40abb5e1a56ea7b7b787d23ceed20f8fe9af9c88e0",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.5",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "3.4.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
73
|
+
"version": "3.4.5",
|
|
74
|
+
"expectedSize": 32311296,
|
|
75
|
+
"expectedChecksum": "406caa49a4d48c03ade3da4a4a6b08b3",
|
|
76
|
+
"expectedChecksumSHA256": "e6e392f5bc3c32faa95d9b1139497492e51c46cb354e30e65c20d575ee3ba9cf",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.5",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.4.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
81
|
+
"version": "3.4.5",
|
|
82
|
+
"expectedSize": 31428792,
|
|
83
|
+
"expectedChecksum": "2e09522cff69142b6882e71516ad0e3c",
|
|
84
|
+
"expectedChecksumSHA256": "86565d7724762e45a76a4627791b60248cfea81f332d45709d69be0e71a3c6ec",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.5",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -12,7 +12,6 @@ const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
|
12
12
|
const error_1 = require("../error");
|
|
13
13
|
const optionsHelper_1 = require("../extensions/emulator/optionsHelper");
|
|
14
14
|
const refs_1 = require("../extensions/refs");
|
|
15
|
-
const shortenUrl_1 = require("../shortenUrl");
|
|
16
15
|
const constants_1 = require("./constants");
|
|
17
16
|
const download_1 = require("./download");
|
|
18
17
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
@@ -218,7 +217,7 @@ class ExtensionsEmulator {
|
|
|
218
217
|
style: { head: ["yellow"] },
|
|
219
218
|
});
|
|
220
219
|
for (const apiToWarn of apisToWarn) {
|
|
221
|
-
const enablementUri =
|
|
220
|
+
const enablementUri = (0, ensureApiEnabled_1.enableApiURI)(this.args.projectId, apiToWarn.apiName);
|
|
222
221
|
table.push([
|
|
223
222
|
apiToWarn.apiName,
|
|
224
223
|
apiToWarn.instanceIds.join(", "),
|
|
@@ -37,6 +37,7 @@ const env_1 = require("./env");
|
|
|
37
37
|
const python_1 = require("../functions/python");
|
|
38
38
|
const supported_1 = require("../deploy/functions/runtimes/supported");
|
|
39
39
|
const dart_1 = require("../deploy/functions/runtimes/dart");
|
|
40
|
+
const triggerSupport_1 = require("../deploy/functions/runtimes/dart/triggerSupport");
|
|
40
41
|
const EVENT_INVOKE_GA4 = "functions_invoke";
|
|
41
42
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
42
43
|
class IPCConn {
|
|
@@ -388,6 +389,7 @@ class FunctionsEmulator {
|
|
|
388
389
|
for (const e of endpoints) {
|
|
389
390
|
e.codebase = emulatableBackend.codebase;
|
|
390
391
|
}
|
|
392
|
+
this.logDartTriggerSupportWarnings(endpoints);
|
|
391
393
|
return (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
|
|
392
394
|
}
|
|
393
395
|
}
|
|
@@ -525,6 +527,19 @@ class FunctionsEmulator {
|
|
|
525
527
|
}
|
|
526
528
|
}
|
|
527
529
|
}
|
|
530
|
+
logDartTriggerSupportWarnings(endpoints) {
|
|
531
|
+
const dartEndpoints = endpoints.filter(triggerSupport_1.isDartEndpoint);
|
|
532
|
+
if (dartEndpoints.length === 0) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const { emulatorOnly, experimental } = (0, triggerSupport_1.classifyNonProductionEndpoints)(dartEndpoints);
|
|
536
|
+
(0, triggerSupport_1.groupByTriggerLabel)(emulatorOnly).forEach((ids, label) => {
|
|
537
|
+
this.logger.logLabeled("WARN", "functions", `Dart ${clc.bold(label)} triggers work in the emulator but cannot be deployed to production yet: ${ids.join(", ")}`);
|
|
538
|
+
});
|
|
539
|
+
(0, triggerSupport_1.groupByTriggerLabel)(experimental).forEach((ids, label) => {
|
|
540
|
+
this.logger.logLabeled("WARN", "functions", `Dart ${clc.bold(label)} triggers are experimental and not yet fully supported: ${ids.join(", ")}`);
|
|
541
|
+
});
|
|
542
|
+
}
|
|
528
543
|
async removeTriggers(toRemove) {
|
|
529
544
|
for (const triggerKey of toRemove) {
|
|
530
545
|
const definition = this.triggers[triggerKey].def;
|
package/lib/emulator/hub.js
CHANGED
|
@@ -117,13 +117,13 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
117
117
|
app.post(EmulatorHub.PATH_CLEAR_DATA_CONNECT, async (req, res) => {
|
|
118
118
|
if (req.headers.origin) {
|
|
119
119
|
res.status(403).json({
|
|
120
|
-
message: `Clear
|
|
120
|
+
message: `Clear SQL Connect cannot be triggered by external callers.`,
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
|
-
utils.logLabeledBullet("emulators", `Clearing data from
|
|
123
|
+
utils.logLabeledBullet("emulators", `Clearing data from SQL Connect data sources.`);
|
|
124
124
|
const instance = registry_1.EmulatorRegistry.get(types_1.Emulators.DATACONNECT);
|
|
125
125
|
if (!instance) {
|
|
126
|
-
res.status(400).json({ error: "The
|
|
126
|
+
res.status(400).json({ error: "The SQL Connect emulator is not running." });
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
await instance.clearData();
|