Filter for DependenciesList and DependentsList#5267
Filter for DependenciesList and DependentsList#5267thomasdax98 wants to merge 11 commits intomainfrom
DependenciesList and DependentsList#5267Conversation
…ependentsList and DependenciesList
| return qb; | ||
| } | ||
|
|
||
| private applyFilter(qb: Knex.QueryBuilder, filter?: DependencyFilter | DependentFilter): void { |
There was a problem hiding this comment.
I could not use the helpers from https://github.com/vivid-planet/comet/blob/99950fa9d1a1b62e48722808f89483006f824046/packages/api/cms-api/src/common/filter/mikro-orm.ts because block_index_dependencies isn't an actual entity and we need to join it on EntityInfo. Thus, the filter and sort logic is basically duplicated
There was a problem hiding this comment.
if you create a entity class for block_index_dependencies (similar to EntityInfoObject) and query that instead of using query builder, we could use the helpers.
I just noticed here we join EntityInfo (on block_index_dependencies), but block_index_dependencies (the view) does already join EntityInfo for entityVisible: we could add name & secondaryInformation to block_index_dependencies and would not have to join again. Or is there a reason, like it updates faster as it is not materialized?
| private getBaseQueryBuilder( | ||
| internalFilter: { targetEntityName?: string; rootEntityName?: string; targetId?: string; rootId?: string }, |
There was a problem hiding this comment.
I'm not 100% certain if this split into internalFilter and other filters (in L316) is a good idea. We could maybe combine this. What do you think?
There was a problem hiding this comment.
IMO getBaseQueryBuilder, applyFilter, and applySort could be implemented in a single getQueryBuilderWithFilters (or getQueryBuilderWithFiltersAndSort) as before.
| function getDisplayNameString(displayName: ReactNode, fallback: string): string { | ||
| if (typeof displayName === "string") return displayName; | ||
| if (isValidElement(displayName)) { | ||
| const { defaultMessage } = displayName.props as { defaultMessage?: string }; | ||
| if (typeof defaultMessage === "string") return defaultMessage; | ||
| } | ||
| return fallback; | ||
| } |
There was a problem hiding this comment.
This is a bit of a workaround because displayName is a ReactNode. I thought about changing it to a string, but the displayName field is shared with DocumentInterface so I didn't want to touch it. Also this is defined in Page.tsx outside a react component, so intl.formatMessage() isn't a suitable alternative either.
There was a problem hiding this comment.
The implementation isn't correct because it would always use the default message, not the translated one. Instead I'd implement it like we do here:
comet/packages/admin/cms-admin/src/blocks/common/AddBlockDrawer.tsx
Lines 50 to 56 in 71ce212
displayName will always be either
string or <FormattedMessage>.
|
|
||
| Users can now filter dependencies/dependents by name, type, secondary information, and visibility, and sort by all columns. A default filter shows only visible items. The `GqlFilter` type is now exported from `@comet/admin`. | ||
|
|
||
| **Breaking changes:** |
There was a problem hiding this comment.
I deliberately didn't add these changes to the migration guide since I'm 99% sure these apis aren't used in any real project so it would just unnecessarily prolong the guide. But maybe it doesn't matter since the primary target are AI agents.
What do you think, should I add it?
d03d93d to
e747a7c
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds server-side filtering and sorting support to the Dependencies/Dependents lists end-to-end (GraphQL schema + API query handling + admin UI grids), including updated docs and demo queries.
Changes:
- Extend the CMS API GraphQL inputs/args to support
filter(with nestedStringFilter/BooleanFilter) andsort(DependencySort). - Implement filter/sort application in
DependenciesServiceand passsortthrough the resolvers. - Update admin
DependenciesList/DependentsListto enable DataGrid filtering/sorting and forward$filter/$sortvariables; update docs/demo queries accordingly.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/api/cms-api/src/dependencies/dto/dependency-sort.ts | Introduces GraphQL input + enum for dependency sorting. |
| packages/api/cms-api/src/dependencies/dto/dependencies.filter.ts | Expands dependency/dependent filters to nested string/boolean filters plus and/or. |
| packages/api/cms-api/src/dependencies/dto/dependencies.args.ts | Adds sort argument to dependencies/dependents GraphQL args. |
| packages/api/cms-api/src/dependencies/dependents.resolver.factory.ts | Passes sort through to service for dependents. |
| packages/api/cms-api/src/dependencies/dependencies.service.ts | Implements Knex-based filter/sort application and ensures count query clears ordering. |
| packages/api/cms-api/src/dependencies/dependencies.resolver.factory.ts | Passes sort through to service for dependencies. |
| packages/api/cms-api/schema.gql | Updates public schema for new filter/sort inputs and args; rehomes shared filter/sort types. |
| packages/admin/cms-admin/src/dependencies/DependentsList.tsx | Enables grid filter/sort UI and forwards filter/sort vars to GraphQL query. |
| packages/admin/cms-admin/src/dependencies/DependenciesList.tsx | Enables grid filter/sort UI and forwards filter/sort vars to GraphQL query. |
| packages/admin/cms-admin/src/dam/FileForm/EditFile.gql.ts | Updates dependents query to accept/forward $filter/$sort and request visible. |
| packages/admin/admin/src/index.ts | Exports GqlFilter type from muiGridFilterToGql. |
| docs/docs/2-core-concepts/7-dependencies/index.md | Updates documentation example queries for new filter/sort variables and visible. |
| demo/api/schema.gql | Updates demo schema to include new filter/sort capabilities. |
| demo/admin/src/documents/pages/EditPage.tsx | Updates demo page queries to accept/forward $filter/$sort and request visible. |
| .changeset/deps-list-filter-sort.md | Declares version bumps and documents breaking changes + required query updates. |
| this.applyStringFilterToKnex(qb, '"EntityInfo"."secondaryInformation"', filter.secondaryInformation); | ||
| } | ||
| if (filter.visible) { | ||
| if (filter.visible.equal !== undefined) { |
| if (filter.contains !== undefined) { | ||
| qb.andWhere(knex.raw(`${column} ILIKE ?`, [`%${filter.contains}%`])); | ||
| } | ||
| if (filter.notContains !== undefined) { | ||
| qb.andWhere(knex.raw(`${column} NOT ILIKE ?`, [`%${filter.notContains}%`])); | ||
| } | ||
| if (filter.startsWith !== undefined) { | ||
| qb.andWhere(knex.raw(`${column} ILIKE ?`, [`${filter.startsWith}%`])); | ||
| } | ||
| if (filter.endsWith !== undefined) { | ||
| qb.andWhere(knex.raw(`${column} ILIKE ?`, [`%${filter.endsWith}`])); | ||
| } | ||
| if (filter.equal !== undefined) { | ||
| qb.andWhere(knex.raw(`${column} = ?`, [filter.equal])); | ||
| } | ||
| if (filter.notEqual !== undefined) { | ||
| qb.andWhere(knex.raw(`${column} != ?`, [filter.notEqual])); | ||
| } | ||
| if (filter.isAnyOf !== undefined && filter.isAnyOf.length > 0) { |
| if (filter.and) { | ||
| for (const subFilter of filter.and) { | ||
| qb.andWhere((sub) => { | ||
| this.applyFilter(sub, subFilter); | ||
| }); | ||
| } | ||
| } | ||
| if (filter.or) { | ||
| const orFilters = filter.or; | ||
| qb.andWhere((outer) => { | ||
| for (const subFilter of orFilters) { | ||
| outer.orWhere((sub) => { | ||
| this.applyFilter(sub, subFilter); | ||
| }); | ||
| } | ||
| }); | ||
| } |
DependenciesList and DependentsListDependenciesList and DependentsList
There was a problem hiding this comment.
Pull request overview
Adds server- and client-side filtering/sorting for the Dependencies/Dependents lists, updating the CMS GraphQL API inputs and the admin UI data grids accordingly.
Changes:
- Extend GraphQL API for
dependencies/dependentswithfilter(nestedStringFilter/BooleanFilter+and/or) andsort(DependencySort). - Update
DependenciesServiceto apply new filter + sort options against theblock_index_dependenciesview andEntityInfojoin. - Update admin
DependenciesList/DependentsListto use DataGrid filter/sort UI and pass$filter/$sortvariables; update docs/demo queries and schemas.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/api/cms-api/src/dependencies/dto/dependency-sort.ts | Introduces DependencySort + DependencySortField GraphQL input/enum. |
| packages/api/cms-api/src/dependencies/dto/dependencies.filter.ts | Expands filter inputs to nested StringFilter/BooleanFilter and and/or. |
| packages/api/cms-api/src/dependencies/dto/dependencies.args.ts | Adds sort argument to dependencies/dependents field args. |
| packages/api/cms-api/src/dependencies/dependents.resolver.factory.ts | Wires sort through to service. |
| packages/api/cms-api/src/dependencies/dependencies.resolver.factory.ts | Wires sort through to service. |
| packages/api/cms-api/src/dependencies/dependencies.service.ts | Implements Knex-side filter/sort application for dependencies/dependents queries. |
| packages/api/cms-api/schema.gql | Updates generated schema to expose new filter/sort inputs and visible field usage. |
| packages/admin/cms-admin/src/dependencies/DependentsList.tsx | Adds DataGrid filter/sort UI and forwards GQL filter/sort variables. |
| packages/admin/cms-admin/src/dependencies/DependenciesList.tsx | Adds DataGrid filter/sort UI and forwards GQL filter/sort variables. |
| packages/admin/cms-admin/src/dam/FileForm/EditFile.gql.ts | Updates dependents query to accept and forward $filter/$sort; includes visible. |
| packages/admin/admin/src/index.ts | Re-exports GqlFilter type from @comet/admin. |
| docs/docs/2-core-concepts/7-dependencies/index.md | Updates documentation query example to include $filter/$sort and visible. |
| demo/api/schema.gql | Updates demo schema for new filter/sort inputs. |
| demo/admin/src/documents/pages/EditPage.tsx | Updates demo queries to include $filter/$sort and request visible. |
| .changeset/deps-list-filter-sort.md | Declares version bumps and documents breaking API/query changes. |
| function getDisplayNameString(displayName: ReactNode, fallback: string): string { | ||
| if (typeof displayName === "string") return displayName; | ||
| if (isValidElement(displayName)) { | ||
| const { defaultMessage } = displayName.props as { defaultMessage?: string }; | ||
| if (typeof defaultMessage === "string") return defaultMessage; | ||
| } | ||
| return fallback; | ||
| } |
| for (const subFilter of filter.and) { | ||
| qb.andWhere((sub) => { | ||
| this.applyFilter(sub, subFilter); | ||
| }); | ||
| } | ||
| } | ||
| if (filter.or?.length) { | ||
| const orFilters = filter.or; | ||
| qb.andWhere((outer) => { | ||
| for (const subFilter of orFilters) { | ||
| outer.orWhere((sub) => { | ||
| this.applyFilter(sub, subFilter); | ||
| }); | ||
| } | ||
| }); |
| private applyStringFilterToKnex(qb: Knex.QueryBuilder, column: string, filter: StringFilter): void { | ||
| const knex = this.entityManager.getKnex("read"); | ||
|
|
||
| if (filter?.rootColumnName) { | ||
| qb.andWhere({ rootColumnName: filter.rootColumnName }); | ||
| if (filter.contains !== undefined && filter.contains !== null) { | ||
| qb.andWhere(knex.raw(`${column} ILIKE ?`, [`%${filter.contains}%`])); | ||
| } | ||
| if (filter.notContains !== undefined && filter.notContains !== null) { | ||
| qb.andWhere(knex.raw(`${column} NOT ILIKE ?`, [`%${filter.notContains}%`])); | ||
| } | ||
| if (filter.startsWith !== undefined && filter.startsWith !== null) { | ||
| qb.andWhere(knex.raw(`${column} ILIKE ?`, [`${filter.startsWith}%`])); | ||
| } | ||
| if (filter.endsWith !== undefined && filter.endsWith !== null) { | ||
| qb.andWhere(knex.raw(`${column} ILIKE ?`, [`%${filter.endsWith}`])); | ||
| } |
| sortBy: "visible", | ||
| toGqlFilter: (filterItem) => { | ||
| if (filterItem.value === undefined || filterItem.value === "") return {}; | ||
| return { visible: { equal: filterItem.value === "true" || filterItem.value === true } } as unknown as GqlFilter; |
| sortBy: "visible", | ||
| toGqlFilter: (filterItem) => { | ||
| if (filterItem.value === undefined || filterItem.value === "") return {}; | ||
| return { visible: { equal: filterItem.value === "true" || filterItem.value === true } } as unknown as GqlFilter; |
| function getDisplayNameString(displayName: ReactNode, fallback: string): string { | ||
| if (typeof displayName === "string") return displayName; | ||
| if (isValidElement(displayName)) { | ||
| const { defaultMessage } = displayName.props as { defaultMessage?: string }; | ||
| if (typeof defaultMessage === "string") return defaultMessage; | ||
| } | ||
| return fallback; | ||
| } |
| }; | ||
|
|
||
| return ( | ||
| <div style={{ display: "flex" }}> |
There was a problem hiding this comment.
Instead of inline styles, could we use MUI's Box?
| @Field(() => [DependencySort], { nullable: true }) | ||
| @ValidateNested({ each: true }) | ||
| @Type(() => DependencySort) | ||
| @IsOptional() |
There was a problem hiding this comment.
Could we use IsUndefinable (and maybe IsNullable)?
| }, | ||
| paginationArgs?: { offset: number; limit: number }, | ||
| options?: { forceRefresh: boolean }, | ||
| options?: { forceRefresh: boolean; sort?: DependencySort[] }, |
There was a problem hiding this comment.
It's weird that filter and pagination are passed as root args, while sort is passed via options.
Add filter to
DependenciesListandDependentsList:filter.mp4
Jira: https://vivid-planet.atlassian.net/browse/PHSB2C-6541