directus icon indicating copy to clipboard operation
directus copied to clipboard

Add the ability to format $NOW dates with adjustment.

Open ijpatricio opened this issue 3 years ago • 0 comments

The issue

This addresses #11314

Long story short: if we want to define in no_code validation, that a date field should be greater or equal than tomorrow, it's currently impossible.

This is because the nearest option on the current API of the dynamic $NOW, is $NOW(+1 day). In turn, this will add 24H to the current time of processing, which is correct. But "tomorrow" starts at 00h00, which makes it impossible to build such validation only with no_code.

Think not only "tomorrow", but also "following month".$NOW(+1 month) will add 30days, X hours, X minutes, and so on. "Following month" starts on the next day 1, at 00h00.

I think it's clear now, but feel free to visit the discussion mentioned above.

The solution

While the most beautiful solution would be to add an "expressive parser" that would understand things like:

  • $NOW(tomorrow)
  • $NOW(tomorrow at 15h00)
  • $NOW(next month, on the 20th at 12h00)

... I believe we can solve this gracefully with a native solution of the library underneath, which is date-fns.

Having said so, we could leverage the native API, which exposes the format function.

With the addition of such feature, we can now achieve such validations:

// "tomorrow"
$NOW(+1 day | format: yyyy-MM-dd 00:00)

// "next month"
$NOW(+1 month | format: yyyy-MM-01 00:00) 

//"on the 15th of following month"
$NOW(+1 month | format: yyyy-MM-15 00:00) 

All we have to do is to perform an adjustment, just like before, but then "pipe" (|) to another function, format: xxxx. The value of xxxx will be used by date-fns, so every format and specification we can use, it's documented here. I think it will not suffice to delegate users to go there and read, hence Directus docs make brief documentation of such, with the basic examples, and then reference to the extensive, hard documentation.

My thinking process on the code implementation

First of all, disclaimer: I don't have much experience with TS 😅

So, for now, we're only adding format task to the API, which makes $NOW(+1 month | format: yyyy-MM-01 00:00) possible and working.

What I did was:

  • Current adjustment is the mainTask.
  • More tasks can be added, sequentially, by piping.

This would make also possible:

$NOW(+1 month | format: yyyy-MM-01 00:00 | anotherTask: payloadForTask ) 

So, I split the whole adjustment by |, trim them, execute the first one exactly like before, and then process the rest. Only if the task is format, then I delegate the processing to the native data-fns/format. This is to allow possibility of the next topic.

Tests were added. They speak for themselves 😄

All signatures, types remained intact.

Considerations

As we can easily add more piped tasks, that would perform more actions on the Dateobject, I think we could also achieve to add a hook that could, of course, be customized by the default extension system!

Picture this:

$NOW( +1 day | _check: availability, rooms )

This could represent a call to the hook now:_check, providing as arguments:

  • The Date object
  • Payload: availability, rooms.

This hook now:_check must be a custom extension, so its developer is responsible to interpret payload, and return a Date object, or undefined.

This is a rough idea, but I hope the message goes through!

Breaking changes

No breaking changes. This change is purely additive.

Thank you for your time!

ijpatricio avatar Jan 29 '22 20:01 ijpatricio