-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Improves Controller and Adapter relationship #549
Improves Controller and Adapter relationship #549
Conversation
flovilmart
commented
Feb 21, 2016
- Controllers that have adapters are AdaptableControllers
- AdaptableController is validating the Adapter type dynamically
- AdapterLoader is responsible for loading the adapter based on mixed params and a defaultAdapter (if provided)
@nlutsenko a small refactoring for the Controller / Adapters relationship so we maximise reusabliliy. Adapters can now be specified in the 'options' as string (to load the module), function to instantiate, or the instantiated object directly. |
I'd like to end up in a situation where external people can implement adapters without having the I'm open to other architectures though. Thoughts? |
I'm removing the dependency on BaseAdapter, and make a check on the options.constructor !== Object. |
@flovilmart updated the pull request. |
1 similar comment
@flovilmart updated the pull request. |
@drew-gross so that's refactored without the BaseAdapter. There is no need of dependency anymore between adapter and parse-server. Only the PushAdapter provides super class implementation, to remove code duplication internally. parse-server should still be a 'dev dependency' and we should be able to run the according tests with 3rd party adapters, this is something that I would have liked to have with the parse-server-urban-airship to ensure the adapter is working correctly as the test suite is exactly the same as the core one. For the mail adapter, I believe we'll have a MailController, with the same adapter interface,
Mail adapter could this way log in console.warn the calls to email to tell the user that it's improperly configured. I could also move the Default Adapter into AdaptableController
This way we won't externally need to pass it each time we instantiate a controller. Toughts? |
@flovilmart updated the pull request. |
So thanks to ES6 and classes, we have a proper
That will be the same for mail, and that could even be like:
For the adapters, I'll move all of them externally TBH. With this architecture, we'll just have to update for example:
And that for any adaptable controller, You'll notice too that the index.js don't hold any logic anymore to load the proper adapter. Which make it cleaner :) |
- Controllers that have adapters are AdaptableControllers - AdaptableController is responsible to instantiate the proper adapter if needed (string, function or BaseAdapter) - BaseAdapter is the base class for adapters, allows skipping when passed directly to the controller
@flovilmart updated the pull request. |
This looks promising, here are my thoughts:
So, the only actionable piece here would actually be - restricting the fancy new DefaultAdapter piece into say AdapterLoader.js with functions to purely load the adapter if it doesn't exist and return it of a specific type. Let me know what you think, I am happy to discuss, improve and restructure this as we go. I am thinking here in ObjC/Swift flow/structure, since those are my mother-languages and this overall design might feel weird in the world of crazy JavaScript, but I really really hope that the world is moving to this direction, and we can see that with ES6 classes and Flow for type safety. |
@nlutsenko we could enforce type safe adapters, but that would mean that external adapters have to import the base adapter from parse-server to extend it OR that we compare prototypes with the base adapter. I can do the latter to enforce implementation of required methods. I can move the adapter loading out of AdapableController into AdapterLoader. AdapterLoader would expose the different loading mechanisms, for all supported types. But still expose a load(options). The ClassesController decoupling is quite a big thing, would not touch that yet, but focus on the 3 we have decoupled to start having more external contributions for push, files and logs. |
What if the base file adapter itself is made into a different repo, and all other file adapters just import that? Something like:
This could solve the problem of type safety. Any new filesasapter would not need to have the entire parse-server as a dependency, just this. Also, i guess this would require minimal changes to other code. What do you think? |
That would be ideal for sure. That implies a bigger refactoring than what I'm trying to do here :) which is having a common adapter loading mechanism. I'm implementing type-like safety, making sure the adapter has the right methods implemented as well as harder enforcement on loading a controller without an adapter. |
- Adds dynamic prototype conformance check upon setting adapter - Throws when adapter is undefined, invalid in controller
@flovilmart updated the pull request. |
@nlutsenko The inherent problem with JS compared to Swift is that we're talking 2 completely opposite paradigms: JS prototype based, functional, loosely typed, dynamic language to OO strongly typed static language :) We could, in a near future, type the controllers constructors with Flow, but we have to decide whether we want external adapters to require parse-server or not. Flow Polymorphism is a real Good candidate for that:
I could drop it now, but that would break all 3rd party modules support for the time being... |
The reason I'm against having external adapters that depend on My opinion is that aiming for type safety in adapters is "working against the grain of the language" and is not something we should attempt. Rather, at server construction time, we should check the adapters for compliance to the interface. This also allows for easy forward compatibility as we can have optional features in adapters, where you can have the server turn different features on and off based on the functions in the adapter. Scheduled push would be a prime example of this. |
OK, so as you can see here I'm checking the interface of the adapter: https://github.com/ParsePlatform/parse-server/pull/549/files#diff-51ac4c2ee846910ae669884b9bdc1234R37 A controller has an |
* - object: a plain javascript object (options.constructor === Object), if options.adapter is set, we'll try to load it with the same mechanics. | ||
* - function: we'll create a new instance from that function, and pass the options object | ||
*/ | ||
constructor(adapter, options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we need options
here, as it looks like it's unused right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's definitely unused and the doc is plain wrong!
Agreed with Looks like this is now inline with our approach towards:
Only a single nit in the code, but otherwise LGTM. |
@nlutsenko I've hardened the AdaptableController, marked the _adapter private, force the validation upon setter and added a few more tests. |
@flovilmart updated the pull request. |
Lovely! LGTM, merging now. |
Improves Controller and Adapter relationship
Dang, I had some thoughts but I guess github lost them. Depending on Having a class with a static function is not really javascript style (for |
Dang... For the push adapter, it is not required per-se, but I removed some code duplication in our code base. Which is what was intended. Should we move the common logic to the controller? The only part is classifyInstallations which can be different for each providers. |
If it can be different for each provider then it should probably be part of the adapter interface, and we should document that it is available/required for implementers to supply it. (and maybe supply a default as a parameter or a separate npm package or something to make life easier for implementers) |
Ok, so I'll remove the common implementation from the PushAdapter in a separate PR. |