Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions frontend/common/services/useFeatureState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Req } from 'common/types/requests'
import { service } from 'common/service'
import Utils from 'common/utils/utils'
import { getFeatureSegment } from './useFeatureSegment'
import { recursivePageGet } from 'common/utils/recursivePageGet'
import { getStore } from 'common/store'
export const addFeatureSegmentsToFeatureStates = async (v) => {
if (typeof v.feature_segment !== 'number') {
Expand All @@ -22,6 +23,18 @@ export const featureStateService = service
})
.injectEndpoints({
endpoints: (builder) => ({
getAllEnvironmentFeatureStates: builder.query<
Res['featureStates'],
Req['getAllEnvironmentFeatureStates']
>({
providesTags: [{ id: 'LIST', type: 'FeatureState' }],
queryFn: async (query, _baseQueryApi, _extraOptions, baseQuery) =>
recursivePageGet<FeatureState>(
`environments/${query.environmentKey}/featurestates/?page_size=999`,
null,
baseQuery,
),
}),
Comment on lines +26 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

ast-grep outline frontend/common/services/useFeatureState.ts --view expanded

Repository: Flagsmith/flagsmith

Length of output: 291


🏁 Script executed:

sed -n '1,180p' frontend/common/services/useFeatureState.ts

Repository: Flagsmith/flagsmith

Length of output: 3609


🏁 Script executed:

rg -n "type FeatureState|interface FeatureState|featureStates|getAllEnvironmentFeatureStates|addFeatureSegmentsToFeatureStates|feature_segment" frontend/common/types frontend/common -g '!**/dist/**'

Repository: Flagsmith/flagsmith

Length of output: 11938


🏁 Script executed:

sed -n '760,810p' frontend/common/types/responses.ts && printf '\n---\n' && sed -n '750,790p' frontend/common/types/requests.ts && printf '\n---\n' && rg -n "useGetAllEnvironmentFeatureStatesQuery|getAllEnvironmentFeatureStates" frontend/common -g '!**/dist/**'

Repository: Flagsmith/flagsmith

Length of output: 2975


🏁 Script executed:

rg -n "environments/.*/featurestates|featurestates/\?page_size=999|getAllEnvironmentFeatureStatesQuery|useGetAllEnvironmentFeatureStatesQuery" . -g '!**/dist/**' -g '!**/node_modules/**'

Repository: Flagsmith/flagsmith

Length of output: 4003


🏁 Script executed:

sed -n '1,180p' frontend/web/components/CompareEnvironments/useEnvironmentComparison.ts && printf '\n---\n' && rg -n "feature_segment.*Serializer|FeatureState.*Serializer|featurestates" api -g '!**/migrations/**' -g '!**/node_modules/**'

Repository: Flagsmith/flagsmith

Length of output: 39867


🏁 Script executed:

sed -n '1,220p' api/environments/serializers.py && printf '\n---\n' && sed -n '1,240p' api/environments/views.py && printf '\n---\n' && sed -n '1,220p' api/features/serializers.py

Repository: Flagsmith/flagsmith

Length of output: 25232


🏁 Script executed:

rg -n "environment-featurestates|featurestates-list|FeatureStateSerializerFull|FeatureStateSerializerBasic|IdentityAllFeatureStatesSerializer" api/environments api/features -g '!**/migrations/**'

Repository: Flagsmith/flagsmith

Length of output: 2077


🏁 Script executed:

sed -n '1,220p' api/environments/urls.py && printf '\n---\n' && sed -n '1,260p' api/features/views.py

Repository: Flagsmith/flagsmith

Length of output: 16009


🏁 Script executed:

sed -n '620,760p' api/features/views.py && printf '\n---\n' && sed -n '540,640p' api/features/serializers.py

Repository: Flagsmith/flagsmith

Length of output: 8873


🏁 Script executed:

sed -n '660,840p' api/features/serializers.py && printf '\n---\n' && sed -n '1,220p' api/features/features_service.py

Repository: Flagsmith/flagsmith

Length of output: 11729


getAllEnvironmentFeatureStates returns a different feature_segment shape than FeatureState expects. Either resolve segment refs here like getFeatureStates does, or narrow the endpoint type to the raw environment-feature-state shape so consumers don’t assume the nested object is present.

getFeatureStates: builder.query<
Res['featureStates'],
Req['getFeatureStates']
Expand Down Expand Up @@ -92,5 +105,8 @@ export async function updateFeatureState(
)
}

export const { useGetFeatureStatesQuery, useUpdateFeatureStateMutation } =
featureStateService
export const {
useGetAllEnvironmentFeatureStatesQuery,
useGetFeatureStatesQuery,
useUpdateFeatureStateMutation,
} = featureStateService
30 changes: 3 additions & 27 deletions frontend/common/services/useProjectFlag.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
import { PagedResponse, ProjectFlag, Res } from 'common/types/responses'
import { ProjectFlag, Res } from 'common/types/responses'
import { Req } from 'common/types/requests'
import { service } from 'common/service'
import Utils from 'common/utils/utils'
import { sortMultivariateOptions } from 'common/utils/multivariate'
import { recursivePageGet } from 'common/utils/recursivePageGet'

/**
* Number of features to display per page in the features list.
*/
export const FEATURES_PAGE_SIZE = 50

function recursivePageGet(
url: string,
parentRes: null | PagedResponse<ProjectFlag>,
baseQuery: (arg: unknown) => any, // matches rtk types,
) {
return baseQuery({
method: 'GET',
url,
}).then((res: Res['projectFlags']) => {
let response
if (parentRes) {
response = {
...parentRes,
results: parentRes.results.concat(res.results),
}
} else {
response = res
}
if (res.next) {
return recursivePageGet(res.next, response, baseQuery)
}
return Promise.resolve(response)
})
}
export const projectFlagService = service
.enhanceEndpoints({
addTagTypes: ['ProjectFlag', 'FeatureList', 'FeatureState', 'Environment'],
Expand Down Expand Up @@ -158,7 +134,7 @@ export const projectFlagService = service
},
],
queryFn: async (args, _, _2, baseQuery) => {
return await recursivePageGet(
return await recursivePageGet<ProjectFlag>(
`projects/${args.project}/features/?${Utils.toParam({
...args,
page_size: 999,
Expand Down
3 changes: 3 additions & 0 deletions frontend/common/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,9 @@ export type Req = {
environment?: number
feature?: number
}
getAllEnvironmentFeatureStates: {
environmentKey: string
}
getFeatureSegment: { id: number }
getSamlConfiguration: { name: string }
getSamlConfigurations: { organisation_id: number }
Expand Down
28 changes: 28 additions & 0 deletions frontend/common/utils/recursivePageGet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PagedResponse } from 'common/types/responses'

// Fetches every page of a paginated endpoint through an RTK Query baseQuery,
// following `next` links and concatenating `results`. Returns the queryFn
// contract: `{ data }` with the merged response, or `{ error }` on failure.
export function recursivePageGet<T>(
url: string,
parentRes: null | PagedResponse<T>,
baseQuery: (
arg: unknown,
) => MaybePromise<QueryReturnValue<PagedResponse<T>, unknown>>,
): Promise<QueryReturnValue<PagedResponse<T>, unknown>> {
return baseQuery({
method: 'GET',
url,
}).then((res: { data?: PagedResponse<T>; error?: any }) => {
if (res.error || !res.data) {
return res
}
const response = parentRes
? { ...res.data, results: parentRes.results.concat(res.data.results) }
: res.data
if (response.next) {
return recursivePageGet(response.next, response, baseQuery)
}
return { data: response }
})
}
Loading
Loading