-
-
Notifications
You must be signed in to change notification settings - Fork 422
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
[RFC] Mandatory ManagedProviders #3017
Comments
I do not really see any reason either why we couldn't make it mandatory by now. |
It makes sense to me to require at least one managed provider for a registry nowadays. |
I don't think that introducing requirement for a managed provider on all elements is fully justified. You can have a read only (managed) installation which does work with providers and registry elements which are supplied from elsewhere - through files or central configuration server and no expectation to supply new elements at all. I believe main problem is somewhere else - a REST layer and UI can not reliably determine if a |
I thought about that, too. But what happens if we have two modifiable providers and add a new entity? Adding it to both is obviously wrong, but what is the criteria to choose one over the other? Since there is no deterministic order in which providers are added, it‘s not easy to decide. |
Each provider produce somehow a thing, a link and so on, so update can be actually an callback which is injected by a provider. In theory, if we would be following "domain driven design", it would be considered a recommended pattern. With current codebase and how it is shaped it is a bit awkward. |
I‘m not sure I understand what you say. User adds a new thing (item/whatever) via UI. UI makes a REST call and the thing now needs to be added. How does the system chose which provider shall be used if there is more than one modifiable provider? Currently the REST resource adds it to the registry and the registry forwards this addition to the managed provider (if available). |
Lets speculate a little bit. Below code is a pure example of how update logic could be shifted to a lower layer which actually does the work with updating the definition, while retaining a feedback loop for interested parties.
class Thing {
Function<Thing, Thing> onUpdate;
void onUpdate(Function<Thing, Thing> updateHandler) {
if (onUpdate != null) {
// first onUpdate handler is guaranted to be one handled by provider so we must call it first,
// later handlers will always receive definition merged by thing source
this.onUpdate = this.onUpdate.andThen(updateHandler);
}
// update will no happen, ignore handler
}
boolean isModifiable() {
return onUpdate != null;
}
}
class ThingBuilder {
ThingBuilder onUpdate(Function<Thing, Thing> updateHandler) {
// function input is new thing definition, returned value is updated thing definition
this.updateHandler = updateHandler;
}
ThingBuilder onDeleteConsumer<Thing> deleteHandler) {
// a callback for deletion of things
this.deleteHandler = deleteHandler;
}
Thing build() { ... } // make sure handlers are passed to a thing instance
} Then rest layer could be as simple as this: Thing thing = thingRegistry.get(thingUID);
if (thing.isModifiable()) {
thing.update(newDefinition);
} else {
throw new WebApplicationException();
} Assuming some provider: class JsonThingProvider implements Provider<Thing>, ThingProvider, Function<Thing, Thing> {
Collection<Thing> getAll() {
return getJsonStream()
.map(json -> thingBuilder.newBuilder(json))
.map(builder -> if (!readonly) builder.onUpdate(this).onDelete(this))
.toList();
}
Thing apply(Thing update) {
// merge definitions
return updated;
}
void accept(Thing deleted) {
things.remove(deleted.getUID);
}
} And a class ThingRegistry extends AbstractRegistry<ThingUID, ThingProvider> {
Collection<Thing> getAll() {
return getProviders()
.flatMap(Provider::getAll)
// appends an additional callback to provider one, if specified
.map(thing -> thing.isModifiable ? thing.onUpdate(this))
.map(thing -> thing.isModifiable ? thing.onDelete(this))
}
Thing apply(Thing merged) {
this.things.put(merged.getUID(), merged);
return merged;
}
// for deleted just untrack removed element
} |
Coming back to the original question: I don't really see a good argument for keeping or even extending the complexity that we have - our managed providers always come for each registry with the same bundle and it imho makes much sense to couple them now. It removes quite some code and simplifies the system's lifecycle. |
And backing up to the tangential discussion on having multiple managed providers, and allowing the UI to choose which provider to use of multiple managed providers, as well as knowing if the object is read-only... This is pretty much exactly what I've been working on in the Ruby scripting helper library lately. If you'll look closely at As for checking read-only-ness... my Anyhow, #3170 is semi-related, just cleaning stuff up for ScriptedRuleProvider to more fully match that paradigm. And, finally, back on topic - +1 making getManagedProvider() not return an Optional. I've forgotten the get() call on that a lot in the past few months. |
I been recently experimenting with |
@openhab/core-maintainers The
AbstractRegistry
defines the presence of a managed provider as optional. IMO we should change that and require a managed provider. This simplifies code (because we don't have to check if the provider is present when adding/removing elements) and we have implemented managed providers for ever registry anyway. WDYT?I would be willing to implement that change.
The text was updated successfully, but these errors were encountered: