You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
__after / __before filtering for attribute fields of kind DateTime doesn't work end-to-end on either 1.8.x or 1.9.x. The frontend UI for it was removed in 1.9 (PR #8793), but the GraphQL schema never exposed those filter inputs for attribute fields in the first place. Only node_metadata__created_at / node_metadata__updated_at get range filtering today.
We'd like to bring before / after / between back as first-class filters on any DateTime attribute (not just metadata), with the same picker UX 1.9 introduced for metadata.
State in 1.8.x
AttributeFilterForm rendered an inline DateRangeFilterForm with two DatePicker inputs when attributeSchema.kind === "DateTime".
Selecting a date wrote <attr>__after=<ISO> / <attr>__before=<ISO> to the URL via useFilters.
But the round-trip was broken:
addFiltersToRequest in frontend/app/src/shared/api/graphql/utils.ts had a switch on the filter suffix with cases for value/values/isnull/ids only — no before/after cases. Filters with those suffixes were silently dropped before the GraphQL request.
Even if (1) had been fixed, the GraphQL schema never declared <attr>__after / <attr>__before as input arguments for DateTime attributes. InfrahubDataType.get_graphql_filters in backend/infrahub/types.py emits only __value/__values/__isnull/property filters, regardless of underlying scalar type.
Even if (2) had been fixed, the Cypher attribute filter path (default_attribute_query_filter in backend/infrahub/core/query/attribute.py) rejects datetime values at its type guard and has no branch for before/after filter names — they fall through with no WHERE clause emitted.
Net effect on 1.8.x: pick a date, the URL updates, the chip shows, but the table never filters.
What changed in 1.9.x
Two distinct PRs touched this area:
PR Update filters UI and add metadata filters #8793 ("Update filters UI and add node metadata filtering") restructured the filter UI: introduced new metadata-date and metadata-user filter definitions, added a react-datepicker-based DateMetadataFilterForm, added a BETWEEN condition, and removedBEFORE / AFTER from DATETIME_FILTER_CONDITION_OPTIONS. After this change, attribute DateTime fields can only be filtered by is empty / is not empty. The before/after capability now exists only on the two built-in audit timestamps node_metadata__created_at and node_metadata__updated_at.
PR support timezones in graphql filters #8815 ("support timezones in graphql filters") fixed the metadata filter path to accept non-UTC offsets in input values by switching Timestamp(ZonedDateTime.from_py_datetime(value)) to Timestamp(value.isoformat()).
The underlying frontend addFiltersToRequest was also fixed in 1.9 (it now switches on parts.at(-1) instead of destructuring the first two segments, and adds explicit before / after cases). That alone makes URL-level __after / __before filters reach the GraphQL request — but they still hit a schema with no such input declared for non-metadata attributes.
What we want in 1.10+
For every node attribute of kind DateTime, the same filter UX that 1.9 added for metadata:
before, after, between conditions in the filter picker (alongside is empty / is not empty).
Same DateTimePicker (inline calendar + time-of-day, 1-min intervals).
URL keys <attr>__after=<ISO> and <attr>__before=<ISO>.
Backend GraphQL schema exposes <attr>__after: DateTime / <attr>__before: DateTime input arguments on every node kind that has a DateTime attribute.
Cypher path emits WHERE av.value > $param (for after) / < $param (for before), with the filter value normalized to canonical UTC via Timestamp(value.isoformat()).to_string() — mirroring how _build_metadata_filter_requirement already handles metadata filters.
Proposed implementation sketch
Three layers, none of them large:
Backend GraphQL schema — override get_graphql_filters on the DateTime data type in backend/infrahub/types.py to also emit <name>__after and <name>__before: graphene.DateTime. Calls super() so existing __value / __values / __isnull are preserved.
Backend Cypher path — in default_attribute_query_filter (backend/infrahub/core/query/attribute.py):
Relax the type guard at the top to accept datetime in addition to str | bool | int | list.
Add a before / after branch (alongside the existing value/values/isnull branches) that matches the value node, emits WHERE av.value > $param (or <), and normalizes datetime filter values to canonical UTC strings.
Frontend UI — extend DATETIME_FILTER_CONDITION_OPTIONS with BEFORE / AFTER / BETWEEN, extract the existing metadata picker block (DateMetadataFilterForm) into a shared DateRangePickerFields component, and route AttributeFilterForm to it when attributeSchema.kind === "DateTime". The 1.9 addFiltersToRequest already handles the URL→variable mapping correctly; no change needed there. Existing getCurrentFilterCondition already maps __after / __before suffixes to AFTER / BEFORE; existing chip rendering in active-filter-tags.tsx already handles those suffixes for non-relationship definitions.
Caveat: stored value timezone canonicalization
DateTime attribute values are user-supplied strings stored verbatim — BaseAttribute.serialize_value() doesn't normalize to UTC at write time. Lexicographic Cypher comparison (< / >) is only correct when stored values are also in canonical form. In practice values written via the standard graphene DateTime ingestion path are canonical, but values written via direct DB writes or non-standard paths could produce incorrect range results. This matches an existing limitation in the metadata filtering path and is worth documenting; a separate hardening effort could either (a) normalize DateTime attribute values at write time, or (b) cast both sides via Cypher datetime() in the WHERE clause.
Reproducing the 1.8.x silent-drop bug
On 1.8.6:
Find any node kind with a DateTime attribute (e.g., CoreValidator.completed_at).
Open its list page in the UI.
Filter picker → select the DateTime attribute → pick "after" + a date → Apply.
Summary
__after/__beforefiltering for attribute fields of kindDateTimedoesn't work end-to-end on either 1.8.x or 1.9.x. The frontend UI for it was removed in 1.9 (PR #8793), but the GraphQL schema never exposed those filter inputs for attribute fields in the first place. Onlynode_metadata__created_at/node_metadata__updated_atget range filtering today.We'd like to bring
before/after/betweenback as first-class filters on any DateTime attribute (not just metadata), with the same picker UX 1.9 introduced for metadata.State in 1.8.x
AttributeFilterFormrendered an inlineDateRangeFilterFormwith twoDatePickerinputs whenattributeSchema.kind === "DateTime".<attr>__after=<ISO>/<attr>__before=<ISO>to the URL viauseFilters.addFiltersToRequestinfrontend/app/src/shared/api/graphql/utils.tshad aswitchon the filter suffix with cases forvalue/values/isnull/idsonly — nobefore/aftercases. Filters with those suffixes were silently dropped before the GraphQL request.<attr>__after/<attr>__beforeas input arguments for DateTime attributes.InfrahubDataType.get_graphql_filtersinbackend/infrahub/types.pyemits only__value/__values/__isnull/property filters, regardless of underlying scalar type.default_attribute_query_filterinbackend/infrahub/core/query/attribute.py) rejectsdatetimevalues at its type guard and has no branch forbefore/afterfilter names — they fall through with no WHERE clause emitted.Net effect on 1.8.x: pick a date, the URL updates, the chip shows, but the table never filters.
What changed in 1.9.x
Two distinct PRs touched this area:
metadata-dateandmetadata-userfilter definitions, added areact-datepicker-basedDateMetadataFilterForm, added aBETWEENcondition, and removedBEFORE/AFTERfromDATETIME_FILTER_CONDITION_OPTIONS. After this change, attribute DateTime fields can only be filtered byis empty/is not empty. The before/after capability now exists only on the two built-in audit timestampsnode_metadata__created_atandnode_metadata__updated_at.Timestamp(ZonedDateTime.from_py_datetime(value))toTimestamp(value.isoformat()).The underlying frontend
addFiltersToRequestwas also fixed in 1.9 (it now switches onparts.at(-1)instead of destructuring the first two segments, and adds explicitbefore/aftercases). That alone makes URL-level__after/__beforefilters reach the GraphQL request — but they still hit a schema with no such input declared for non-metadata attributes.What we want in 1.10+
For every node attribute of kind
DateTime, the same filter UX that 1.9 added for metadata:before,after,betweenconditions in the filter picker (alongsideis empty/is not empty).DateTimePicker(inline calendar + time-of-day, 1-min intervals).<attr>__after=<ISO>and<attr>__before=<ISO>.<attr>__after: DateTime/<attr>__before: DateTimeinput arguments on every node kind that has a DateTime attribute.WHERE av.value > $param(for after) /< $param(for before), with the filter value normalized to canonical UTC viaTimestamp(value.isoformat()).to_string()— mirroring how_build_metadata_filter_requirementalready handles metadata filters.Proposed implementation sketch
Three layers, none of them large:
get_graphql_filterson theDateTimedata type inbackend/infrahub/types.pyto also emit<name>__afterand<name>__before: graphene.DateTime. Callssuper()so existing__value/__values/__isnullare preserved.default_attribute_query_filter(backend/infrahub/core/query/attribute.py):datetimein addition tostr | bool | int | list.before/afterbranch (alongside the existingvalue/values/isnullbranches) that matches the value node, emitsWHERE av.value > $param(or<), and normalizes datetime filter values to canonical UTC strings.DATETIME_FILTER_CONDITION_OPTIONSwithBEFORE/AFTER/BETWEEN, extract the existing metadata picker block (DateMetadataFilterForm) into a sharedDateRangePickerFieldscomponent, and routeAttributeFilterFormto it whenattributeSchema.kind === "DateTime". The 1.9addFiltersToRequestalready handles the URL→variable mapping correctly; no change needed there. ExistinggetCurrentFilterConditionalready maps__after/__beforesuffixes toAFTER/BEFORE; existing chip rendering inactive-filter-tags.tsxalready handles those suffixes for non-relationship definitions.Caveat: stored value timezone canonicalization
DateTime attribute values are user-supplied strings stored verbatim —
BaseAttribute.serialize_value()doesn't normalize to UTC at write time. Lexicographic Cypher comparison (</>) is only correct when stored values are also in canonical form. In practice values written via the standard grapheneDateTimeingestion path are canonical, but values written via direct DB writes or non-standard paths could produce incorrect range results. This matches an existing limitation in the metadata filtering path and is worth documenting; a separate hardening effort could either (a) normalize DateTime attribute values at write time, or (b) cast both sides via Cypherdatetime()in the WHERE clause.Reproducing the 1.8.x silent-drop bug
On 1.8.6:
CoreValidator.completed_at).?completed_at__after=2026-01-01T00:00:00Z.completed_at__aftervariable.Out of scope
Number,Bandwidth, etc.). Same machinery would apply via per-data-type overrides ofget_graphql_filters.Happy to open a PR with the implementation if helpful — we have a working branch ready.