We could have users updated at the exact same time, so we need to
include that timestamp in the next fetch to avoid missing users, but
also include the latest user id so we can exclude the ones already
fetched.
The following test shows how to process pages with this new method:
c03df86ee0/src/lib/features/users/user-updates-read-model.test.ts (L39-L65)
## About the changes
In our code, we're not expecting constraints to be null:
https://github.com/search?q=repo%3AUnleash%2Funleash%20jsonb_array_elements(constraints)&type=code
It's unlikely to get a null value in constraints when using our API or
UI, but there might be cases where this can happen, as we saw in our
logs:
```
error: select "context_fields"."name", "context_fields"."description", "context_fields"."stickiness", "context_fields"."sort_order", "context_fields"."legal_values", "context_fields"."created_at", COUNT(DISTINCT CASE
WHEN features.archived_at IS NULL
THEN feature_strategies.project_name
END) AS used_in_projects, COUNT(DISTINCT CASE
WHEN features.archived_at IS NULL
THEN feature_strategies.feature_name
END) AS used_in_features from "context_fields" LEFT JOIN feature_strategies ON EXISTS (
SELECT 1
FROM jsonb_array_elements(feature_strategies.constraints) AS elem
WHERE elem ->> 'contextName' = context_fields.name
) left join "features" on "features"."name" = "feature_strategies"."feature_name" group by "context_fields"."name", "context_fields"."description", "context_fields"."stickiness", "context_fields"."sort_order", "context_fields"."created_at" order by "name" asc - cannot extract elements from a scalar
```
which is likely due to:
`jsonb_array_elements(feature_strategies.constraints)` with null
constraints
For this reason, it seems reasonable to enforce the constraint at the
database level.
Archived flags link will now it will show up on the right side, next to
import/export, which makes it more in line with flags overview.
Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This PR cleans up the etagVariant flag. These changes were automatically
generated by AI and should be reviewed carefully.
Fixes#10711
## 🧹 AI Flag Cleanup Summary
This PR removes the `etagVariant` feature flag, making the versioned
ETag format
(`v2`) the default and only behavior for the client features API.
### 🚮 Removed
- **Feature Flag**
- Removed the `etagVariant` flag definition from `experimental.ts`.
- Removed conditional logic for ETag generation in
`client-feature-toggle.controller.ts`.
- **Testing**
- Removed parameterized tests for both states of the flag in
`feature.optimal304.e2e.test.ts`.
- Removed configuration of the `etagVariant` flag in test setup.
### 🛠 Kept
- **ETag Generation**
- The logic to generate ETags with a version suffix (`v1`) is now the
standard
behavior.
- **Testing**
- Tests have been updated to exclusively assert the presence of the `v1`
suffix in ETags.
### 📝 Why
The `etagVariant` feature flag has been successfully rolled out and is
now
considered complete. By removing the flag, we are simplifying the
codebase by
eliminating conditional paths and making the improved ETag format
permanent.
This change ensures all client API responses for features include a
versioned
ETag, which helps with cache-busting when the ETag format changes in the
future.
---------
Co-authored-by: unleash-bot <194219037+unleash-bot[bot]@users.noreply.github.com>
Co-authored-by: Gastón Fournier <gaston@getunleash.io>
Refactored and simplified code around flag filters, in preparation for
UI improvements. It's split into 2 PRs in order to simplify what needs
to be behind a flag and what doesn't.
- `ExperimentalColumnsMenu` moved to `ColumnsMenu`, old unused
`ColumnsMenu` removed
- Parts of the code moved to `ProjectFeaturesColumnsMenu`
- Moved `FlagCreationButton` to a separate file
- Removed part behind archived flag (`projectOverviewRefactorFeedback`)
Adds filter buttons for filtering between "CRs created by me" and "CRs
where I've been requested as an approver".
The current implementation is fairly simplistic and the buttons are not
connected to the actual table state directly (instead being set up with
their own simple state and onChange hooks), but it covers the simple
scenario. I want to defer a more complex solution until we know we need
it and until we know exactly what we need. The implementation is based
on the lifecycle filters that we have on the project flags page.
The current logic is such that: when you land on the page, there's no
query params in the URL, but the data fetch applies `createdBy:IS<your
user>`. If you switch to "approval requested" (and back again), the URL
will reflect this.
For reference, the github workflow works like this, where each URL has a
set of default filters, e.g.:
- `/pulls`: `is:open is:pr assignee:thomasheartman archived:false`
- `/pulls/review-requested`: `is:open is:pr
review-requested:thomasheartman archived:false`
But if you change the default filters or add new ones, the URL will
update to `pulls?<query-string>` (e.g.
`/pulls?q=is%3Aopen+is%3Apr+review-requested%3Athomasheartman+archived%3Atrue`)
So this takes a similar approach, but better suited to the way we do
tables in general.
Rendered:
<img width="1816" height="791" alt="image"
src="https://github.com/user-attachments/assets/60935900-488d-4ca9-b110-39f3568a08a6"
/>
<img width="1855" height="329" alt="image"
src="https://github.com/user-attachments/assets/5e865a2e-8fdc-41ab-ba38-bbe6776d04ad"
/>
With three and four different parameters (of which two are strings that
are easily interchanged), it makes sense to rewrite these two functions
to take named parameters instead. This is a follow-up to
https://github.com/Unleash/unleash/pull/10689 based on one of the review
comments.
Fixes an issue where the project feature list (and potentially other
places in the app that use the `useClearSWRCache` hook) would end up in
an infinite loading screen because the latest entry that we want to show
was overwritten.
The primary reason this happened is that we used `keysToDelete =
array.slice(SWR_CACHE_SIZE - 1)`. Because the map keys are returned in
insertion order, this would make us never delete the oldest keys, but
always anything after the cache reached it's maximum size. The fix was
to instead do `slice(0, -(SWR_CACHE_SIZE - 1))`, unless that is `0, 0`.
If so, then just delete the entire filtered keys set.
As a bonus: this PR also deduplicates cache entries that have the same
query params but in different order for the feature search. This further
reduces the cache space needed.
https://linear.app/unleash/issue/2-3897/limit-custom-strategies-like-were-doing-with-release-templates
Limits total custom strategies displayed, like we're doing for release
templates, in the new "add strategy" modal.
Added a more generic logic to `FeatureStrategyMenuCardsSection.tsx` so
we can reuse it for both.
We can also do it for other sections in the modal, but that feels like a
premature optimization. These 2 categories are the ones that are user
owned, and can have many items.
## About the changes
When deleting a user we set the email to null and deleted_at to the
current date, there's no case where email is set and deleted_at is also
set.
We found some situations where this happens, specifically when SAML and
SCIM are used in conjunction
Adds a paginated table to the change request overview page and
integrates it with the search API hook.
The current implementation still has some rough edges to work out, but
it's getting closer.
There's no sort buttons in this implementation. I've got it working on
the side, but TS is complaining about types not matching up, so I'm
spinning that out to a separate PR.
<img width="1808" height="1400" alt="image"
src="https://github.com/user-attachments/assets/bdee97b7-ee2a-46c0-8460-a8b8e14d3c92"
/>
## About the changes
Having SCIM enabled with SAML and auto-create can generate issues with
each protocol stepping into the other protocol's toes.
This PR adds protection to avoid updating SCIM-managed users with SAML
data (cause SCIM will override this data later).
It also adds a new method in the store to check if we have cases where
deleted_at is set but the email is not cleared, and there's no delete
event in the audit log (we've found one case, and we believe it might be
related to interoperability issues between SAML and SCIM)
https://linear.app/unleash/issue/2-3878/persist-strategy-filter-when-going-back-from-the-release-template
Persists the strategy filter in the new "add strategy" modal when going
back from the release template preview modal.
This is done by moving the filter to the parent component, so the filter
state persists across these navigations.
Also updates the button text in the release templates preview to say
"Apply template" for consistency, but only if the `newStrategyModal`
flag is enabled.