mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-19 07:36:52 +00:00
fix(editor): Show data redaction upgrade prompt on unlicensed instances (#31816)
This commit is contained in:
@@ -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"
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user