fix(editor): Show data redaction upgrade prompt on unlicensed instances (#31816)

This commit is contained in:
Csaba Tuncsik
2026-06-08 10:37:50 +02:00
committed by GitHub
parent c3abda14bc
commit b755ee4b32
4 changed files with 56 additions and 4 deletions
@@ -889,6 +889,7 @@ export type CloudUpdateLinkSourceType =
| 'chat-hub'
| 'empty-state-builder-prompt'
| 'instance-ai'
| 'data-redaction'
| 'workflow-settings';
export type UTMCampaign =
@@ -146,7 +146,7 @@ function onSelectFloor(value: SelectValue | undefined) {
}
function goToUpgrade() {
void pageRedirectionHelper.goToUpgrade('settings-users', 'upgrade-users');
void pageRedirectionHelper.goToUpgrade('data-redaction', 'upgrade-data-redaction');
}
</script>
@@ -787,5 +787,49 @@ describe('SecuritySettings', () => {
);
});
});
describe('when security settings endpoint is unlicensed (403)', () => {
beforeEach(() => {
enableRedactionEnforcementFlag(true);
settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.DataRedaction] = false;
// Reproduce the 403: useAsyncState keeps state === undefined.
getSecuritySettings.mockRejectedValue(new Error('Forbidden'));
});
it('should still render the data redaction section when the endpoint 403s', async () => {
const { getByTestId } = renderView();
await waitFor(() => {
expect(getByTestId('enable-redaction-enforcement')).toBeInTheDocument();
});
expect(getByTestId('enable-redaction-enforcement')).toHaveClass('is-disabled');
});
it('should show the Upgrade badges and disabled scope dropdown when endpoint 403s', async () => {
const { getByTestId, getAllByText } = renderView();
await waitFor(() => {
expect(getByTestId('enable-redaction-enforcement')).toBeInTheDocument();
});
expect(getByTestId('redaction-enforcement-scope-row')).toBeInTheDocument();
expect(getByTestId('redaction-enforcement-scope-select')).toHaveAttribute('data-disabled');
// One badge on the toggle row, one on the scope row.
expect(getAllByText('Upgrade').length).toBeGreaterThanOrEqual(2);
// Defaults to floor 'off' when state never resolves.
expect(getByTestId('redaction-enforcement-summary')).toHaveTextContent('No executions');
});
it('should not show an error toast when the endpoint 403s', async () => {
const { getByTestId } = renderView();
await waitFor(() => {
expect(getByTestId('enable-redaction-enforcement')).toBeInTheDocument();
});
expect(showError).not.toHaveBeenCalled();
});
});
});
});
@@ -68,7 +68,7 @@ function goToUpgrade() {
void pageRedirectionHelper.goToUpgrade('settings-users', 'upgrade-users');
}
const { state } = useAsyncState(async () => {
const { state, isReady, error } = useAsyncState(async () => {
const settings = await securitySettingsApi.getSecuritySettings(rootStore.restApiContext);
return {
personalSpacePublishing: settings.personalSpacePublishing,
@@ -83,6 +83,13 @@ const { state } = useAsyncState(async () => {
const isManagedByEnv = computed(() => state.value?.managedByEnv ?? false);
// The security settings endpoint is gated by an enterprise license and 403s on
// unlicensed instances, leaving `state` undefined. The data redaction section
// still needs to render so the licensed-feature upgrade prompt is reachable, so
// we render once the request settles (resolved or failed) rather than waiting
// for a defined `state`.
const isSecuritySettingsSettled = computed(() => isReady.value || error.value !== undefined);
async function updatePersonalSpaceSetting(
key: 'personalSpacePublishing' | 'personalSpaceSharing',
value: boolean,
@@ -238,8 +245,8 @@ const sharingCountText = computed(() => {
</div>
<DataRedactionSection
v-if="isRedactionEnforcementFlagEnabled && state !== undefined"
:initial-floor="state.initialRedactionFloor"
v-if="isRedactionEnforcementFlagEnabled && isSecuritySettingsSettled"
:initial-floor="state?.initialRedactionFloor ?? 'off'"
:managed-by-env="isManagedByEnv"
/>