diff --git a/.gitignore b/.gitignore index 2395f73..6bf948c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ lib .vscode *.tgz .tmp -*.log \ No newline at end of file +*.log +.idea diff --git a/package-lock.json b/package-lock.json index ba46160..cd3a483 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@types/mocha": "^5.2.7", "chai": "^4.2.0", "firebase-admin": "^12.0.0", - "firebase-functions": "^4.6.0", + "firebase-functions": "^4.9.0", "firebase-tools": "^8.9.2", "mocha": "^6.2.2", "prettier": "^1.19.1", @@ -31,7 +31,7 @@ }, "peerDependencies": { "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "firebase-functions": ">=4.3.0", + "firebase-functions": ">=4.9.0", "jest": ">=28.0.0" } }, @@ -5329,16 +5329,15 @@ "dev": true }, "node_modules/firebase-functions": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-4.6.0.tgz", - "integrity": "sha512-mY3wuU/Qe+vjVyoCIv0TGXcqr5iQhsMlccLBSAHJ+cWgbszo915mcFP8E9adtXoitqf/4CVzzTwYcfPdCQo2RQ==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-4.9.0.tgz", + "integrity": "sha512-IqxOEsVAWGcRv9KRGzWQR5mOFuNsil3vsfkRPPiaV1U/ATC27/jbahh4z8I4rW8Xqa6cQE5xqnw0ueyMH7i7Ag==", "dev": true, "dependencies": { "@types/cors": "^2.8.5", "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "node-fetch": "^2.6.7", "protobufjs": "^7.2.2" }, "bin": { @@ -17501,16 +17500,15 @@ } }, "firebase-functions": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-4.6.0.tgz", - "integrity": "sha512-mY3wuU/Qe+vjVyoCIv0TGXcqr5iQhsMlccLBSAHJ+cWgbszo915mcFP8E9adtXoitqf/4CVzzTwYcfPdCQo2RQ==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-4.9.0.tgz", + "integrity": "sha512-IqxOEsVAWGcRv9KRGzWQR5mOFuNsil3vsfkRPPiaV1U/ATC27/jbahh4z8I4rW8Xqa6cQE5xqnw0ueyMH7i7Ag==", "dev": true, "requires": { "@types/cors": "^2.8.5", "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "node-fetch": "^2.6.7", "protobufjs": "^7.2.2" }, "dependencies": { diff --git a/package.json b/package.json index 171b007..92ea15f 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@types/mocha": "^5.2.7", "chai": "^4.2.0", "firebase-admin": "^12.0.0", - "firebase-functions": "^4.6.0", + "firebase-functions": "^4.9.0", "firebase-tools": "^8.9.2", "mocha": "^6.2.2", "prettier": "^1.19.1", @@ -57,7 +57,7 @@ }, "peerDependencies": { "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "firebase-functions": ">=4.3.0", + "firebase-functions": ">=4.9.0", "jest": ">=28.0.0" }, "engines": { diff --git a/src/cloudevent/mocks/firestore/firestore-on-document-created-with-auth-context.ts b/src/cloudevent/mocks/firestore/firestore-on-document-created-with-auth-context.ts new file mode 100644 index 0000000..bf4359c --- /dev/null +++ b/src/cloudevent/mocks/firestore/firestore-on-document-created-with-auth-context.ts @@ -0,0 +1,17 @@ +import { MockCloudEventAbstractFactory } from '../../types'; +import { CloudEvent, CloudFunction, firestore } from 'firebase-functions/v2'; +import { getEventType } from '../helpers'; +import { QueryDocumentSnapshot } from 'firebase-admin/firestore'; +import { getDocumentSnapshotCloudEventWithAuthContext } from './helpers'; + +export const firestoreOnDocumentCreatedWithAuthContext: MockCloudEventAbstractFactory> = { + generateMock: getDocumentSnapshotCloudEventWithAuthContext, + match(cloudFunction: CloudFunction>): boolean { + return ( + getEventType(cloudFunction) === + 'google.cloud.firestore.document.v1.created.withAuthContext' + ); + }, +}; diff --git a/src/cloudevent/mocks/firestore/firestore-on-document-deleted-with-auth-context.ts b/src/cloudevent/mocks/firestore/firestore-on-document-deleted-with-auth-context.ts new file mode 100644 index 0000000..942ebc7 --- /dev/null +++ b/src/cloudevent/mocks/firestore/firestore-on-document-deleted-with-auth-context.ts @@ -0,0 +1,17 @@ +import { MockCloudEventAbstractFactory } from '../../types'; +import { CloudEvent, CloudFunction, firestore } from 'firebase-functions/v2'; +import { getEventType } from '../helpers'; +import { QueryDocumentSnapshot } from 'firebase-admin/firestore'; +import { getDocumentSnapshotCloudEventWithAuthContext } from './helpers'; + +export const firestoreOnDocumentDeletedWithAuthContext: MockCloudEventAbstractFactory> = { + generateMock: getDocumentSnapshotCloudEventWithAuthContext, + match(cloudFunction: CloudFunction>): boolean { + return ( + getEventType(cloudFunction) === + 'google.cloud.firestore.document.v1.deleted.withAuthContext' + ); + }, +}; diff --git a/src/cloudevent/mocks/firestore/firestore-on-document-updated-with-auth-context.ts b/src/cloudevent/mocks/firestore/firestore-on-document-updated-with-auth-context.ts new file mode 100644 index 0000000..a023ce8 --- /dev/null +++ b/src/cloudevent/mocks/firestore/firestore-on-document-updated-with-auth-context.ts @@ -0,0 +1,22 @@ +import { MockCloudEventAbstractFactory } from '../../types'; +import { + Change, + CloudEvent, + CloudFunction, + firestore, +} from 'firebase-functions/v2'; +import { getEventType } from '../helpers'; +import { QueryDocumentSnapshot } from 'firebase-admin/firestore'; +import { getDocumentSnapshotChangeCloudEventWithAuthContext } from './helpers'; + +export const firestoreOnDocumentUpdatedWithAuthContext: MockCloudEventAbstractFactory +>> = { + generateMock: getDocumentSnapshotChangeCloudEventWithAuthContext, + match(cloudFunction: CloudFunction>): boolean { + return ( + getEventType(cloudFunction) === + 'google.cloud.firestore.document.v1.updated.withAuthContext' + ); + }, +}; diff --git a/src/cloudevent/mocks/firestore/firestore-on-document-written-with-auth-context.ts b/src/cloudevent/mocks/firestore/firestore-on-document-written-with-auth-context.ts new file mode 100644 index 0000000..a9171a9 --- /dev/null +++ b/src/cloudevent/mocks/firestore/firestore-on-document-written-with-auth-context.ts @@ -0,0 +1,22 @@ +import { MockCloudEventAbstractFactory } from '../../types'; +import { + Change, + CloudEvent, + CloudFunction, + firestore, +} from 'firebase-functions/v2'; +import { getEventType } from '../helpers'; +import { DocumentSnapshot } from 'firebase-admin/firestore'; +import { getDocumentSnapshotChangeCloudEventWithAuthContext } from './helpers'; + +export const firestoreOnDocumentWrittenWithAuthContext: MockCloudEventAbstractFactory +>> = { + generateMock: getDocumentSnapshotChangeCloudEventWithAuthContext, + match(cloudFunction: CloudFunction>): boolean { + return ( + getEventType(cloudFunction) === + 'google.cloud.firestore.document.v1.written.withAuthContext' + ); + }, +}; diff --git a/src/cloudevent/mocks/firestore/helpers.ts b/src/cloudevent/mocks/firestore/helpers.ts index cc301bf..77b144c 100644 --- a/src/cloudevent/mocks/firestore/helpers.ts +++ b/src/cloudevent/mocks/firestore/helpers.ts @@ -47,6 +47,31 @@ export function getDocumentSnapshotCloudEvent( }; } +export function getDocumentSnapshotCloudEventWithAuthContext( + cloudFunction: CloudFunction>, + cloudEventPartial?: DeepPartial< + firestore.FirestoreAuthEvent + > +) { + const eventWithoutAuthContext = getDocumentSnapshotCloudEvent( + cloudFunction, + cloudEventPartial + ); + const authContext: { authId?: string; authType: firestore.AuthType } = { + authType: 'unknown', + }; + if (cloudEventPartial?.authId) { + authContext.authId = cloudEventPartial.authId; + } + if (cloudEventPartial?.authType) { + authContext.authType = cloudEventPartial.authType; + } + return { + ...eventWithoutAuthContext, + ...authContext, + }; +} + /** Creates a mock CloudEvent that contains a Change as its data. */ export function getDocumentSnapshotChangeCloudEvent( cloudFunction: CloudFunction< @@ -82,6 +107,34 @@ export function getDocumentSnapshotChangeCloudEvent( }; } +export function getDocumentSnapshotChangeCloudEventWithAuthContext( + cloudFunction: CloudFunction< + firestore.FirestoreAuthEvent> + >, + cloudEventPartial?: DeepPartial< + firestore.FirestoreAuthEvent | ChangeLike> + > +) { + const eventWithoutAuthContext = getDocumentSnapshotChangeCloudEvent( + cloudFunction, + cloudEventPartial + ); + const authContext: { authId?: string; authType: firestore.AuthType } = { + authType: 'unknown', + }; + if (cloudEventPartial?.authId) { + authContext.authId = cloudEventPartial.authId; + } + if (cloudEventPartial?.authType) { + authContext.authType = cloudEventPartial.authType; + } + + return { + ...eventWithoutAuthContext, + ...authContext, + }; +} + /** Finds or provides reasonable defaults for mock FirestoreEvent data. */ function getFirestoreEventFields( cloudFunction: CloudFunction< diff --git a/src/cloudevent/mocks/partials.ts b/src/cloudevent/mocks/partials.ts index 1658ebf..ffc3082 100644 --- a/src/cloudevent/mocks/partials.ts +++ b/src/cloudevent/mocks/partials.ts @@ -27,6 +27,10 @@ import { import { storageV1 } from './storage'; import { remoteConfigOnConfigUpdated } from './remoteconfig/remote-config-on-config-updated'; import { testLabOnTestMatrixCompleted } from './testlab/test-lab-on-test-matrix-completed'; +import { firestoreOnDocumentCreatedWithAuthContext } from './firestore/firestore-on-document-created-with-auth-context'; +import { firestoreOnDocumentDeletedWithAuthContext } from './firestore/firestore-on-document-deleted-with-auth-context'; +import { firestoreOnDocumentUpdatedWithAuthContext } from './firestore/firestore-on-document-updated-with-auth-context'; +import { firestoreOnDocumentWrittenWithAuthContext } from './firestore/firestore-on-document-written-with-auth-context'; /** * Note: Ordering matters. Some MockEventPartials will match more generally @@ -59,6 +63,10 @@ export const LIST_OF_MOCK_CLOUD_EVENT_PARTIALS: Array