diff --git a/CHANGELOG.md b/CHANGELOG.md index 349b92a3ed8..75601213bf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Refactor the way timeouts are enforced by the Functions Emulator (#5464) +- Fix bug where cloudevent emitted by various emulators didn't conform to spec (#5466) diff --git a/src/deploy/functions/runtimes/python/index.ts b/src/deploy/functions/runtimes/python/index.ts index 8825e383f5a..37a6c546fcd 100644 --- a/src/deploy/functions/runtimes/python/index.ts +++ b/src/deploy/functions/runtimes/python/index.ts @@ -129,6 +129,14 @@ export class Delegate implements runtimes.RuntimeDelegate { )} in ${this.sourceDir}` ); const childProcess = runWithVirtualEnv(args, this.sourceDir, envWithAdminPort); + childProcess.stdout?.on("data", (chunk: Buffer) => { + const chunkString = chunk.toString(); + logger.debug(`stdout: ${chunkString}`); + }); + childProcess.stderr?.on("data", (chunk: Buffer) => { + const chunkString = chunk.toString(); + logger.debug(`stderr: ${chunkString}`); + }); return Promise.resolve(async () => { await fetch(`http://127.0.0.1:${port}/__/quitquitquit`); const quitTimeout = setTimeout(() => { diff --git a/src/emulator/pubsubEmulator.ts b/src/emulator/pubsubEmulator.ts index a208407a930..1f8be4716b1 100644 --- a/src/emulator/pubsubEmulator.ts +++ b/src/emulator/pubsubEmulator.ts @@ -189,20 +189,29 @@ export class PubsubEmulator implements EmulatorInstance { topic: string, message: Message ): CloudEvent { + // Pubsub events from Pubsub Emulator include a date with nanoseconds. + // Prod Pubsub doesn't publish timestamp at that level of precision. Timestamp with nanosecond precision also + // are difficult to parse in languages other than Node.js (e.g. python). + const truncatedPublishTime = new Date(message.publishTime.getMilliseconds()).toISOString(); const data: MessagePublishedData = { message: { messageId: message.id, - publishTime: message.publishTime, + publishTime: truncatedPublishTime, attributes: message.attributes, orderingKey: message.orderingKey, data: message.data.toString("base64"), - }, + + // NOTE: We include camel_cased attributes since they also available and depended on by other runtimes + // like python. + message_id: message.id, + publish_time: truncatedPublishTime, + } as MessagePublishedData["message"], subscription: this.subscriptionForTopic.get(topic)!.name, }; return { - specversion: "1", + specversion: "1.0", id: uuid.v4(), - time: message.publishTime.toISOString(), + time: truncatedPublishTime, type: "google.cloud.pubsub.topic.v1.messagePublished", source: `//pubsub.googleapis.com/projects/${this.args.projectId}/topics/${topic}`, data, diff --git a/src/emulator/storage/cloudFunctions.ts b/src/emulator/storage/cloudFunctions.ts index 7308c67018e..bdd8aca82d3 100644 --- a/src/emulator/storage/cloudFunctions.ts +++ b/src/emulator/storage/cloudFunctions.ts @@ -106,7 +106,7 @@ export class StorageCloudFunctions { time = typeof data.updated === "string" ? data.updated : data.updated.toISOString(); } return { - specversion: "1", + specversion: "1.0", id: uuid.v4(), type: `google.cloud.storage.object.v1.${ceAction}`, source: `//storage.googleapis.com/projects/_/buckets/${objectMetadataPayload.bucket}/objects/${objectMetadataPayload.name}`, @@ -255,9 +255,9 @@ export interface ObjectMetadataPayload { * Customer-supplied encryption key. * * This object contains the following properties: - * * `encryptionAlgorithm` (`string|undefined`): The encryption algorithm that + * `encryptionAlgorithm` (`string|undefined`): The encryption algorithm that * was used. Always contains the value `AES256`. - * * `keySha256` (`string|undefined`): An RFC 4648 base64-encoded string of the + * `keySha256` (`string|undefined`): An RFC 4648 base64-encoded string of the * SHA256 hash of your encryption key. You can use this SHA256 hash to * uniquely identify the AES-256 encryption key required to decrypt the * object, which you must store securely.