Page MenuHomePhabricator

Define message spec interface and implement it for each message type that produces notifications
ClosedPublic

Authored by marcin on May 6 2022, 2:51 AM.
Tags
None
Referenced Files
Unknown Object (File)
Fri, Dec 20, 2:02 AM
Unknown Object (File)
Fri, Dec 20, 2:01 AM
Unknown Object (File)
Fri, Dec 20, 12:58 AM
Unknown Object (File)
Tue, Dec 17, 6:22 PM
Unknown Object (File)
Tue, Dec 17, 6:22 PM
Unknown Object (File)
Tue, Dec 17, 6:22 PM
Unknown Object (File)
Tue, Dec 17, 6:22 PM
Unknown Object (File)
Tue, Dec 17, 6:22 PM

Details

Summary

Implement message spec interface for each type of message that mirrors corresponding JavaScript implementation.

Test Plan

No testing plan. Guture diffs will provide a code that was used on Android and iOS to parse raw message string and store it to database.

Diff Detail

Repository
rCOMM Comm
Lint
No Lint Coverage
Unit
No Test Coverage

Event Timeline

Harbormaster returned this revision to the author for changes because remote builds failed.May 6 2022, 2:51 AM
Harbormaster failed remote builds in B8843: Diff 12338!
Harbormaster returned this revision to the author for changes because remote builds failed.May 6 2022, 2:59 AM
Harbormaster failed remote builds in B8845: Diff 12340!
marcin requested review of this revision.May 6 2022, 9:48 AM
ashoat added 1 blocking reviewer(s): tomek.

This looks great to me, but @palys-swm should definitely take a look since I think he has some context on folly::dynamic

native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs.h
2
  1. It's transitively included, but we should make the dependency on MessageSpec.h explicit here
  2. Can we add another newline after #pragma once, like in the other files?

One thing we should think about here: how do we make sure that somebody introducing a new message type remembers to create a new C++ MessageSpec?

Unrelatedly, we could consider writing these message specs in Rust, and trying to move more of the "business logic" of native to Rust.

Add newline after pragma, include MessageSpec.h in MessageSpecs.h

  1. Currently we do not implement MessageSpec for all type of messages. In particular you advised me during 1:1 not to implement it for messages that do no generate notifications (ADD_MEMBERS etc.). This way if message type is not present in MESSAGE_SPEC map we return nullptr as unique pointer to its content. Nevertheless we still have enumeration type that covers all message types. We can perform some simple check in translateRawMessageInfoToClientDBMessageInfo in next diff to throw runtime error if message of type not covered by this enumeration is received. If someone is led to MessageSpecs.h they will probably realise thet need to implement MessageSpec for that message if they want its content to be stored in the database. How is it currently done on the JS part? Perhaps we could apply the same solution here.
  1. I am open to writing those message specs in Rust, but I am not sure what exactly do you mean. We can:
    1. implement entire functionality of parsing notification in Rust.
    2. implement it twice (in C++ with higher priority and in Rust in the meantime)
    3. implement entire functionality in C++ and some part of it in Rust (for future work)
    4. Implement some part in C++ and some part in Rust and use binary bridges

Which one did you actually had in mind?

Please, add the missing newlines.

native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs.h
22 ↗(On Diff #12402)

I think it's better to use enum class, can we use it here?

44 ↗(On Diff #12402)

Why this name is upper-case-snake? It's not static. const for collections doesn't prevent modifying them (it prevents re-assigning).

92 ↗(On Diff #12402)

I'm not sure about this. Is this going to create all these objects despite whether they are used or not? Maybe it would be better to create them lazily? Have you considered some kind of a factory here?

This revision now requires changes to proceed.May 9 2022, 6:19 AM
native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs.h
44 ↗(On Diff #12402)

Actually const keyword prevents STL collections modification. But I agree with the rest of the comment that upper-case snake might not have been the best choice here. How about const std::map<MessageType, std::unique_ptr<MessageSpec>> messageSpecsHolder?

92 ↗(On Diff #12402)

Yes, all this objects will be created when the app starts. There are solutions to create them lazily. folly::Lazy or folly::Singleton (it is also lazy and provides additional Singleton functionality) so I can implement their lazy instantiation. Nevertheless I am just curious about the potential drawbacks of eager instantiation. Those objects are stateless and there are just few of them so the memory overhead should not be an issue here. Still I agree I may be wrong and I am willing to work on lazy instantiation.

native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs.h
44 ↗(On Diff #12402)

Looks better. Sorry, what I said was wrong about const, I confused this with the js where const prevents reassigning but you can still modify the collection. Nevertheless, we should use camel case.

92 ↗(On Diff #12402)

For me, there are a lot of objects, not just a few. I'd lean towards the lazy init. It is a good rule of thumb let's say to avoid allocating memory as much as possible. If these objects ever grow, it will be harder to refactor this code to use the lazy init.

Pinging this question – I think it's important. Ideally the program crashes if there is a message type in JS that isn't in C++, and vice versa.

One thing we should think about here: how do we make sure that somebody introducing a new message type remembers to create a new C++ MessageSpec?

Unrelatedly, we could consider writing these message specs in Rust, and trying to move more of the "business logic" of native to Rust.

native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs.h
92 ↗(On Diff #12402)

I think eager initialization is fine for the message specs, personally. @palys-swm – what do you think?

@marcinwasowicz, sorry I missed your response earlier!

In D3940#111019, @marcinwasowicz wrote:
  1. Currently we do not implement MessageSpec for all type of messages. In particular you advised me during 1:1 not to implement it for messages that do no generate notifications (ADD_MEMBERS etc.). This way if message type is not present in MESSAGE_SPEC map we return nullptr as unique pointer to its content. Nevertheless we still have enumeration type that covers all message types. We can perform some simple check in translateRawMessageInfoToClientDBMessageInfo in next diff to throw runtime error if message of type not covered by this enumeration is received. If someone is led to MessageSpecs.h they will probably realise thet need to implement MessageSpec for that message if they want its content to be stored in the database. How is it currently done on the JS part? Perhaps we could apply the same solution here.

I think we'll want some protection to make sure somebody doesn't create a MessageSpec in JS with generatesNotifs: true without also creating a MessageSpec in C++. One thing we could do is do a check at app boot for every JS MessageSpec with generatesNotifs: true to make sure C++ has a corresponding MessageSpec.

  1. I am open to writing those message specs in Rust, but I am not sure what exactly do you mean. We can:
    1. implement entire functionality of parsing notification in Rust.
    2. implement it twice (in C++ with higher priority and in Rust in the meantime)
    3. implement entire functionality in C++ and some part of it in Rust (for future work)
    4. Implement some part in C++ and some part in Rust and use binary bridges

Which one did you actually had in mind?

Honestly it was just a thought... I haven't thought it through in much detail. But I think the thinking was mostly D (by necessity) – any potential Rust code would probably be called from the C++, at least initially.

Use enum class for MessageTypes, add missing newlines to MessageSpec interface implementations, use camel case for message specs dictionary variable name.

LGTM, I still think we should go lazy with the initialization but if I'm outvoted, then we can go with eager init. Accpeting.

@marcinwasowicz, sorry I missed your response earlier!

In D3940#111019, @marcinwasowicz wrote:
  1. Currently we do not implement MessageSpec for all type of messages. In particular you advised me during 1:1 not to implement it for messages that do no generate notifications (ADD_MEMBERS etc.). This way if message type is not present in MESSAGE_SPEC map we return nullptr as unique pointer to its content. Nevertheless we still have enumeration type that covers all message types. We can perform some simple check in translateRawMessageInfoToClientDBMessageInfo in next diff to throw runtime error if message of type not covered by this enumeration is received. If someone is led to MessageSpecs.h they will probably realise thet need to implement MessageSpec for that message if they want its content to be stored in the database. How is it currently done on the JS part? Perhaps we could apply the same solution here.

I think we'll want some protection to make sure somebody doesn't create a MessageSpec in JS with generatesNotifs: true without also creating a MessageSpec in C++. One thing we could do is do a check at app boot for every JS MessageSpec with generatesNotifs: true to make sure C++ has a corresponding MessageSpec.

For this, can we create a new task to follow up?

@marcinwasowicz, sorry I missed your response earlier!

In D3940#111019, @marcinwasowicz wrote:
  1. Currently we do not implement MessageSpec for all type of messages. In particular you advised me during 1:1 not to implement it for messages that do no generate notifications (ADD_MEMBERS etc.). This way if message type is not present in MESSAGE_SPEC map we return nullptr as unique pointer to its content. Nevertheless we still have enumeration type that covers all message types. We can perform some simple check in translateRawMessageInfoToClientDBMessageInfo in next diff to throw runtime error if message of type not covered by this enumeration is received. If someone is led to MessageSpecs.h they will probably realise thet need to implement MessageSpec for that message if they want its content to be stored in the database. How is it currently done on the JS part? Perhaps we could apply the same solution here.

I think we'll want some protection to make sure somebody doesn't create a MessageSpec in JS with generatesNotifs: true without also creating a MessageSpec in C++. One thing we could do is do a check at app boot for every JS MessageSpec with generatesNotifs: true to make sure C++ has a corresponding MessageSpec.

For this, can we create a new task to follow up?

Definitely, I can create a task follow-up. I did not respond earlier since I thought it should be implemented prior to landing this diff so I started researching possibilities.

I think we'll want some protection to make sure somebody doesn't create a MessageSpec in JS with generatesNotifs: true without also creating a MessageSpec in C++. One thing we could do is do a check at app boot for every JS MessageSpec with generatesNotifs: true to make sure C++ has a corresponding MessageSpec.

Sounds like a good idea

native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs.h
92 ↗(On Diff #12402)

I think we should avoid premature optimizations. In this case it doesn't sound too expensive to create a fixed number of objects at the start of the app, so keeping it eager makes the most sense to me. If we detect that this is a performance issue, we can always make it lazy.

48–50 ↗(On Diff #12474)

We can probably simplify it

native/cpp/CommonCpp/NativeModules/MessageOperationsUtilities/MessageSpecs/MultimediaMessageSpec.h
10 ↗(On Diff #12474)

We usually use media as a name

This revision is now accepted and ready to land.May 11 2022, 3:35 AM

Simplify MessageSpecs dictionary

Definitely, I can create a task follow-up. I did not respond earlier since I thought it should be implemented prior to landing this diff so I started researching possibilities.

Please link the task!! A link to a task is what I'm looking for here

Definitely, I can create a task follow-up. I did not respond earlier since I thought it should be implemented prior to landing this diff so I started researching possibilities.

Please link the task!! A link to a task is what I'm looking for here

Reminder about this – the task still has not been linked (as far as I can see)

I created Linear issue with task to implement checking whether each MessageSpec in JS has corresponding MessageSpec in C++. https://linear.app/comm/issue/ENG-1159/ensure-that-all-messagespec-objects-in-libsharedmessagesmessage

Move to persistent storage