-
Notifications
You must be signed in to change notification settings - Fork 11
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
Action trigger feature #25
base: master
Are you sure you want to change the base?
Action trigger feature #25
Conversation
8205243
to
bde6769
Compare
bde6769
to
ce82d2b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work. As I stated in the demo, I really think this puts the module much closer to where it needs to be in order to become a viable long term solution to versioning headaches. I have two primary concerns, other than what's detailed in the comments:
-
Documentation -- I know you wrote a ton of new content, which is great, but I feel like it still requires a lot of context to understand what's going on. I had to read it three times to wrap my head around it, and I'm more familiar with the use cases than most will be. So I think just backing up and thinking really high level about what problems this solves, and working by example is really key. In fact, I would probably prefer that you worked off an imaginary project to show the real business case for it and to connect all the examples into one coherent theme.
-
Coupling -- I'm sure this wasn't your preferred architecture, but it does seem like what used to be a fairly self-contained module is now has all kinds of awareness about different CMS features. You've added a bunch of public APIs that are named specifically after external features, and things like GraphQL Schema are now imported into Snapshot. It may very well have to be that way just because our framework isn't very well decoupled to begin with, but I'd just share that if I were building this from the ground up, the way I would envision that working is with something like a registry that accepts any number of classes of a certain interface that can declare their identifiers. So something like:
Injector:
Snapshot:
properties:
listenerRegistry:
graphql: %My\GraphQL\Listener
Then, your implementation is $snapshot->fireAction('graphql', array $params)
, which fails gracefully if nothing in the registry matches graphql
, and you destructure the argument signature of those methods so it just takes arbitrary params -- more like an event system. You lose the typing, but I think that's a fair trade off for the value of decoupling.
Does that make sense? Right now it feels like you're really overloading the purpose of extensions. Like I said, you've probably got good reasons for that, but just a gut reaction tells me there's a cleaner approach somewhere.
'`save`': '`Save page`' | ||
|
||
This means each time a user saves a page via page edit form a snapshot will be created with a context message `Save page`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still isn't clear to me what save
is. I think I need to see the code that is providing this identifier back to the config. I assume that's something like:
class CMSPageSaveAction ...
{
public function getActionName(): string { return 'save' }
}
???
I think it's important here to show why a snapshot is created whenever someone saves a page, so it doesn't look so much like magic.
|
||
'`save`': '`Save page`' | ||
|
||
This means each time a user saves a page via page edit form a snapshot will be created with a context message `Save page`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you define "context message" here, or just show what the end result looks like in the IUI?
|
||
`CustomActionListenerAbstract` is the parent class because this action is a custom mutation | ||
|
||
Returning `false` inside `processAction` makes the module fallback to default behaviour. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is "default" behaviour at this point?
|
||
There are two main cases here: | ||
|
||
`1` - snapshot module doesn't have enough information to find origin and is using `event` as a placeholder to fill the gap. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An example of this would be really helpful, as you've done below.
* @param mixed $controller | ||
* @return Page|null | ||
*/ | ||
private function getCurrentPageFromController($controller): ?Page |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like I've written this code so many times. Would be worth considering if it should be a core API. Nothing in here is unique to the concerns of snapshots.
*/ | ||
private function getActionType(string $query): ?string | ||
{ | ||
$action = explode('(', $query); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this assume the query has arguments?
$url = $request->getHeader('referer'); | ||
$url = parse_url($url, PHP_URL_PATH); | ||
$url = ltrim($url, '/'); | ||
$page = $this->getCurrentPageFromRequestUrl($url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second time we've done this. Does it belong in a parent class, or maybe in your trait?
|
||
if ($owner instanceof Create) { | ||
return static::TYPE_CREATE; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like really awkward coupling between the extension and its owner. It feels like something that should be the concern of the CRUD operation. Since it's static anyway, what if this was just return $owner->config()->snapshot_action_type
, and this class defined a default value in private static $snapshot_action_type
to ensure the property always existed?
} | ||
|
||
return $actions[$identifier]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is singleton access only, should it just be static? There's nothing in here that requires an instance.
// no need to add the origin to the list of objects as it's already there | ||
$origin = $owner; | ||
} | ||
} elseif ($origin->ClassName === $owner->ClassName && (int) $origin->ID === (int) $owner->ID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to compare hashes with SnapshotHasher
here.
'graphql_crud_create': 'GraphQL create' | ||
'graphql_crud_delete': 'GraphQL delete' | ||
'graphql_crud_update': 'GraphQL update' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have plans on how to make these i18n compatible?
--- | ||
SilverStripe\Snapshots\Snapshot: | ||
actions: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's worth considering grouping the actions by listeners. Action names are probably not unique in general.
For example:
Page\CMSMainAction - save
Form\Submission - save
Should the configuration be moved to individual listeners then?
…ave-snapshots SS-8748: Avoid recording useless save actions
Some snapshot items can be information-only so don't require a publish at the owner. These items will be excluded from status flag checks
The lister mode was not marking items as WasPublished. This meant the status flags were always showing as 'modified'
…cation-snapshots Bugfix/no modification snapshots
Action trigger workflow feature
New workflow added which is based on user actions as opposed to model writes.
Features
readme