Closed mstenta closed 2 years ago
Another consideration Mike and I discussed today is that the configuration for each field module will need to be retrieved from the server, cached in IndexedDB, and updated periodically. Ideally this would not require a lot of consideration from the module developer but would be handled by Field Kit Core.
The best solution I can come up with is to provide some sort of getter that any module can call to get its own settings. Behind the scenes, Field Kit Core will check the server for updates whenever this getter is fired; if it can be reached successfully over the network, it will return these values, and also cache the config in a special IDB store, just for that module, so that if later the server cannot be reached, it has a local copy of the last good settings.
The configuration for each field module would live at a unique endpoint, and would only contain that modules config. Field Kit Core would get these endpoints from the /farm.json
endpoint, the same way it gets the URL for the module's script. This way, modules could update their own config w/o necessarily fetching all other modules' confgs as well, which might become quite large. It still might be desirable to do such large batch fetches from time to time as well, but at least this way, with separate endpoints, we have the choice.
Yes! Great summary.
And I want to emphasize this:
It still might be desirable to do such large batch fetches from time to time as well
We discussed firing the getter function when the field module is loaded. Also firing it periodically for all field modules will ensure that settings are available even if you haven't loaded the module until you are offline.
Another consideration Mike and I discussed today is that the configuration for each field module will need to be retrieved from the server, cached in IndexedDB, and updated periodically. Ideally this would not require a lot of consideration from the module developer but would be handled by Field Kit Core.
This is a great consideration!!
I'm thinking.. obviously we want to minimize the time between a configuration change on the server and when that config is known by the client. Could the server have some kind of a state
for the Field Module config? The state
could be a simple timestamp
or generated string of characters. When a configuration is changed, the state
would change. The client could be notified of this two ways...
state
could be returned with every API request. This way anytime FK makes a request the state of the server will be known, almost constantly.farm.json
field_module
settings. Thus, to see if any changes have been made, you requestfarm.json
and see if the state
is different than the last known state
.It still might be desirable to do such large batch fetches from time to time as well
Regardless how the state
is implemented, I think this could decrease how often large batch fetches would be made. Rather than doing a batch fetch "every hour" (as an example), a request to farm.json
could be made every hour, and perform the batch update if a change has been made.
I think this could also be used as a way of notifying when new field modules are installed/updated. If FK is open when a FM is added to the server, the new (or modified) FM could be pulled in "on the fly" (not sure if this is possible?) OR at the minimum, a notification of some sort could be shown saying "New field modules are available. Logout/login to refresh"
At first I was thinking the event that FK is open when a new FM is added would be unlikely, but because the PWA could be running in a minimized browser or offline for a few days, this could happen fairly often. Also, if a farmer adds the FM to farmOS, seeing a notification to "restart" FK would be a good indication of it's success (if the FM isn't just added automatically!)
To really minimize time FK configuration is "out of sync" the state could be returned with every API request.
Hm, so literally every API request, whether it was for a log, or a taxonomy term, or for farm.json
, would have somewhere in the JSON response this state
value? What exactly would we encode in that value then? Just the last time that any field module has changed, or would it be possible to encode that a specific field module has changed? I guess I'm a little fuzzy on what all would be included in this state
, if it's some sort of string/timestamp/hash, or if it's an object, how it's structured.
Also, I'm not sure if this is strictly relevant, but you might find this talk about "logical clocks" interesting: https://www.dotconferences.com/2019/12/james-long-crdts-for-mortals
@jgaehring woah thanks for sharing that video! Connected more of the dots surrounding CRDTs for me especially concerning the actual implementations. The "logical clocks" makes a lot of sense in that context!!
What I'm thinking of is a bit more simple, although it's certainly related because we are working local-first :smiley: Because we won't be modifying the config for a field module locally (as I understand) there shouldn't be a need to handle conflicts. In terms on the local app, we just want to know when there is a change on the server regarding field module configuration (or possibly changes to the field module itself & knowledge of new field module installation). Consider this a sub-set of local-first problem where the config for the local app is only modified on the server, but needs to be kept up to date locally.
Yea, state
could simply be a timestamp
generated on the server anytime a change to a field module config is made. The first time the client starts it would save this timestamp
. Then, rather than fetching the configs for each field module every hour, it could simply load farm.json
and check the last_updated
timestamp key under the field_modules
object. If the timestamp
is greater than the one saved locally, we know a change has been made, and all of the field module configs could be fetched.
would it be possible to encode that a specific field module has changed?
I had been thinking of this as a "global" state, but each field module could have it's own state too. This would be most useful for pulling in changes to a field module itself, which would probably be best handled by versioning the field module code? I think the "global" state would be sufficient for tracking field module configuration because it would likely be a small request to pull in the configuration for all field modules (but each field module could have a state
tracking it's configuration if we wanted that granularity)
Hm, so literally every API request, whether it was for a log, or a taxonomy term, or for farm.json, would have somewhere in the JSON response this state value?
This is the most "extreme" - the alternative would be to return the state
in a request to farm.json
that could be run on a periodic schedule
Cool! I like this idea. I wonder if it's worth considering other application state as well, and modelling that as a whole. I could see this being useful for a lot of the data we're storing on Field Kit. Sort of like a global pulse for the whole system that keeps track on a high level of when certain entities have changed, and if so, directs any client to where the change has been made so it can update itself.
Just found another good article on logical clocks, which refers to the above talk:
In an effort to test script caching (#310), I ran a full web build of the client and served it up at http://localhost:8887 via Chrome Web Server. I did this rather than using the Webpack devServer so the necessary service worker would be included, unlike in the regular dev environment.
This created a bug that wasn't caught before, because of Webpack's proxy server prevented it from becoming an issue. Here's the error from the console:
Access to script at 'http://localhost/sites/all/modules/farm_precipitation/dist/FieldModule/Precipitation/index.js' from origin 'http://localhost:8887' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Basically the scripts aren't getting the necessary CORS headers that are being applied to other http responses, like the JSON we're getting via restws. @mstenta, any idea why the JS files aren't getting the same headers?
Copying this from chat with @jgaehring:
the
farm_access
module adds those headers to any requests that DRUPAL serves But in the case of the JS files, Apache is serving them directlySo there are two options:
- We add headers via Apache
- We serve the JS files through Drupal
I think 2 is the best choice, because it doesn't add any requirements on the server/hosting layer
In the
farm_client
module, we need to provide a route (eg:client/js/*
) which is a PHP page callback function that callshook_farm_client_module_info
to figure out the actual location of the JS file, and then output the content of it So... on the bight side... this would also abstract that "contract" of where the JS files live By always serving them through the same path pattern in Drupal... but in the actual module code files they could be anywhere (as long as the module declares that somehow so farm_client can find it)
I can take a quick stab at that...
Done! I set it up to serve the field module JS file at /farm/client/js/[module-name]/index.js
This is deployed to test.farmos.net now with the farm_precipitation
module, resulting in: https://test.farmos.net/farm/client/js/precipitation/index.js
Woohoo!! Thanks, @mstenta!!
:rocket:
FYI @jgaehring I merged part of the farm_client
branch into farmOS (this part: https://github.com/farmOS/farmOS-client/issues/311#issuecomment-626810786), but not the "field module" specific stuff yet.
I moved that to a new field_modules
branch in my fork: https://github.com/farmOS/farmOS/compare/7.x-1.x...mstenta:field_modules
So update your local dev server to use that branch moving forward.
I'm going to close this, because it was initially opened when we were implementing field modules in 1.x, so a lot of the details discussed above aren't really relevant to what remains ahead for field modules in 2.x. I was leaving it open b/c I think we still need to keep server configuration in mind as we move closer to the release of Field Modules, which I would like to be simultaneous with the FK 2.0.0 at this point. Perhaps we can do a quick assessment of remaining issues while addressing #468.
We've identified some use-cases where it will be necessary to have server-side configuration for field modules, so that farm admins can configure how field modules work in field kit. The configuration forms can be built in Drupal using Form API and Drupal variables/tables, served as JSON to Field Kit, and stored in Field Kit for offline reference.