in src/emulator/functionsEmulator.ts [452:608]
async loadTriggers(emulatableBackend: EmulatableBackend, force = false): Promise<void> {
// Before loading any triggers we need to make sure there are no 'stale' workers
// in the pool that would cause us to run old code.
this.workerPool.refresh();
if (!emulatableBackend.nodeBinary) {
throw new FirebaseError(
`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`
);
}
const worker = this.invokeRuntime(
this.getBaseBundle(emulatableBackend),
{
nodeBinary: emulatableBackend.nodeBinary,
extensionTriggers: emulatableBackend.predefinedTriggers,
},
// Don't include user envs when parsing triggers.
{
...this.getSystemEnvs(),
...this.getEmulatorEnvs(),
FIREBASE_CONFIG: this.getFirebaseConfig(),
...emulatableBackend.env,
}
);
const triggerParseEvent = await EmulatorLog.waitForLog(
worker.runtime.events,
"SYSTEM",
"triggers-parsed"
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const parsedDefinitions = triggerParseEvent.data
.triggerDefinitions as ParsedTriggerDefinition[];
const triggerDefinitions: EmulatedTriggerDefinition[] =
emulatedFunctionsByRegion(parsedDefinitions);
// When force is true we set up all triggers, otherwise we only set up
// triggers which have a unique function name
const toSetup = triggerDefinitions.filter((definition) => {
if (force) {
return true;
}
// We want to add a trigger if we don't already have an enabled trigger
// with the same entryPoint / trigger.
const anyEnabledMatch = Object.values(this.triggers).some((record) => {
const sameEntryPoint = record.def.entryPoint === definition.entryPoint;
// If they both have event triggers, make sure they match
const sameEventTrigger =
JSON.stringify(record.def.eventTrigger) === JSON.stringify(definition.eventTrigger);
if (sameEntryPoint && !sameEventTrigger) {
this.logger.log(
"DEBUG",
`Definition for trigger ${definition.entryPoint} changed from ${JSON.stringify(
record.def.eventTrigger
)} to ${JSON.stringify(definition.eventTrigger)}`
);
}
return record.enabled && sameEntryPoint && sameEventTrigger;
});
return !anyEnabledMatch;
});
for (const definition of toSetup) {
// Skip function with invalid id.
try {
functionIdsAreValid([definition]);
} catch (e: any) {
this.logger.logLabeled(
"WARN",
`functions[${definition.id}]`,
`Invalid function id: ${e.message}`
);
continue;
}
let added = false;
let url: string | undefined = undefined;
if (definition.httpsTrigger) {
const { host, port } = this.getInfo();
added = true;
url = FunctionsEmulator.getHttpFunctionUrl(
host,
port,
this.args.projectId,
definition.name,
definition.region
);
} else if (definition.eventTrigger) {
const service: string = getFunctionService(definition);
const key = this.getTriggerKey(definition);
const signature = getSignatureType(definition);
switch (service) {
case Constants.SERVICE_FIRESTORE:
added = await this.addFirestoreTrigger(
this.args.projectId,
key,
definition.eventTrigger
);
break;
case Constants.SERVICE_REALTIME_DATABASE:
added = await this.addRealtimeDatabaseTrigger(
this.args.projectId,
key,
definition.eventTrigger
);
break;
case Constants.SERVICE_PUBSUB:
added = await this.addPubsubTrigger(
definition.name,
key,
definition.eventTrigger,
signature,
definition.schedule
);
break;
case Constants.SERVICE_AUTH:
added = this.addAuthTrigger(this.args.projectId, key, definition.eventTrigger);
break;
case Constants.SERVICE_STORAGE:
added = this.addStorageTrigger(this.args.projectId, key, definition.eventTrigger);
break;
default:
this.logger.log("DEBUG", `Unsupported trigger: ${JSON.stringify(definition)}`);
break;
}
} else {
this.logger.log(
"WARN",
`Trigger trigger "${definition.name}" has has neither "httpsTrigger" or "eventTrigger" member`
);
}
const ignored = !added;
this.addTriggerRecord(definition, { backend: emulatableBackend, ignored, url });
const type = definition.httpsTrigger
? "http"
: Constants.getServiceName(getFunctionService(definition));
if (ignored) {
const msg = `function ignored because the ${type} emulator does not exist or is not running.`;
this.logger.logLabeled("BULLET", `functions[${definition.id}]`, msg);
} else {
const msg = url
? `${clc.bold(type)} function initialized (${url}).`
: `${clc.bold(type)} function initialized.`;
this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
}
}
}