feat: align saved filter API shape, add saved_filter_id support and dynamic label mapping#6702
feat: align saved filter API shape, add saved_filter_id support and dynamic label mapping#6702notsafeforgit wants to merge 2 commits intostashapp:developfrom
Conversation
|
This is your 3rd PR. Lets go ahead and limit the amount that we have open till 1-2. Also, for larger refactors like this please reach out to someone on the dev team before blindly PRing. We like to take things slower around here and stacking all of these on top of each other makes our life harder. Please reach out on GH, Discord, or Discourse. You can ping me whenever, i'm around |
| c.value.length > 0 && | ||
| typeof c.value[0] === "string" | ||
| ) { | ||
| this.value = (c.value as unknown as string[]).map((id: string) => ({ |
There was a problem hiding this comment.
You'll notice some "ugly" double-casting. This was a necessary trade-off to support the new normalized ID-only storage format while maintaining the UI's rich model.
The ModifierCriterion<V> class is a generic base used by almost every filter in the UI. For many filters, V is a complex object (like ILabeledId[]), but the raw data coming from the saved filter's object_filter is now just a simple string[] (IDs only).
Because V is a generic type parameter, TypeScript won't allow a direct cast to string[]. We have to cast through unknown to "reset" the type checker so we can map the IDs back into the rich objects ({id, label}) the UI expects. The alternative would be a massive refactor of the entire filter type hierarchy to support union types (V | string[]) at every level. This would have touched dozens of files and significantly increased the risk of regressions.
The current implementation effectively "hydrates" the ID-only storage format back into the rich UI model using the label_mapping provided by the API, keeping the complexity isolated to this single base class.
|
@Gykes sorry about the 3 rapid fire PRs, this is all I have for now. I have just been bothered by these issues in stash for years now and finally got the time to really go heads down for the past week or so and iterate. Despite opening the PRs in quick succession, I spent a lot of time thinking about how to actually go about implementing these changes in a clean way, so they should hopefully not be a nightmare to review. |
|
Projects like https://github.com/1letzgo/stashy and https://github.com/secondfolder/stash-tv have long had to hack around the oddity of saved filters returning a different shape to clients vs what the find* apis actually accept, and most apps that use stash's api like these just have broken filters all the time. Hopefully this PR will make clients like them effortless to maintain when it comes to fetching the objects they want out of stash! |
|
No problem, I don't want to make it seem like we don't want you making fixes and features, as we appreciate it. Currently we are getting near the end of the release and neither myself or the main dev has put any thought into how to handle this situation. Having a 30 minute convo with the admin team can go a long way of saving both of our times if we prefer a different implementation or just want to defer it completly. |
- Added comprehensive tests for LabelMapping deduplication and full property coverage in resolver_model_saved_filter_test.go - Removed redundant hardcoded switch [type]Filter statements across 10 query resolvers in favor of direct models.FilterMode assignment - Cleaned up unused fmt imports generated by the refactor
0c39702 to
45ae1c2
Compare
This PR refactors how saved filters are stored and handled in the API to align them with standard input shapes. It also introduces the ability to query results directly using a
saved_filter_id, which simplifies UI state management and improves performance.Importantly, this change migrates labels from static storage within the filter JSON to a dynamic resolver, ensuring that labels (e.g., Tag names, Performer names) are always up-to-date even if the underlying objects are renamed.
Key Changes
1. API Shape Alignment & Migration
object_filterin thesaved_filterstable has been refactored to match the GraphQL input types (e.g.,SceneFilterType). Previously, it stored a more complex structure that included hardcoded labels, which led to stale data.2.
saved_filter_idQuery Parametersaved_filter_idparameter to allfind*queries (Scenes, Images, Performers, Studios, etc.).filter(FindFilter) overrides like search string, paging, or sorting.3. Dynamic Label Mapping Support
label_mappingfield to theSavedFiltertype that is resolved dynamically at request time.object_filterblob and following better data normalization practices.4. UI Integration
label_mappingand the simplifiedobject_filterstructure.Testing
pkg/sqlite/migrations/86_postmigrate_test.goto verify the conversion of various saved filter configurations.internal/api/resolver_saved_filter_helper_test.goto verify:FindFilteroverrides.label_mappingfor tags, performers, and other entities.make validate(UI lint/check and backend lint/tests) andmake it(integration tests).