Closed techanvil closed 3 months ago
Note that I've moved this back to the Backlog pending the outcome of this discussion on the design doc regarding the shape of the API.
Said discussion is now resolved and this issue is ready for AC.
AC ✔️
I've moved this back to AC pending an update once we've confirmed changes to the infrastructure underpinning the "New" badges, as discussed on Slack.
Update: I've moved this to the Backlog instead to make it clearer which issues are waiting for a change or approval to the design doc (see this comment on the doc).
The related design doc discussion has been resolved and this is ready for IB.
Unassigning myself as this is squad 2 related
Hi @ankitrox! Thanks for drafting this IB, nice work so far. A few points:
- [ ] The option will have the following data structure:
array( 'item-slug-1' => array( "expires" => 1700000000 ), 'item-slug-2' => array( "expires" => 1690000000 ), );
Arguably, we don't need a nested array structure here, if we only have the one subkey expires
. We could follow the example of the Dismissed_Items
setting, i.e.:
array(
'item-slug-1' => 1700000000,
'item-slug-2' => 1690000000
);
The nested approach is a bit more self documenting and extensible, which I do like. However, there's something to be said for consistency. Seeing as it's essentially the same conceptual shape maybe we should go for that (the simple, non-nested array) in the first iteration at least. This would also have a marginal benefit to the page weight as we'll be including the serialized value on the page as a result of preloading the path (as discussed further below). WDYT?
- [ ]
get_active_items
method:
- I don't think we actually need this method. We want to provide the full value of the setting to the client, and leave the logic for checking if an item has been seen/set before, and if it's active/expired to the client. This way the client can determine whether a request needs to be made to set the timer for the item.
- [ ] If an item with the given slug already exists, only its expires time is updated, preserving other attributes like seen. If the item is new, it is added with a default seen status of
false
.
- What's the reference to "seen" here? Did you intend to add another key to the nested structure above? I'm not sure we'd need a
seen
though (although I do think we need an extra selector where we can determine if something has been seen, as per below, but we can derive the "seen" status from whether the item has ever been set). Anyhow, please do clarify this one.
- [ ]
GET:core/user/data/expirable-items
- Callback for this route should return the items which has expired. That means the timestamp for the expiration time has been passed when we compare it with the current time.
- As discussed above, we should ensure the full value of the setting is returned from this route, and leave additional logic to the client side.
- [ ]
POST:core/user/set-expirable-item-timers
- This should accept the object consisting ofslug (type: string)
which represents the item andexpiresInSeconds (type: int)
represents the expiration time in seconds.
- As per the AC, this route should support multiple items so we can keep the number of requests to a minimum.
- [ ] If expiration time is not passed, set it to 0 that means it will never expire.
- This is not a requirement here, we should always expect an expirable item to have an expiry date.
- [ ]
get_expirable_items
method:
- We won't need this method, at least not that we currently know of. So, there's no need to include it - we can add it if/when we do need it.
- [ ] Create a store (fetchExpireItemStore) off
createFetchStore
.
- As discussed above, the related endpoint should handle multiple items in a single request, so the args should be updated accordingly.
- Also, super minor but the backticks are missing around
fetchExpireItemStore
, and it would be more grammatically correct to say "viacreateFetchStore
", or "from a call tocreateFetchStore
".- Please review the IB for any other backticks that have been missed in its formatting.
GET:core/user/data/expirable-items
route is preloaded using the googlesitekit_apifetch_preload_paths
filter, here's an example.POST
route should be core/user/data/set-expirable-item-timers
not core/user/set-expirable-item-timers
.hasExpirableItem()
, so we can tell if the user's already seen/set the item. This can return a boolean, true
if the item slug exists in the set of expirable items, false
if not. Please can you include this in the IB?Thanks for reviewing this @techanvil .
Regarding the seen
option, earlier I was writing IB based on the seen
property, but later I thought that it may not be required. Setting an item in expirable items itself should be sufficient to know that it has been seen. That one point remained in IB by mistake which I've removed now.
Rest of the points I have addressed as per your suggestion.
Assigning this to you for your re-review.
Thank you.
Thanks @ankitrox, that makes sense.
I've made a few amendments to the IB - some points were missed, and a couple of naming and grammatical tweaks were needed.
Please review the changes and if you're happy, feel free to move this to the EB.
IB :white_check_mark:
Looks everything good now. Thank you @techanvil .
Moving this to EB.
Hi @ankitrox, regarding the QAB here - rather than using fetchExpirableItems()
in the test, I would suggest rewriting it to use (and therefore test) setExpirableItemTimers()
.
Please can you also include instructions for testing hasExpirableItem()
?
Hi @techanvil ,
Thanks for the CR. I have addressed the comments in the PR and also updated the QAB to include setExpirableItemTimers
action instead of fetchExpirableItems
.
Assigning this to you for review again.
Thanks
Thanks @ankitrox! The QAB is looking good, I've left a few more comments on the PR.
Tested per the QAB and results were as expected:
First script about setting expirable timers triggered a request in network tab of the browser to core/user/data/set-expirable-item-timers
endpoint succesfully with response 200.
Getting the expirable items were successful too:
All the other scrips were as expected as well :
Moving ticket to Approval.
Feature Description
Add the generic “expirable items" infrastructure that will be used to manage the "New" badges.
See "New" badges in the design doc.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
POST:core/user/data/set-expirable-item-timers
should be added to Site Kit.slug
: A unique identifier for the item.expiresInSeconds
: The number of seconds after which this item should be expired. This can be renamed if it turns out to conflict with usage of the dismissed items infrastructure.core/user
data store:isExpirableItemActive( itemSlug )
: This selector should return aboolean
indicating whether the given expirable item is active for the current user, i.e. it will returnfalse
if the item has expired,true
otherwise.setExpirableItemTimers( item [, item…] )
: This action should set the expiry date of the specified items for the current user via thePOST:core/user/data/start-expirable-item-timers
endpoint.Implementation Brief
Note that the infrastructure should leverage existing APIs where possible, for example it may be viable to build it on top of the dismissed items infra in conjunction with the use of WordPress transients.
[x] Create a new
Expirables
directory in theincludes/Core
directory.[x] Create a new
Expirable_Items
class in theincludes/Core/Expirables
directory.Expirable_Items
should extend theUser_Setting
class.Expirable_Items
should be similar to theDismissed_Prompts
class, but with the following differences:googlesitekitpersistent_expirable_items
.get_default()
method which will return an empty array.add
methodslug
and anexpires_in_seconds
parameter. It first retrieves the current list of items.User_Options::set()
method.remove
method:slug
parameter.get_sanitize_callback
method:[x] Create a new
REST_Expirable_Items_Controller
class in theincludes/Core/Expirables
directory.Expirable_Items
and set it to the instance variable$expirable_items
.register
method:googlesitekit_rest_routes
filter the to register the route.get_rest_routes
method as callback to register the route.get_rest_routes
method:GET:core/user/data/expirable-items
- Callback for this route should return the associative array of expirable items and their expiry times.POST:core/user/data/set-expirable-item-timers
- This should accept the array objects consisting ofslug (type: string)
which represents the item andexpiresInSeconds (type: int)
represents the expiration time in seconds.$expirable_items::add
method iteratively to add items in the expirables data.GET:core/user/data/expirable-items
route is preloaded using thegooglesitekit_apifetch_preload_paths
filter, here's an example.[x] Create a new
Expirables
class in theincludes/Core/Expirables
directory.Dismissals
class in structure:dismissed_items
instance variable will be replaced byexpirable_items
.register
method should call register methods fromREST_Expirable_Items_Controller
andExpirable_Items
classes.get_expirable_items
method.Adding selector and action to
core/user
storeassets/js/googlesitekit/datastore/user/expirable-items.js
fetchGetExpirableItemsStore
) viacreateFetchStore
.baseName
should begetExpirableItems
controlCallback
should useexpirable-items
datapoint.fetchSetExpirableItemTimersStore
) viacreateFetchStore
.baseName
should besetExpirableItemTimers
controlCallback
should useset-expirable-item-timers
datapoint.argsToParams
should return array of objects. Each object should be in following shape.validateParams
should validate slug is non-empty string andexpiresInSeconds
is non-negative, non-zero integer.baseInitialState
should be{expirableItems: undefined}
baseActions
should havesetExpirableItemTimers
selector which would call thefetchSetExpirableItemTimersStore.actions.fetchSetExpirableItemTimers
.baseSelectors
should havegetExpirableItems
, which would have equivalent*getExpirableItems
resolver which would callfetchGetExpirableItemsStore.actions.fetchGetExpirableItems
.baseSelectors
should haveisExpirableItemActive
which would internally callgetExpirableItems
selector to check if item exists, and its expiry date is in the past.baseSelectors
should havehasExpirableItem
which would accept the slug of the item and check if it exists in the list of expirable items. It returnstrue
if item exists, elsefalse
.Note: Take a reference of
assets/js/googlesitekit/datastore/user/dismiss-items.js
to create the store.Test Coverage
expirable-items.js
inassets/js/googlesitekit/datastore/user/expirable-items.test.js
.REST_Expirable_Items_Controller
andExpirable_Items
classes.QA Brief
Adding the expirable items
network
tab of the browser tocore/user/data/set-expirable-item-timers
endpoint. Ensure that the request is successfully executed with response 200.Fetching the expirable items
Note that timestamp may be different which is object value like
1715622594
.Checking whether the expirable item is active
this should return the value
false
as the item must have expired.Checking whether the expirable item is exists
This should return
true
as we have createdfoo
item above.Run another command in browser console.
false
as we have never createdbaz
as expirable item.Changelog entry