diff --git a/packages/compass-schema-validation/src/components/validation-editor.spec.tsx b/packages/compass-schema-validation/src/components/validation-editor.spec.tsx
index aee40f00dbb..43945859c61 100644
--- a/packages/compass-schema-validation/src/components/validation-editor.spec.tsx
+++ b/packages/compass-schema-validation/src/components/validation-editor.spec.tsx
@@ -36,6 +36,7 @@ async function renderValidationEditor(
validation={validation}
generateValidationRules={() => {}}
isRulesGenerationInProgress={false}
+ isSavingInProgress={false}
isEditable
isEditingEnabled
{...props}
@@ -44,6 +45,8 @@ async function renderValidationEditor(
);
}
+const updateValidationTestId = 'update-validation-button';
+
describe('ValidationEditor [Component]', function () {
context(
'when it is an editable mode but editing is not yet enabled',
@@ -173,10 +176,73 @@ describe('ValidationEditor [Component]', function () {
});
});
- it('allows to generate rules', function () {
+ it('disables the ability to generate rules', function () {
const generateBtn = screen.getByTestId('generate-rules-button');
expect(generateBtn).to.be.visible;
expect(generateBtn).to.have.attribute('aria-disabled', 'true');
});
});
+
+ context('when the validator is not changed', function () {
+ beforeEach(async function () {
+ await renderValidationEditor({
+ validation: {
+ validator: '{}',
+ validationAction: 'error',
+ validationLevel: 'moderate',
+ isChanged: false,
+ syntaxError: null,
+ error: null,
+ },
+ });
+ });
+
+ it('does not allow applying the rules', function () {
+ const applyBtn = screen.queryByTestId(updateValidationTestId);
+ expect(applyBtn).to.not.exist;
+ });
+ });
+
+ context('when the validator is changed', function () {
+ beforeEach(async function () {
+ await renderValidationEditor({
+ validation: {
+ validator: '{}',
+ validationAction: 'error',
+ validationLevel: 'moderate',
+ isChanged: true,
+ syntaxError: null,
+ error: null,
+ },
+ });
+ });
+
+ it('allows to apply changes', function () {
+ const applyBtn = screen.getByTestId(updateValidationTestId);
+ expect(applyBtn).to.be.visible;
+ expect(applyBtn).to.have.attribute('aria-disabled', 'false');
+ });
+ });
+
+ context('when the validation saving is in progress', function () {
+ beforeEach(async function () {
+ await renderValidationEditor({
+ validation: {
+ validator: '{}',
+ validationAction: 'error',
+ validationLevel: 'moderate',
+ isChanged: true,
+ syntaxError: null,
+ error: null,
+ },
+ isSavingInProgress: true,
+ });
+ });
+
+ it('disables the apply button and shows a spin loader', function () {
+ const applyBtn = screen.queryByTestId(updateValidationTestId);
+ expect(applyBtn).to.be.visible;
+ expect(applyBtn).to.have.attribute('aria-disabled', 'true');
+ });
+ });
});
diff --git a/packages/compass-schema-validation/src/components/validation-editor.tsx b/packages/compass-schema-validation/src/components/validation-editor.tsx
index 2c41c9239c8..613e9ca472a 100644
--- a/packages/compass-schema-validation/src/components/validation-editor.tsx
+++ b/packages/compass-schema-validation/src/components/validation-editor.tsx
@@ -149,6 +149,7 @@ type ValidationEditorProps = {
isEditable: boolean;
isEditingEnabled: boolean;
isRulesGenerationInProgress: boolean;
+ isSavingInProgress: boolean;
};
/**
@@ -171,6 +172,7 @@ export const ValidationEditor: React.FunctionComponent<
isEditable,
isEditingEnabled,
isRulesGenerationInProgress,
+ isSavingInProgress,
}) => {
const enableExportSchema = usePreference('enableExportSchema');
const track = useTelemetry();
@@ -343,6 +345,8 @@ export const ValidationEditor: React.FunctionComponent<
onClick={() => {
void onClickApplyValidation();
}}
+ isLoading={isSavingInProgress}
+ loadingIndicator={}
disabled={!!syntaxError}
>
Apply
@@ -372,6 +376,7 @@ const mapStateToProps = (state: RootState) => ({
validation: state.validation,
namespace: state.namespace.ns,
isRulesGenerationInProgress: state.rulesGeneration.isInProgress,
+ isSavingInProgress: state.validation.isSaving,
});
/**
diff --git a/packages/compass-schema-validation/src/modules/validation.spec.ts b/packages/compass-schema-validation/src/modules/validation.spec.ts
index cce90e53ddf..876076209d9 100644
--- a/packages/compass-schema-validation/src/modules/validation.spec.ts
+++ b/packages/compass-schema-validation/src/modules/validation.spec.ts
@@ -32,6 +32,7 @@ describe('validation module', function () {
validationAction: 'error',
validationLevel: 'strict',
isChanged: false,
+ isSaving: false,
syntaxError: null,
error: null,
});
@@ -131,6 +132,7 @@ describe('validation module', function () {
expect(validation).to.deep.equal({
isChanged: false,
+ isSaving: false,
prevValidation: {
validator,
validationAction: 'warn',
@@ -159,6 +161,7 @@ describe('validation module', function () {
validationAction: 'error',
validationLevel: 'strict',
isChanged: false,
+ isSaving: false,
syntaxError: null,
error: {
message: 'Validation save failed!',
diff --git a/packages/compass-schema-validation/src/modules/validation.ts b/packages/compass-schema-validation/src/modules/validation.ts
index 01211209f03..ac00b17670b 100644
--- a/packages/compass-schema-validation/src/modules/validation.ts
+++ b/packages/compass-schema-validation/src/modules/validation.ts
@@ -18,6 +18,8 @@ export type ValidationLevel = 'off' | 'moderate' | 'strict';
export const enum ValidationActions {
ValidatorChanged = 'compass-schema-validation/validation/ValidatorChanged',
ValidationCanceled = 'compass-schema-validation/validation/ValidationCanceled',
+ ValidationSaveStarted = 'compass-schema-validation/validation/ValidationSaveStarted',
+ ValidationSaveEnded = 'compass-schema-validation/validation/ValidationSaveEnded',
ValidationSaveFailed = 'compass-schema-validation/validation/ValidationSaveFailed',
ValidationFetched = 'compass-schema-validation/validation/ValidationFetched',
EmptyValidationFetched = 'compass-schema-validation/validation/EmptyValidationFetched',
@@ -35,6 +37,14 @@ export interface ValidationCanceledAction {
type: ValidationActions.ValidationCanceled;
}
+interface ValidationSaveStartedAction {
+ type: ValidationActions.ValidationSaveStarted;
+}
+
+interface ValidationSaveEndedAction {
+ type: ValidationActions.ValidationSaveEnded;
+}
+
interface ValidationSaveFailedAction {
type: ValidationActions.ValidationSaveFailed;
error: { message: string };
@@ -86,6 +96,7 @@ export interface Validation {
export interface ValidationState extends Validation {
isChanged: boolean;
+ isSaving: boolean;
syntaxError: null | { message: string };
error: null | { message: string };
prevValidation?: Validation;
@@ -99,6 +110,7 @@ export const INITIAL_STATE: ValidationState = {
validationAction: 'error',
validationLevel: 'strict',
isChanged: false,
+ isSaving: false,
syntaxError: null,
error: null,
};
@@ -165,6 +177,31 @@ export default function reducer(
};
}
+ if (
+ isAction(
+ action,
+ ValidationActions.ValidationSaveStarted
+ )
+ ) {
+ return {
+ ...state,
+ error: null,
+ isSaving: true,
+ };
+ }
+
+ if (
+ isAction(
+ action,
+ ValidationActions.ValidationSaveEnded
+ )
+ ) {
+ return {
+ ...state,
+ isSaving: false,
+ };
+ }
+
if (
isAction(
action,
@@ -369,6 +406,14 @@ export const emptyValidationFetched = ({
validationTemplate,
});
+export const validationSaveStarted = (): ValidationSaveStartedAction => ({
+ type: ValidationActions.ValidationSaveStarted,
+});
+
+export const validationSaveEnded = (): ValidationSaveEndedAction => ({
+ type: ValidationActions.ValidationSaveEnded,
+});
+
export const validationCanceled = (): ValidationCanceledAction => ({
type: ValidationActions.ValidationCanceled,
});
@@ -446,6 +491,9 @@ export const saveValidation = (
validation_level: validation.validationLevel,
};
track('Schema Validation Updated', trackEvent, connectionInfoRef.current);
+
+ dispatch(validationSaveStarted());
+
try {
await dataService.updateCollection(
`${namespace.database}.${namespace.collection}`,
@@ -463,6 +511,8 @@ export const saveValidation = (
dispatch(disableEditRules());
} catch (error) {
dispatch(validationSaveFailed(error as Error));
+ } finally {
+ dispatch(validationSaveEnded());
}
};
};
diff --git a/packages/compass-schema-validation/src/stores/store.spec.ts b/packages/compass-schema-validation/src/stores/store.spec.ts
index cbda112d886..1357dfec014 100644
--- a/packages/compass-schema-validation/src/stores/store.spec.ts
+++ b/packages/compass-schema-validation/src/stores/store.spec.ts
@@ -217,6 +217,7 @@ describe('Schema Validation Store', function () {
error: null,
syntaxError: null,
isChanged: false,
+ isSaving: false,
prevValidation: {
validator,
validationAction: 'warn',
@@ -242,6 +243,7 @@ describe('Schema Validation Store', function () {
error: { message: 'Validation fetch failed!' },
syntaxError: null,
isChanged: true,
+ isSaving: false,
prevValidation: {
validator: '{}',
validationAction: 'error',