-
Notifications
You must be signed in to change notification settings - Fork 7
Re fetching in producers
There are several utilities in default producer to facilitate fetching / re-fetching of entity-typed values. They serve to following purposes:
- Abstract out the need to get specific reader.
- Abstract out the need to get fetch model.
- Automate handling of not-found entity instances (throw error result).
- Employ filtering.
All these aspects can be handled as part of end-application code. This however leads to greater code base and less intuitive / comprehensible code. Also, since https://github.com/fieldenms/tg/issues/1107 every entity with known ID can be opened in entity master using URI. This requires end-application developer to carefully turn on domain-driven filtering inside producers.
It is recommended to use these utilities to abstract out these important aspects and thus simplify the code.
Lets say producer context contains some entity inside. Imagine situation where this entity is not [yet] persisted. In this case re-fetching is not needed and subproperties of this entity [or the entity itself] can be used for constructing new entity backed by the producer. This case is rare.
But what happens when context contains persisted entity? This is where things get complicated. It is not easy to know fetched properties' graph for this entity (all other properties are proxied). In this case we can re-fetch the instance to know for sure that all necessary information is present.
Someone would argue that if fetched properties' graph is known then it is easier not to re-fetch instance if the graph is sufficient. Or even graph can be altered to make it sufficient. But that is not very reliable approach. The problem is that actions are added all over the place inside end-application and upper fetch models differ in each place. Also these models evolve and that could break producer logic.
Standartisation is very reasonable here and our answer to that is: re-fetch everywhere.
Lets define entity
as the entity being produced. If there is a need to initialise "document"
property in it from masterEntity(Document.class)
then it should be made like this:
entity.setDocument(refetch(masterEntity(Document.class), "document"));
refetch(instance, property)
method will take care of getting the reader, taking appropriate fetch model for "document"
property, and will correctly apply filtering. If for some reason the entity does not exist (filtered out or deleted previously) it will throw an error and the toast will appear to the user also preventing the entity to show in entity master.
There are cases where we know only ID. In these cases we use fetchById(id, property)
method with similar signature:
if (currentEntityInstanceOf(HierarchyEntry.class)) {
entity.setKey(fetchById(currentEntity(HierarchyEntry.class).getId(), KEY));
...
Other cases require fetching by known keyValues
(fetchByKey(property, ...keyValues)
):
entity.setKey(fetchByKey(KEY, currentEntity(SynDocumentSheetModification.class).getDocument().getKey()));
Please note that keyValues
argument is last (swapped with property
parameter) -- that's because it supports variable number of values.
refetchInstrumentedEntityById(id)
fetches the producer entity itself. This is useful for one-to-one associations under compound master's secondary menu item:
protected WaCostDetails provideDefaultValues(final WaCostDetails entityIn) {
if (keyOfMasterEntityInstanceOf(WorkActivity.class)) {
return refetchInstrumentedEntityById(keyOfMasterEntity(WorkActivity.class).getId());
} else {
...
There is utility method with custom fetch model for that (fetchById(id, fetchModel)
):
final WorkActivityExpendable workActivityExpendable = fetchById(masterEntity.getNextExpendableId(), fetch(WorkActivityExpendable.class).with("inventoryPart", "quantityRequired", "quantityExpended").fetchModel());
... // workActivityExpendable to be used in terms of the above very specific fetch model
Frequently we use producer entity as a carrier for new instance we produce. For example OpenWorkActivityMasterAction
carries new WorkActivity
instance in its "key"
property. In that case we cannot use refetch(instance, property)
-- OpenWorkActivityMasterAction.property
fetch model will be used, but we need WorkActivity.property
fetch model instead. For that reason refetch(instance, property, entityType)
was added:
final WorkActivity newWa = new_(WorkActivity.class);
newWa.setWaType(refetch(masterEntity.getWaType(), "waType", WorkActivity.class));
...
entity.setKey(newWa);
fetchByKey(property, entityType, ...keyValues)
variant also exists and proven to be useful:
final WorkActivity newWa = new_(WorkActivity.class);
...
newWa.setLocationSubsystem(fetchByKey("locationSubsystem", WorkActivity.class, masterEntity.getLocation(), masterEntity.getSubsystem()));
entity.setKey(newWa);
Even greater attempt was made to simplify major cases for creating new instance and setting one / two properties into it. Consider following example:
...
} else if (keyOfMasterEntityInstanceOf(Equipment.class)) {
// '+' action on Equipment embedded centre on Equipment compound master
entity.setKey(create(new_(Equipment.class)).apply("major", keyOfMasterEntity(Equipment.class)));
return entity;
} else if (keyOfMasterEntityInstanceOf(LocationSubsystem.class)) {
// '+' action on Equipment embedded centre on LocationSubsystem compound master
entity.setKey(create(new_(Equipment.class)).apply("locationSubsystem", keyOfMasterEntity(LocationSubsystem.class)));
...
Here new_(Equipment.class)
is the same as co(Equipment.class).new_()
(just a little bit shorter). And create(newEntity)
method gives ability to "apply" some raw (not re-fetched) value from the context. The method does three things:
- re-fetches the value internally and sets it into
newEntity
; - throws validation error on property (if any);
- makes property not-editable.
There is also create2(newEntity)
variant to be able to chain two .apply(..., ...)
invocations.
Per aspera ad astra
- Web UI Design and Web API
- Safe Communication and User Authentication
- Gitworkflow
- JavaScript: Testing with Maven
- Java Application Profiling
-
TG Development Guidelines
- TLS and HAProxy for development
- TG Development Checklist
- Entities and their validation
- Entity Properties
- Entity Type Enhancement
- EQL
- Tooltip How To
- All about Matchers
- Streaming data
- Synthetic entities
- Activatable entities
- Jasper Reports
- Opening Compound Master from another Compound Master
- Window management test plan
- Multi Time Zone Environment
- GraphQL Web API
- Guice
- Maven
- Full Text Search
- Deployment recipes
- Application Configuration
- JRebel Installation and Integration
- Compile-time mechanisms