From 2517e9dfd42a58adb69a52050f4e3fc1663bc127 Mon Sep 17 00:00:00 2001 From: Kyle Johnson Date: Tue, 13 Aug 2024 16:31:19 +0100 Subject: [PATCH] fix: save feature error handling (#4058) --- frontend/common/stores/feature-list-store.ts | 254 ++++++++++--------- frontend/web/components/ErrorMessage.js | 4 +- frontend/web/project/api.js | 5 + 3 files changed, 143 insertions(+), 120 deletions(-) diff --git a/frontend/common/stores/feature-list-store.ts b/frontend/common/stores/feature-list-store.ts index b2067cf5695b..a47c9ffe27d5 100644 --- a/frontend/common/stores/feature-list-store.ts +++ b/frontend/common/stores/feature-list-store.ts @@ -90,8 +90,11 @@ const controller = { }), project_id: projectId, }) - .then((res) => - Promise.all( + .then((res) => { + if (res.error) { + throw res.error?.error || res.error + } + return Promise.all( (flag.multivariate_options || []).map((v) => data .post( @@ -107,8 +110,8 @@ const controller = { data.get( `${Project.api}projects/${projectId}/features/${res.data.id}/`, ), - ), - ) + ) + }) .then(() => Promise.all([ data.get(`${Project.api}projects/${projectId}/features/`), @@ -322,123 +325,132 @@ const controller = { store.saving() API.trackEvent(Constants.events.EDIT_FEATURE) - segmentOverridesProm.then(() => { - if (mode !== 'VALUE') { - prom = Promise.resolve() - } else if (environmentFlag) { - prom = data - .get( - `${Project.api}environments/${environmentId}/featurestates/${environmentFlag.id}/`, - ) - .then((environmentFeatureStates) => { - const multivariate_feature_state_values = - environmentFeatureStates.multivariate_feature_state_values && - environmentFeatureStates.multivariate_feature_state_values.map( - (v) => { - const matching = - environmentFlag.multivariate_feature_state_values.find( - (m) => m.id === v.multivariate_feature_option, - ) || {} - return { - ...v, - percentage_allocation: - matching.default_percentage_allocation, - } - }, - ) - environmentFlag.multivariate_feature_state_values = - multivariate_feature_state_values - return data.put( + segmentOverridesProm + .then(() => { + if (mode !== 'VALUE') { + prom = Promise.resolve() + } else if (environmentFlag) { + prom = data + .get( `${Project.api}environments/${environmentId}/featurestates/${environmentFlag.id}/`, - Object.assign({}, environmentFlag, { - enabled: flag.default_enabled, - feature_state_value: Utils.getTypedValue( - flag.initial_value, - undefined, - true, - ), - }), ) - }) - } else { - prom = data.post( - `${Project.api}environments/${environmentId}/featurestates/`, - Object.assign({}, flag, { - enabled: false, - environment: environmentId, - feature: projectFlag, - }), - ) - } - - const segmentOverridesRequest = - mode === 'SEGMENT' && segmentOverrides - ? (segmentOverrides.length - ? updateSegmentPriorities( - getStore(), - segmentOverrides.map((override, index) => ({ - id: override.id, - priority: index, - })), + .then((environmentFeatureStates) => { + const multivariate_feature_state_values = + environmentFeatureStates.multivariate_feature_state_values && + environmentFeatureStates.multivariate_feature_state_values.map( + (v) => { + const matching = + environmentFlag.multivariate_feature_state_values.find( + (m) => m.id === v.multivariate_feature_option, + ) || {} + return { + ...v, + percentage_allocation: + matching.default_percentage_allocation, + } + }, ) - : Promise.resolve([]) - ).then(() => - Promise.all( - segmentOverrides.map((override) => - data.put( - `${Project.api}features/featurestates/${override.feature_segment_value.id}/`, - { - ...override.feature_segment_value, - enabled: override.enabled, - feature_state_value: Utils.valueToFeatureState( - override.value, - ), - multivariate_feature_state_values: - override.multivariate_options && - override.multivariate_options.map((o) => { - if (o.multivariate_feature_option) return o - return { - multivariate_feature_option: - environmentFlag.multivariate_feature_state_values[ - o.multivariate_feature_option_index - ].multivariate_feature_option, - percentage_allocation: o.percentage_allocation, - } - }), - }, + environmentFlag.multivariate_feature_state_values = + multivariate_feature_state_values + return data.put( + `${Project.api}environments/${environmentId}/featurestates/${environmentFlag.id}/`, + Object.assign({}, environmentFlag, { + enabled: flag.default_enabled, + feature_state_value: Utils.getTypedValue( + flag.initial_value, + undefined, + true, ), - ), - ), - ) - : Promise.resolve() - - Promise.all([prom, segmentOverridesRequest]).then(([res, segmentRes]) => { - if (store.model) { - store.model.keyedEnvironmentFeatures[projectFlag.id] = res - if (segmentRes) { - const feature = _.find( - store.model.features, - (f) => f.id === projectFlag.id, - ) - if (feature) { - feature.feature_segments = _.map( - segmentRes.feature_segments, - (segment) => ({ - ...segment, - segment: segment.segment.id, }), ) - } - } + }) + } else { + prom = data.post( + `${Project.api}environments/${environmentId}/featurestates/`, + Object.assign({}, flag, { + enabled: false, + environment: environmentId, + feature: projectFlag, + }), + ) } - if (store.model) { - store.model.lastSaved = new Date().valueOf() - } - onComplete && onComplete() - store.saved({}) + const segmentOverridesRequest = + mode === 'SEGMENT' && segmentOverrides + ? (segmentOverrides.length + ? updateSegmentPriorities( + getStore(), + segmentOverrides.map((override, index) => ({ + id: override.id, + priority: index, + })), + ) + : Promise.resolve([]) + ).then(() => + Promise.all( + segmentOverrides.map((override) => + data.put( + `${Project.api}features/featurestates/${override.feature_segment_value.id}/`, + { + ...override.feature_segment_value, + enabled: override.enabled, + feature_state_value: Utils.valueToFeatureState( + override.value, + ), + multivariate_feature_state_values: + override.multivariate_options && + override.multivariate_options.map((o) => { + if (o.multivariate_feature_option) return o + return { + multivariate_feature_option: + environmentFlag + .multivariate_feature_state_values[ + o.multivariate_feature_option_index + ].multivariate_feature_option, + percentage_allocation: o.percentage_allocation, + } + }), + }, + ), + ), + ), + ) + : Promise.resolve() + + Promise.all([prom, segmentOverridesRequest]) + .then(([res, segmentRes]) => { + if (store.model) { + store.model.keyedEnvironmentFeatures[projectFlag.id] = res + if (segmentRes) { + const feature = _.find( + store.model.features, + (f) => f.id === projectFlag.id, + ) + if (feature) { + feature.feature_segments = _.map( + segmentRes.feature_segments, + (segment) => ({ + ...segment, + segment: segment.segment.id, + }), + ) + } + } + } + + if (store.model) { + store.model.lastSaved = new Date().valueOf() + } + onComplete && onComplete() + store.saved({}) + }) + .catch((e) => { + API.ajaxHandler(store, e) + }) + }) + .catch((e) => { + API.ajaxHandler(store, e) }) - }) }, editFeatureStateChangeRequest: async ( projectId: string, @@ -770,13 +782,17 @@ const controller = { ) } - prom.then((res) => { - if (store.model) { - store.model.lastSaved = new Date().valueOf() - } - onComplete && onComplete() - store.saved({}) - }) + prom + .then((res) => { + if (store.model) { + store.model.lastSaved = new Date().valueOf() + } + onComplete && onComplete() + store.saved({}) + }) + .catch((e) => { + API.ajaxHandler(store, e) + }) }, getFeatureUsage(projectId, environmentId, flag, period) { data diff --git a/frontend/web/components/ErrorMessage.js b/frontend/web/components/ErrorMessage.js index cbf1832f6c7e..b50f644e6093 100644 --- a/frontend/web/components/ErrorMessage.js +++ b/frontend/web/components/ErrorMessage.js @@ -28,7 +28,9 @@ export default class ErrorMessage extends PureComponent { - {typeof error === 'object' ? ( + {error instanceof Error ? ( + error.message + ) : typeof error === 'object' ? (