Skip to content

Commit

Permalink
fix snapshot creation for null docs, add script to protos
Browse files Browse the repository at this point in the history
  • Loading branch information
colerogers committed Mar 21, 2023
1 parent 70faa4a commit 3692a01
Show file tree
Hide file tree
Showing 10 changed files with 4,712 additions and 3,022 deletions.
7,506 changes: 4,505 additions & 3,001 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -237,6 +237,7 @@
"node-fetch": "^2.6.7",
"portfinder": "^1.0.28",
"prettier": "^2.7.1",
"protobufjs-cli": "^1.1.1",
"semver": "^7.3.5",
"sinon": "^7.3.2",
"ts-node": "^10.4.0",
Expand Down
38 changes: 38 additions & 0 deletions protos/README.md
@@ -0,0 +1,38 @@
# Generate compiled ProtoBuf

## Step 1

### Grab the protos

Firestore DocumentEventData - https://github.com/googleapis/google-cloudevents/blob/main/proto/google/events/cloud/firestore/v1/data.proto

Any - https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/any.proto

Struct - https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto

Timestamp - https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/timestamp.proto

Latlng - https://github.com/googleapis/googleapis/blob/master/google/type/latlng.proto

## Step 2

### Directory structure

data.proto
any.proto
google/
struct.proto
timestamp.proto
type/
latlng.proto

## Step 3

### Run the protobufjs cli to generate

```
npx pbjs -t static-module -w commonjs -o compiled.js data.proto any.proto google/struct.proto google/timestamp.proto google/type/latlng.proto
npx pbts -o compiled.d.ts compiled.js
```

https://github.com/protobufjs/protobuf.js/tree/master/cli#pbts-for-typescript
94 changes: 94 additions & 0 deletions protos/update.sh
@@ -0,0 +1,94 @@
#!/bin/bash

# The MIT License (MIT)
#
# Copyright (c) 2023 Firebase
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# vars
PROTOS_DIR="$(pwd)"
WORK_DIR=`mktemp -d`

# deletes the temp directory on exit
function cleanup {
rm -rf "$WORK_DIR"
echo "Deleted temp working directory $WORK_DIR"
# rm -rf "${PROTOS_DIR}/data.proto" "${PROTOS_DIR}/any.proto" "${PROTOS_DIR}/google"
# echo "Deleted copied protos"
}

# register the cleanup function to be called on the EXIT signal
trap cleanup EXIT

# Capture location of pbjs / pbts before we pushd.
PBJS="$(npm bin)/pbjs"
PBTS="$(npm bin)/pbts"

# enter working directory
pushd "$WORK_DIR"

git clone --depth 1 https://github.com/googleapis/google-cloudevents.git
git clone --depth 1 https://github.com/googleapis/googleapis.git
git clone --depth 1 https://github.com/google/protobuf.git

# make dirs
mkdir -p "${PROTOS_DIR}/google/type"

# copy protos
cp google-cloudevents/proto/google/events/cloud/firestore/v1/data.proto \
"${PROTOS_DIR}/"

cp protobuf/src/google/protobuf/any.proto \
"${PROTOS_DIR}/"

cp protobuf/src/google/protobuf/struct.proto \
"${PROTOS_DIR}/google/"

cp protobuf/src/google/protobuf/timestamp.proto \
"${PROTOS_DIR}/google/"

cp googleapis/google/type/latlng.proto \
"${PROTOS_DIR}/google/type/"

popd

# PBJS_ARGS=( --proto_path=. \
# --js_out=import_style=commonjs,binary:library \
# --target=static-module \
# --no-create \
# --no-encode \
# --no-decode \
# --no-verify \
# --no-delimited \
# --force-enum-string)

"${PBJS}" -t static-module -w commonjs -o compiled.js \
data.proto any.proto google/*.proto google/type/*.proto

"${PBTS}" -o compiledFirestore.d.ts compiledFirestore.js

# "${PBJS}" "${PBJS_ARGS[@]}" -o compiledFirestore.js \
# -r firestore_v1 \
# "${PROTOS_DIR}/*.proto" \
# "${PROTOS_DIR}/google/type/*.proto"

# perl -pi -e 's/number\|Long/number\|string/g' compiledFirestore.js

# "${PBTS}" -o compiledFirestore.d.ts compiledFirestore.js
30 changes: 29 additions & 1 deletion spec/v2/providers/firestore.spec.ts
Expand Up @@ -80,7 +80,7 @@ function makeEncodedProtobuf(data: any) {
return Any.encode(anyData).finish();
}

function makeEvent(data: any): firestore.RawFirestoreEvent {
function makeEvent(data?: any): firestore.RawFirestoreEvent {
return {
...eventBase,
data,
Expand Down Expand Up @@ -377,6 +377,15 @@ describe("firestore", () => {
).to.throw("Error: Cannot parse event payload.");
});

it("should create snapshot of a protobuf encoded event if datacontexttype is missing", () => {
const rawEvent: firestore.RawFirestoreEvent = makeEvent(makeEncodedProtobuf(createdProto));
delete rawEvent.datacontenttype;

const snapshot = firestore.createSnapshot(rawEvent);

expect(snapshot.data()).to.deep.eq({ hello: "create world" });
});

it("should create snapshot of a protobuf encoded created event", () => {
const rawEvent: firestore.RawFirestoreEvent = makeEvent(makeEncodedProtobuf(createdProto));

Expand Down Expand Up @@ -511,6 +520,16 @@ describe("firestore", () => {
});

describe("makeFirestoreEvent", () => {
it("should make event from an event without data", () => {
const event = firestore.makeFirestoreEvent(
firestore.createdEventType,
makeEvent(),
firestore.makeParams("foo/fGRodw71mHutZ4wGDuT8", new PathPattern("foo/{bar}"))
);

expect(event.data).to.eq(undefined);
});

it("should make event from a created event", () => {
const event = firestore.makeFirestoreEvent(
firestore.createdEventType,
Expand All @@ -533,6 +552,15 @@ describe("firestore", () => {
});

describe("makeChangedFirestoreEvent", () => {
it("should make event from an event without data", () => {
const event = firestore.makeChangedFirestoreEvent(
makeEvent(),
firestore.makeParams("foo/fGRodw71mHutZ4wGDuT8", new PathPattern("foo/{bar}"))
);

expect(event.data).to.eq(undefined);
});

it("should make event from an updated event", () => {
const event = firestore.makeChangedFirestoreEvent(
makeEvent(makeEncodedProtobuf(updatedProto)),
Expand Down
14 changes: 10 additions & 4 deletions src/common/providers/firestore.ts
Expand Up @@ -32,6 +32,7 @@ const DocumentEventData = google.events.cloud.firestore.v1.DocumentEventData;

let firestoreInstance: any;

/** @hidden */
function _getValueProto(data: any, resource: string, valueFieldName: string) {
const value = data?.[valueFieldName];
if (
Expand All @@ -51,22 +52,25 @@ function _getValueProto(data: any, resource: string, valueFieldName: string) {
return proto;
}

export function createSnapshotFromProtobuf(data: Uint8Array) {
/** @internal */
export function createSnapshotFromProtobuf(data: Uint8Array, path: string) {
if (!firestoreInstance) {
firestoreInstance = firestore.getFirestore(getApp());
}
try {
const dataBuffer = Buffer.from(data);
const anyDecoded = Any.decode(dataBuffer);
const firestoreDecoded = DocumentEventData.decode(anyDecoded.value);
return firestoreInstance.snapshot_(firestoreDecoded.value, null, "protobufJS");

return firestoreInstance.snapshot_(firestoreDecoded.value || path, null, "protobufJS");
} catch (err: unknown) {
logger.error("Failed to decode protobuf and create a snapshot.");
throw err;
}
}

export function createBeforeSnapshotFromProtobuf(data: Uint8Array) {
/** @internal */
export function createBeforeSnapshotFromProtobuf(data: Uint8Array, path: string) {
if (!firestoreInstance) {
firestoreInstance = firestore.getFirestore(getApp());
}
Expand All @@ -75,13 +79,14 @@ export function createBeforeSnapshotFromProtobuf(data: Uint8Array) {
const anyDecoded = Any.decode(dataBuffer);
const firestoreDecoded = DocumentEventData.decode(anyDecoded.value);

return firestoreInstance.snapshot_(firestoreDecoded.oldValue, null, "protobufJS");
return firestoreInstance.snapshot_(firestoreDecoded.oldValue || path, null, "protobufJS");
} catch (err: unknown) {
logger.error("Failed to decode protobuf and create a before snapshot.");
throw err;
}
}

/** @internal */
export function createSnapshotFromJson(
data: any,
source: string,
Expand All @@ -103,6 +108,7 @@ export function createSnapshotFromJson(
return firestoreInstance.snapshot_(valueProto, readTime, "json");
}

/** @internal */
export function createBeforeSnapshotFromJson(
data: any,
source: string,
Expand Down
4 changes: 2 additions & 2 deletions src/v1/function-builder.ts
Expand Up @@ -249,7 +249,7 @@ function assertRegionsAreValid(regions: string[]): boolean {
* functions.region('us-east1', 'us-central1')
*/
export function region(
...regions: Array<typeof SUPPORTED_REGIONS[number] | string>
...regions: Array<(typeof SUPPORTED_REGIONS)[number] | string>
): FunctionBuilder {
if (assertRegionsAreValid(regions)) {
return new FunctionBuilder({ regions });
Expand Down Expand Up @@ -291,7 +291,7 @@ export class FunctionBuilder {
* @example
* functions.region('us-east1', 'us-central1')
*/
region(...regions: Array<typeof SUPPORTED_REGIONS[number] | string>): FunctionBuilder {
region(...regions: Array<(typeof SUPPORTED_REGIONS)[number] | string>): FunctionBuilder {
if (assertRegionsAreValid(regions)) {
this.options.regions = regions;
return this;
Expand Down
8 changes: 4 additions & 4 deletions src/v1/function-configuration.ts
Expand Up @@ -182,7 +182,7 @@ export interface RuntimeOptions {
/**
* Amount of memory to allocate to the function.
*/
memory?: typeof VALID_MEMORY_OPTIONS[number] | Expression<number> | ResetValue;
memory?: (typeof VALID_MEMORY_OPTIONS)[number] | Expression<number> | ResetValue;
/**
* Timeout for the function in seconds, possible values are 0 to 540.
*/
Expand Down Expand Up @@ -210,7 +210,7 @@ export interface RuntimeOptions {
/**
* Egress settings for VPC connector.
*/
vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number] | ResetValue;
vpcConnectorEgressSettings?: (typeof VPC_EGRESS_SETTINGS_OPTIONS)[number] | ResetValue;

/**
* Specific service account for the function to run as.
Expand All @@ -220,7 +220,7 @@ export interface RuntimeOptions {
/**
* Ingress settings which control where this function can be called from.
*/
ingressSettings?: typeof INGRESS_SETTINGS_OPTIONS[number] | ResetValue;
ingressSettings?: (typeof INGRESS_SETTINGS_OPTIONS)[number] | ResetValue;

/**
* User labels to set on the function.
Expand Down Expand Up @@ -270,7 +270,7 @@ export interface DeploymentOptions extends RuntimeOptions {
/**
* Regions where function should be deployed.
*/
regions?: Array<typeof SUPPORTED_REGIONS[number] | string>;
regions?: Array<(typeof SUPPORTED_REGIONS)[number] | string>;
/**
* Schedule for the scheduled function.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/v2/core.ts
Expand Up @@ -89,7 +89,7 @@ export interface CloudEvent<T> {
time: string;

/** Information about this specific event. */
data: T;
data?: T;
}

/**
Expand Down

0 comments on commit 3692a01

Please sign in to comment.