Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Segment change requests #4852

Draft
wants to merge 27 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1108b20
Move Change requests page to typescript
kyle-ssg Nov 20, 2024
2c9d224
Remove change requests page
kyle-ssg Nov 20, 2024
78c9788
Remove change requests actions
kyle-ssg Nov 20, 2024
9ae5a5c
Remove unused imports
kyle-ssg Nov 20, 2024
1076ed5
Rename diff segments to segment overrides
kyle-ssg Nov 20, 2024
954e775
Rename diff segments to segment overrides
kyle-ssg Nov 20, 2024
5ed03da
Initial
kyle-ssg Nov 26, 2024
7cc94dd
Create DiffSegment ui
kyle-ssg Nov 26, 2024
0b00957
Create change request page view
kyle-ssg Nov 26, 2024
199a4e5
View and Action Project change requests
kyle-ssg Nov 26, 2024
01c1c62
lint
kyle-ssg Nov 27, 2024
38986d4
QA fixes
kyle-ssg Nov 27, 2024
723b8c0
Merge branch 'refs/heads/main' into feat/segment-change-requests
kyle-ssg Nov 27, 2024
67a09f7
Tidy up
kyle-ssg Nov 27, 2024
3abd623
Tidy up types
kyle-ssg Nov 27, 2024
37fac68
Feature flag project change requests
kyle-ssg Nov 27, 2024
53fecf5
QA Fixes
kyle-ssg Nov 27, 2024
2d4be05
Merge branch 'refs/heads/main' into feat/segment-change-requests
kyle-ssg Dec 18, 2024
4be99e7
Merge main
kyle-ssg Dec 18, 2024
7b26c74
Fix build
kyle-ssg Jan 8, 2025
44bbdca
Merge branch 'refs/heads/main' into feat/segment-change-requests
kyle-ssg Jan 8, 2025
2de7a47
fix schedule icon style
kyle-ssg Jan 14, 2025
b604199
embolden live date
kyle-ssg Jan 14, 2025
9a1219f
Merge branch 'refs/heads/main' into feat/segment-change-requests
kyle-ssg Jan 15, 2025
8d91337
Adjustments
kyle-ssg Jan 17, 2025
6873d0a
Adjust styles and condition mapping
kyle-ssg Jan 29, 2025
7ea6110
Improve formatting
kyle-ssg Jan 29, 2025
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
1 change: 0 additions & 1 deletion frontend/common/dispatcher/action-constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const Actions = Object.assign({}, require('./base/_action-constants'), {
'EDIT_USER_FLAG': 'EDIT_USER_FLAG',
'ENABLE_TWO_FACTOR': 'ENABLE_TWO_FACTOR',
'GET_CHANGE_REQUEST': 'GET_CHANGE_REQUEST',
'GET_CHANGE_REQUESTS': 'GET_CHANGE_REQUESTS',
'GET_ENVIRONMENT': 'GET_ENVIRONMENT',
'GET_FEATURE_USAGE': 'GET_FEATURE_USAGE',
'GET_FLAGS': 'GET_FLAGS',
Expand Down
9 changes: 0 additions & 9 deletions frontend/common/dispatcher/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,6 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), {
projectId,
})
},
getChangeRequests(environment, data, page) {
Dispatcher.handleViewAction({
actionType: Actions.GET_CHANGE_REQUESTS,
committed: data.committed,
environment,
live_from_after: data.live_from_after,
page,
})
},
getFeatureUsage(projectId, environmentId, flag, period) {
Dispatcher.handleViewAction({
actionType: Actions.GET_FEATURE_USAGE,
Expand Down
2 changes: 2 additions & 0 deletions frontend/common/services/useChangeRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { service } from 'common/service'
import Utils from 'common/utils/utils'
import sortBy from 'lodash/sortBy'
import moment from 'moment'
import transformCorePaging from 'common/transformCorePaging'

export const changeRequestService = service
.enhanceEndpoints({ addTagTypes: ['ChangeRequest'] })
Expand All @@ -24,6 +25,7 @@ export const changeRequestService = service
{ ...rest },
)}`,
}),
transformResponse: (res, _, req) => transformCorePaging(req, res),
}),
// END OF ENDPOINTS
}),
Expand Down
16 changes: 8 additions & 8 deletions frontend/common/services/useFeatureVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import transformCorePaging from 'common/transformCorePaging'
import Utils from 'common/utils/utils'
import {
getFeatureStateDiff,
getSegmentDiff,
getSegmentOverrideDiff,
getVariationDiff,
} from 'components/diff/diff-utils'
import { getSegments } from './useSegment'
Expand Down Expand Up @@ -43,13 +43,12 @@ export const getFeatureStateCrud = (
if (!oldFeatureStates) {
return featureStates
}
const segmentDiffs = segments?.length
? getSegmentDiff(
featureStates.filter((v) => !!v.feature_segment),
oldFeatureStates.filter((v) => !!v.feature_segment),
segments,
)
: null
const segmentDiffs = getSegmentOverrideDiff(
featureStates.filter((v) => !!v.feature_segment),
oldFeatureStates.filter((v) => !!v.feature_segment),
segments,
)

const featureStateDiffs = featureStates.filter((v) => {
if (!v.feature_segment) return
const diff = segmentDiffs?.diffs?.find(
Expand Down Expand Up @@ -102,6 +101,7 @@ export const getFeatureStateCrud = (
segment_ids_to_delete_overrides,
}
}

export const featureVersionService = service
.enhanceEndpoints({ addTagTypes: ['FeatureVersion'] })
.injectEndpoints({
Expand Down
2 changes: 1 addition & 1 deletion frontend/common/services/useProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const projectService = service
getProject: builder.query<Res['project'], Req['getProject']>({
providesTags: (res) => [{ id: res?.id, type: 'Project' }],
query: (query: Req['getProject']) => ({
url: `projects/${query.id}`,
url: `projects/${query.id}/`,
}),
}),
getProjects: builder.query<Res['projects'], Req['getProjects']>({
Expand Down
140 changes: 140 additions & 0 deletions frontend/common/services/useProjectChangeRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Res } from 'common/types/responses'
import { Req } from 'common/types/requests'
import { service } from 'common/service'
import Utils from 'common/utils/utils'
import transformCorePaging from 'common/transformCorePaging'
import { getStore } from 'common/store'
import { segmentService } from './useSegment'

export const projectChangeRequestService = service
.enhanceEndpoints({ addTagTypes: ['ProjectChangeRequest'] })
.injectEndpoints({
endpoints: (builder) => ({
actionProjectChangeRequest: builder.mutation<
Res['projectChangeRequest'],
Req['actionProjectChangeRequest']
>({
invalidatesTags: (_, _2, req) => [
{ id: `${req.id}`, type: 'ProjectChangeRequest' },
{ id: 'LIST', type: 'ProjectChangeRequest' },
],
query: (query: Req['actionProjectChangeRequest']) => ({
body: {},
method: 'POST',
url: `projects/${query.project_id}/change-requests/${query.id}/${query.actionType}/`,
}),
transformResponse: (response, _, req) => {
getStore().dispatch(segmentService.util.invalidateTags(['Segment']))
return response
},
}),
createProjectChangeRequest: builder.mutation<
Res['projectChangeRequest'],
Req['createProjectChangeRequest']
>({
invalidatesTags: [{ id: 'LIST', type: 'ProjectChangeRequest' }],
query: (query: Req['createProjectChangeRequest']) => ({
body: query.data,
method: 'POST',
url: `projects/${query.project_id}/change-requests/`,
}),
}),
deleteProjectChangeRequest: builder.mutation<
Res['projectChangeRequest'],
Req['deleteProjectChangeRequest']
>({
invalidatesTags: [{ id: 'LIST', type: 'ProjectChangeRequest' }],
query: (query: Req['deleteProjectChangeRequest']) => ({
body: query,
method: 'DELETE',
url: `projects/${query.project_id}/change-requests/${query.id}/`,
}),
}),
getProjectChangeRequest: builder.query<
Res['projectChangeRequest'],
Req['getProjectChangeRequest']
>({
providesTags: (res) => [
{ id: `${res?.id}`, type: 'ProjectChangeRequest' },
],
query: (query: Req['getProjectChangeRequest']) => ({
url: `projects/${query.project_id}/change-requests/${query.id}/`,
}),
}),
getProjectChangeRequests: builder.query<
Res['projectChangeRequests'],
Req['getProjectChangeRequests']
>({
providesTags: (res) => [{ id: 'LIST', type: 'ProjectChangeRequest' }],
query: ({ project_id, ...rest }: Req['getProjectChangeRequests']) => ({
url: `projects/${project_id}/change-requests/?${Utils.toParam({
...rest,
})}`,
}),
transformResponse: (res, _, req) => transformCorePaging(req, res),
}),
updateProjectChangeRequest: builder.mutation<
Res['projectChangeRequest'],
Req['updateProjectChangeRequest']
>({
invalidatesTags: (res, _, req) => [
{ id: req.data.id, type: 'ProjectChangeRequest' },
{ id: 'LIST', type: 'ProjectChangeRequest' },
],
query: (query: Req['updateProjectChangeRequest']) => ({
body: query.data,
method: 'PUT',
url: `projects/${query.project_id}/change-requests/${query.data.id}/`,
}),
}),

// END OF ENDPOINTS
}),
})

export async function createProjectChangeRequest(
store: any,
data: Req['createProjectChangeRequest'],
options?: Parameters<
typeof projectChangeRequestService.endpoints.createProjectChangeRequest.initiate
>[1],
) {
return store.dispatch(
projectChangeRequestService.endpoints.createProjectChangeRequest.initiate(
data,
options,
),
)
}

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

export const {
useActionProjectChangeRequestMutation,
useCreateProjectChangeRequestMutation,
useDeleteProjectChangeRequestMutation,
useGetProjectChangeRequestQuery,
useGetProjectChangeRequestsQuery,
useUpdateProjectChangeRequestMutation,
// END OF EXPORTS
} = projectChangeRequestService

/* Usage examples:
const { data, isLoading } = useGetProjectChangeRequestsQuery({ id: 2 }, {}) //get hook
const [createProjectChangeRequest, { isLoading, data, isSuccess }] = useCreateProjectChangeRequestsMutation() //create hook
projectChangeRequestService.endpoints.getProjectChangeRequests.select({id: 2})(store.getState()) //access data from any function
*/
67 changes: 11 additions & 56 deletions frontend/common/stores/change-requests-store.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
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 { changeRequestService } = require('common/services/useChangeRequest')
const { getStore } = require('common/store')

const PAGE_SIZE = 20
const transformChangeRequest = async (changeRequest) => {
const feature_states = await Promise.all(
changeRequest.feature_states.map(addFeatureSegmentsToFeatureStates),
Expand All @@ -27,6 +27,9 @@ const controller = {
.get(`${Project.api}features/workflows/change-requests/${id}/`)
.then(async (res) => {
store.model[id] = await transformChangeRequest(res)
getStore().dispatch(
changeRequestService.util.invalidateTags(['ChangeRequest']),
)
cb && cb()
store.loaded()
})
Expand All @@ -39,6 +42,9 @@ const controller = {
.delete(`${Project.api}features/workflows/change-requests/${id}/`)
.then(() => {
store.loaded()
getStore().dispatch(
changeRequestService.util.invalidateTags(['ChangeRequest']),
)
cb()
})
.catch((e) => API.ajaxHandler(store, e))
Expand Down Expand Up @@ -67,50 +73,6 @@ const controller = {
})
.catch((e) => API.ajaxHandler(store, e))
},
getChangeRequests: (envId, { committed, live_from_after }, page) => {
const has4EyesPermission =
Utils.getPlansPermission('4_EYES') ||
Utils.getPlansPermission('SCHEDULE_FLAGS')
if (!has4EyesPermission) {
return
}

if (!envId) {
return
}
store.loading()
store.envId = envId
const committedParams = `${
committed || live_from_after ? 'committed=1' : 'committed=0'
}` // request only committed for closed and scheduled
const liveFromParams = live_from_after
? `&live_from_after=${live_from_after}`
: '' // request live from after for scheduled
const liveFromBeforeParams = committed
? `&live_from_before=${new Date().toISOString()}`
: '' // request live from before for closed
let endpoint =
page ||
`${Project.api}environments/${envId}/list-change-requests/?${committedParams}${liveFromParams}${liveFromBeforeParams}`
if (!endpoint.includes('page_size')) {
endpoint += `&page_size=${PAGE_SIZE}`
}
data
.get(endpoint)
.then((res) => {
res.currentPage = page ? parseInt(page.split('page=')[1]) : 1
res.pageSize = PAGE_SIZE
if (live_from_after) {
store.scheduled[envId] = res
} else if (committed) {
store.committed[envId] = res
} else {
store.model[envId] = res
}
store.loaded()
})
.catch((e) => API.ajaxHandler(store, e))
},
updateChangeRequest: (changeRequest) => {
store.loading()
data
Expand All @@ -136,6 +98,9 @@ const controller = {
`${Project.api}features/workflows/change-requests/${changeRequest.id}/`,
)
store.model[changeRequest.id] = await transformChangeRequest(res)
getStore().dispatch(
changeRequestService.util.invalidateTags(['ChangeRequest']),
)
store.loaded()
})
.catch((e) => API.ajaxHandler(store, e))
Expand All @@ -154,16 +119,6 @@ const store = Object.assign({}, BaseStore, {
store.dispatcherIndex = Dispatcher.register(store, (payload) => {
const action = payload.action // this is our action from handleViewAction
switch (action.actionType) {
case Actions.GET_CHANGE_REQUESTS:
controller.getChangeRequests(
action.environment,
{
committed: action.committed,
live_from_after: action.live_from_after,
},
action.page,
)
break
case Actions.GET_CHANGE_REQUEST:
controller.getChangeRequest(
action.id,
Expand Down
6 changes: 0 additions & 6 deletions frontend/common/stores/feature-list-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,12 +654,6 @@ const controller = {
}
})
prom.then(() => {
AppActions.getChangeRequests(environmentId, {})
AppActions.getChangeRequests(environmentId, { committed: true })
AppActions.getChangeRequests(environmentId, {
live_from_after: new Date().toISOString(),
})

if (featureStateId) {
AppActions.getChangeRequest(
changeRequestData.id,
Expand Down
Loading