Skip to content

Update Rtdb test sdk to allow json for CloudEvent #159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 8, 2022

Conversation

TheIronDev
Copy link
Contributor

@TheIronDev TheIronDev commented Aug 3, 2022

Description

This commit updates the test sdk api for the Database CloudEventPartial
to support end-users submitting JSON that gets transformed into DataSnapshot
instead of forcing users to import and use Change and DataSnapshot to
mock out the CloudEvent resopnses.

Code sample

// Previous example
const cloudFn = database.onValueDeleted(referenceOptions, handler);
const cloudFnWrap = wrapV2(cloudFn);
const dataVal = { snapshot: 'override' };

// Calling makeDataSnapshot is the step we are trying to simplify
const data = makeDataSnapshot(dataVal, referenceOptions.ref);

cloudFnWrap({ data: data });
// New example
const cloudFn = database.onValueDeleted(referenceOptions, handler);
const cloudFnWrap = wrapV2(cloudFn);
const dataVal = { snapshot: 'override' };

cloudFnWrap({ data: dataVal });

This commit updates the test sdk api for the Database CloudEventPartial
to support end-users submitting JSON that gets transformed into `DataSnapshot`
instead of forcing users to import and use `Change` and `DataSnapshot` to
mock out the CloudEvent resopnses.
@@ -29,7 +29,7 @@ import { DeepPartial } from './cloudevent/types';
* It will subsequently invoke the cloud function it wraps with the provided {@link CloudEvent}
*/
export type WrappedV2Function<T extends CloudEvent<unknown>> = (
cloudEventPartial?: DeepPartial<T>
cloudEventPartial?: DeepPartial<T | object>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I think typing of this should be DeepPartial<T> | Object instead.

  2. Since Object is roughly similar to any, I'm pretty sad to see this function loosing quite a bit of type info. Is there way to make the typing a little more stricter? E.g. Object can only have keys that are part of CloudEvent type (I'm not sure if my suggestion makes complete sense).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. But Object throws a lint error.
  2. Thats a rough one. If we want to support raw JSON being passed in for the onValueCreated and onValueDeleted, I'm not sure of any good alternatives.. :\

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. TIL.

  2. Understood. I wish there were a way to make this type-safe somehow (e.g. after/before example we discussed offline) but I don't see a way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a big difference between Object (JS) and object (TS). For one, I don't know if DeepPartial<T> | object would be the same (IDK if you can even use "object" outside of "extends object"). If you really want to say "Anything that looks like a map" you can use Record<string, unknown>. If you want to come up with some really clever metaprogramming, I can try too.

TheIronDev and others added 3 commits August 4, 2022 16:09

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
Co-authored-by: Daniel Lee <danielylee@google.com>
@TheIronDev TheIronDev requested a review from taeold August 5, 2022 15:40
Copy link
Contributor

@taeold taeold left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm but just one question so I understand this PR better:

In the new example, doesn't have to call makeDataSnapshot(data, ref). Does user NEED to specify ref property on their object? Or is it automatically inferred from... something else (maybe from cloud function definition? But that would almost always be wildcard ref right?)

@TheIronDev
Copy link
Contributor Author

lgtm but just one question so I understand this PR better:

In the new example, doesn't have to call makeDataSnapshot(data, ref). Does user NEED to specify ref property on their object? Or is it automatically inferred from... something else (maybe from cloud function definition? But that would almost always be wildcard ref right?)

Ref gets automatically inferred and extracted here:

https://github.com/firebase/firebase-functions-test/blob/master/src/cloudevent/mocks/database/helpers.ts

@TheIronDev TheIronDev requested a review from taeold August 8, 2022 18:30
@TheIronDev TheIronDev merged commit 6ee1a34 into master Aug 8, 2022
: generatedCloudEvent;
}

function shouldDeleteUserSuppliedData<EventType extends CloudEvent<unknown>>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is such a cool problem you had to solve. Good find!

@@ -6,10 +6,46 @@ import {
} from '../../../providers/database';
import { getBaseCloudEvent } from '../helpers';
import { Change } from 'firebase-functions';
import { makeDataSnapshot } from '../../../providers/database';

type ChangeLike = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This reads a little weird. Why did you create a type alias to an anonymous interface type rather than just creating a named interface (i.e. interface ChangeLike {)

if (data instanceof Change) {
return data;
}
if (data instanceof Object && data?.before && data?.after) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we want && (data?.before || data?.after) and make an undefined before/after be a DataSnapshot to a nonexistent value. WDYT?

Functionally it's the difference between one LOC in:

wrappedFn({
  ref: "users/inlined"
  data: {
    // is this necessary?
    before: null,
    after: { name: "Thomas" },
  }
});

I could see arguments in either direction. Either we're helping them write simpler code or we could require them to be explicit to avoid accidental misuse (e.g. typo in "before" or "after"). Thoughts?


export function getDatabaseSnapshotCloudEvent(
cloudFunction: CloudFunction<database.DatabaseEvent<database.DataSnapshot>>,
cloudEventPartial?: DeepPartial<database.DatabaseEvent<database.DataSnapshot>>
cloudEventPartial?: DeepPartial<
database.DatabaseEvent<database.DataSnapshot | object>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I personally would hoist | object to the top of the type definition because a DeepPartial<object> is meaningless. So DeepPartial<database.DatabaseEvent<database.DataSnapshot>> | object

@@ -29,7 +29,7 @@ import { DeepPartial } from './cloudevent/types';
* It will subsequently invoke the cloud function it wraps with the provided {@link CloudEvent}
*/
export type WrappedV2Function<T extends CloudEvent<unknown>> = (
cloudEventPartial?: DeepPartial<T>
cloudEventPartial?: DeepPartial<T | object>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a big difference between Object (JS) and object (TS). For one, I don't know if DeepPartial<T> | object would be the same (IDK if you can even use "object" outside of "extends object"). If you really want to say "Anything that looks like a map" you can use Record<string, unknown>. If you want to come up with some really clever metaprogramming, I can try too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants