Skip to content

Commit

Permalink
feat: versioned segment override change request (#3790)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthew Elwell <[email protected]>
  • Loading branch information
kyle-ssg and matthewelwell authored May 9, 2024
1 parent 0c79870 commit cf320b7
Show file tree
Hide file tree
Showing 18 changed files with 544 additions and 203 deletions.
2 changes: 2 additions & 0 deletions frontend/common/dispatcher/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), {
segmentOverrides,
changeRequest,
commit,
mode,
) {
Dispatcher.handleViewAction({
actionType: Actions.EDIT_ENVIRONMENT_FLAG_CHANGE_REQUEST,
Expand All @@ -154,6 +155,7 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), {
environmentFlag,
environmentId,
flag,
mode,
projectFlag,
projectId,
segmentOverrides,
Expand Down
8 changes: 5 additions & 3 deletions frontend/common/providers/FeatureListProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const FeatureListProvider = class extends React.Component {
})
})

this.listenTo(FeatureListStore, 'saved', (isCreate) => {
this.props.onSave && this.props.onSave(isCreate)
this.listenTo(FeatureListStore, 'saved', (data) => {
this.props.onSave && this.props.onSave(data)
})

this.listenTo(FeatureListStore, 'problem', () => {
Expand Down Expand Up @@ -174,7 +174,7 @@ const FeatureListProvider = class extends React.Component {
}),
() => {
FeatureListStore.isSaving = false
FeatureListStore.trigger('saved')
FeatureListStore.trigger('saved', {})
FeatureListStore.trigger('change')
},
)
Expand All @@ -189,6 +189,7 @@ const FeatureListProvider = class extends React.Component {
segmentOverrides,
changeRequest,
commit,
mode,
) => {
AppActions.editFeatureMv(
projectId,
Expand Down Expand Up @@ -220,6 +221,7 @@ const FeatureListProvider = class extends React.Component {
segmentOverrides,
changeRequest,
commit,
mode,
)
},
)
Expand Down
21 changes: 21 additions & 0 deletions frontend/common/services/useFeatureSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ export const featureSegmentService = service
url: `features/feature-segments/${query.id}/`,
}),
}),
getFeatureSegment: builder.query<
Res['featureSegment'],
Req['getFeatureSegment']
>({
providesTags: (res) => [{ id: res?.id, type: 'FeatureSegment' }],
query: (query: Req['getFeatureSegment']) => ({
url: `features/feature-segments/${query.id}/`,
}),
}),
// END OF ENDPOINTS
}),
})
Expand All @@ -35,10 +44,22 @@ export async function deleteFeatureSegment(
),
)
}
export async function getFeatureSegment(
store: any,
data: Req['getFeatureSegment'],
options?: Parameters<
typeof featureSegmentService.endpoints.getFeatureSegment.initiate
>[1],
) {
return store.dispatch(
featureSegmentService.endpoints.getFeatureSegment.initiate(data, options),
)
}
// END OF FUNCTION_EXPORTS

export const {
useDeleteFeatureSegmentMutation,
useGetFeatureSegmentQuery,
// END OF EXPORTS
} = featureSegmentService

Expand Down
79 changes: 79 additions & 0 deletions frontend/common/services/useFeatureState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { FeatureState, PagedResponse, Res } from 'common/types/responses'
import { Req } from 'common/types/requests'
import { service } from 'common/service'
import Utils from 'common/utils/utils'
import { getFeatureSegment } from './useFeatureSegment'
import { getStore } from 'common/store'
const _data = require('../data/base/_data')
export const addFeatureSegmentsToFeatureStates = async (v) => {
if (!v.feature_segment) {
return v
}
const featureSegmentData = await getFeatureSegment(getStore(), {
id: v.feature_segment,
})
return {
...v,
feature_segment: featureSegmentData.data,
}
}
export const featureStateService = service
.enhanceEndpoints({ addTagTypes: ['FeatureState'] })
.injectEndpoints({
endpoints: (builder) => ({
getFeatureStates: builder.query<
Res['featureStates'],
Req['getFeatureStates']
>({
providesTags: [{ id: 'LIST', type: 'FeatureState' }],
queryFn: async (query, baseQueryApi, extraOptions, baseQuery) => {
//This endpoint returns feature_segments as a number, so it fetches the feature segments and appends
const {
data,
}: {
data: PagedResponse<
Omit<FeatureState, 'feature_segment'> & {
feature_segment: number | null
}
>
} = await baseQuery({
url: `features/featurestates/?${Utils.toParam(query)}`,
})
const results = await Promise.all(
data.results.map(addFeatureSegmentsToFeatureStates),
)
return {
data: {
...data,
results,
},
}
},
}),
// END OF ENDPOINTS
}),
})

export async function getFeatureStates(
store: any,
data: Req['getFeatureStates'],
options?: Parameters<
typeof featureStateService.endpoints.getFeatureStates.initiate
>[1],
) {
return store.dispatch(
featureStateService.endpoints.getFeatureStates.initiate(data, options),
)
}
// END OF FUNCTION_EXPORTS

export const {
useGetFeatureStatesQuery,
// END OF EXPORTS
} = featureStateService

/* Usage examples:
const { data, isLoading } = useGetFeatureStatesQuery({ id: 2 }, {}) //get hook
const [createFeatureStates, { isLoading, data, isSuccess }] = useCreateFeatureStatesMutation() //create hook
featureStateService.endpoints.getFeatureStates.select({id: 2})(store.getState()) //access data from any function
*/
29 changes: 26 additions & 3 deletions frontend/common/services/useFeatureVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { deleteFeatureSegment } from './useFeatureSegment'
import transformCorePaging from 'common/transformCorePaging'
import Utils from 'common/utils/utils'
import { updateSegmentPriorities } from './useSegmentPriority'

export const featureVersionService = service
.enhanceEndpoints({ addTagTypes: ['FeatureVersion'] })
Expand All @@ -27,16 +28,18 @@ export const featureVersionService = service
environmentId: query.environmentId,
featureId: query.featureId,
})

// Step 2: Get the feature states for the live version
const currentFeatureStates: { data: FeatureState[] } =
await getVersionFeatureState(getStore(), {
environmentId: query.environmentId,
featureId: query.featureId,
sha: versionRes.data.uuid,
})
const res = await Promise.all(

// Step 3: update, create or delete feature states from the new version
const res: { data: FeatureState }[] = await Promise.all(
query.featureStates.map((featureState) => {
// Step 3: update, create or delete feature states from the new version
const matchingVersionState = currentFeatureStates.data.find(
(feature) => {
return (
Expand Down Expand Up @@ -110,14 +113,34 @@ export const featureVersionService = service
}
}),
)

//Step 4: Update feature segment priorities before saving feature states
const prioritiesToUpdate = query.featureStates
.filter((v) => !v.toRemove && !!v.feature_segment)
.map((v) => {
const matchingFeatureSegment = res?.find(
(currentFeatureState) =>
v.feature_segment?.segment ===
currentFeatureState.data.feature_segment?.segment,
)
return {
id: matchingFeatureSegment!.data.feature_segment!.id!,
priority: v.feature_segment!.priority,
}
})
if (prioritiesToUpdate.length) {
await updateSegmentPriorities(getStore(), prioritiesToUpdate)
}

const ret = {
data: res.map((item) => ({
...item,
version_sha: versionRes.data.uuid,
})),
error: res.find((v) => !!v.error)?.error,
}
// Step 4: Publish the feature version

// Step 5: Publish the feature version
if (!query.skipPublish) {
await publishFeatureVersion(getStore(), {
environmentId: query.environmentId,
Expand Down
66 changes: 47 additions & 19 deletions frontend/common/stores/change-requests-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@ const Dispatcher = require('../dispatcher/dispatcher')
const BaseStore = require('./base/_store')
const data = require('../data/base/_data')
const { flatten } = require('lodash')
const {
addFeatureSegmentsToFeatureStates,
} = require('../services/useFeatureState')

const PAGE_SIZE = 20
const transformChangeRequest = (changeRequest) => {
if (changeRequest?.environment_feature_versions?.length) {
return {
...changeRequest,
feature_states: flatten(
changeRequest.environment_feature_versions.map(
(featureVersion) => featureVersion.feature_states,
const transformChangeRequest = async (changeRequest) => {
const res = changeRequest?.environment_feature_versions?.length
? {
...changeRequest,
feature_states: flatten(
changeRequest.environment_feature_versions.map(
(featureVersion) => featureVersion.feature_states,
),
),
),
}
}
: changeRequest

const feature_states = await Promise.all(
res.feature_states.map(addFeatureSegmentsToFeatureStates),
)

return {
...res,
feature_states,
}
return changeRequest
}
const controller = {
actionChangeRequest: (id, action, cb) => {
Expand All @@ -25,8 +36,8 @@ const controller = {
.then(() => {
data
.get(`${Project.api}features/workflows/change-requests/${id}/`)
.then((res) => {
store.model[id] = transformChangeRequest(res)
.then(async (res) => {
store.model[id] = await transformChangeRequest(res)
cb && cb()
store.loaded()
})
Expand All @@ -47,8 +58,8 @@ const controller = {
store.loading()
data
.get(`${Project.api}features/workflows/change-requests/${id}/`)
.then((apiResponse) => {
const res = transformChangeRequest(apiResponse)
.then(async (apiResponse) => {
const res = await transformChangeRequest(apiResponse)
return Promise.all([
data.get(
`${Project.api}environments/${environmentId}/featurestates/?feature=${res.feature_states[0].feature}`,
Expand Down Expand Up @@ -114,15 +125,32 @@ const controller = {
updateChangeRequest: (changeRequest) => {
store.loading()
data
.put(
.get(
`${Project.api}features/workflows/change-requests/${changeRequest.id}/`,
changeRequest,
)
.then((res) => {
store.model[changeRequest.id] = transformChangeRequest(res)
store.loaded()
data
.put(
`${Project.api}features/workflows/change-requests/${changeRequest.id}/`,
{
...res,
approvals: changeRequest.approvals,
description: changeRequest.description,
environment_feature_versions:
changeRequest?.environment_feature_versions?.map((v) => v.uuid),
group_assignments: changeRequest.group_assignments,
title: changeRequest.title,
},
)
.then(async () => {
const res = await data.get(
`${Project.api}features/workflows/change-requests/${changeRequest.id}/`,
)
store.model[changeRequest.id] = await transformChangeRequest(res)
store.loaded()
})
.catch((e) => API.ajaxHandler(store, e))
})
.catch((e) => API.ajaxHandler(store, e))
},
}

Expand Down
Loading

0 comments on commit cf320b7

Please sign in to comment.