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 5145e12ebbc..aee40f00dbb 100644 --- a/packages/compass-schema-validation/src/components/validation-editor.spec.tsx +++ b/packages/compass-schema-validation/src/components/validation-editor.spec.tsx @@ -3,12 +3,14 @@ import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; import sinon from 'sinon'; import { ValidationEditor } from './validation-editor'; +import { PreferencesProvider } from 'compass-preferences-model/provider'; +import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; -function renderValidationEditor( +async function renderValidationEditor( props: Partial> ) { const validation = { - validator: '', + validator: '{}', validationAction: 'warn', validationLevel: 'moderate', isChanged: false, @@ -16,22 +18,29 @@ function renderValidationEditor( error: null, } as const; + const preferences = await createSandboxFromDefaultPreferences(); + await preferences.savePreferences({ enableExportSchema: true }); + return render( - {}} - validationActionChanged={() => {}} - validationLevelChanged={() => {}} - cancelValidation={() => {}} - saveValidation={() => {}} - clearSampleDocuments={() => {}} - serverVersion="8.0.5" - onClickEnableEditRules={() => {}} - validation={validation} - isEditable - isEditingEnabled - {...props} - /> + + {}} + validationActionChanged={() => {}} + validationLevelChanged={() => {}} + cancelValidation={() => {}} + saveValidation={() => {}} + clearSampleDocuments={() => {}} + serverVersion="8.0.5" + onClickEnableEditRules={() => {}} + validation={validation} + generateValidationRules={() => {}} + isRulesGenerationInProgress={false} + isEditable + isEditingEnabled + {...props} + /> + ); } @@ -40,9 +49,9 @@ describe('ValidationEditor [Component]', function () { 'when it is an editable mode but editing is not yet enabled', function () { let onClickEnableEditRulesSpy: sinon.SinonSpy; - beforeEach(function () { + beforeEach(async function () { onClickEnableEditRulesSpy = sinon.spy(); - renderValidationEditor({ + await renderValidationEditor({ onClickEnableEditRules: onClickEnableEditRulesSpy, isEditingEnabled: false, isEditable: true, @@ -62,8 +71,8 @@ describe('ValidationEditor [Component]', function () { ); context('when it is an editable mode and editing is enabled', function () { - beforeEach(function () { - renderValidationEditor({ + beforeEach(async function () { + await renderValidationEditor({ isEditable: true, isEditingEnabled: true, }); @@ -78,8 +87,8 @@ describe('ValidationEditor [Component]', function () { }); context('when it is a not editable mode', function () { - beforeEach(function () { - renderValidationEditor({ + beforeEach(async function () { + await renderValidationEditor({ isEditable: false, }); }); @@ -91,4 +100,83 @@ describe('ValidationEditor [Component]', function () { ).to.not.exist; }); }); + + context('when the validator is empty', function () { + let onGenerateRulesSpy: sinon.SinonSpy; + beforeEach(async function () { + onGenerateRulesSpy = sinon.spy(); + await renderValidationEditor({ + generateValidationRules: onGenerateRulesSpy, + isEditable: true, + validation: { + validator: '', + validationAction: 'error', + validationLevel: 'moderate', + isChanged: false, + syntaxError: null, + error: null, + }, + }); + }); + + it('allows to generate rules', function () { + const generateBtn = screen.getByRole('button', { + name: 'Generate rules', + }); + expect(generateBtn).to.be.visible; + userEvent.click(generateBtn); + expect(onGenerateRulesSpy).to.have.been.calledOnce; + }); + }); + + context('when the validator is empty object', function () { + let onGenerateRulesSpy: sinon.SinonSpy; + beforeEach(async function () { + onGenerateRulesSpy = sinon.spy(); + await renderValidationEditor({ + generateValidationRules: onGenerateRulesSpy, + isEditable: true, + validation: { + validator: '{}', + validationAction: 'error', + validationLevel: 'moderate', + isChanged: false, + syntaxError: null, + error: null, + }, + }); + }); + + it('allows to generate rules', function () { + const generateBtn = screen.getByRole('button', { + name: 'Generate rules', + }); + expect(generateBtn).to.be.visible; + userEvent.click(generateBtn); + expect(onGenerateRulesSpy).to.have.been.calledOnce; + }); + }); + + context('when rules generation is in progress', function () { + beforeEach(async function () { + await renderValidationEditor({ + isEditable: true, + isRulesGenerationInProgress: true, + validation: { + validator: '{}', + validationAction: 'error', + validationLevel: 'moderate', + isChanged: false, + syntaxError: null, + error: null, + }, + }); + }); + + it('allows to generate rules', function () { + const generateBtn = screen.getByTestId('generate-rules-button'); + expect(generateBtn).to.be.visible; + expect(generateBtn).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 b360de28fc3..2c41c9239c8 100644 --- a/packages/compass-schema-validation/src/components/validation-editor.tsx +++ b/packages/compass-schema-validation/src/components/validation-editor.tsx @@ -14,6 +14,9 @@ import { useConfirmationModal, useDarkMode, KeylineCard, + ButtonVariant, + SpinLoader, + Tooltip, } from '@mongodb-js/compass-components'; import { CodemirrorMultilineEditor, @@ -39,6 +42,8 @@ import { } from '../modules/validation'; import { clearSampleDocuments } from '../modules/sample-documents'; import { enableEditRules } from '../modules/edit-mode'; +import { usePreference } from 'compass-preferences-model/provider'; +import { generateValidationRules } from '../modules/rules-generation'; const validationEditorStyles = css({ padding: spacing[400], @@ -48,6 +53,10 @@ const validationOptionsStyles = css({ display: 'flex', }); +const generateButtonContainerStyles = css({ + flexGrow: 1, +}); + const actionsStyles = css({ display: 'flex', alignItems: 'center', @@ -126,6 +135,7 @@ type ValidationEditorProps = { validationLevelChanged: (level: ValidationLevel) => void; cancelValidation: () => void; saveValidation: (text: Validation) => void; + generateValidationRules: () => void; serverVersion: string; validation: Pick< ValidationState, @@ -138,6 +148,7 @@ type ValidationEditorProps = { >; isEditable: boolean; isEditingEnabled: boolean; + isRulesGenerationInProgress: boolean; }; /** @@ -154,11 +165,14 @@ export const ValidationEditor: React.FunctionComponent< validationLevelChanged, cancelValidation, saveValidation, + generateValidationRules, serverVersion, validation, isEditable, isEditingEnabled, + isRulesGenerationInProgress, }) => { + const enableExportSchema = usePreference('enableExportSchema'); const track = useTelemetry(); const connectionInfoRef = useConnectionInfoRef(); const { showConfirmation } = useConfirmationModal(); @@ -227,12 +241,43 @@ export const ValidationEditor: React.FunctionComponent< saveValidationRef.current(validationRef.current); }, [showConfirmation]); + const isEmpty = useMemo(() => { + if (!validation.validator || validation.validator.length === 0) return true; + try { + return Object.keys(JSON.parse(validation.validator)).length === 0; + } catch { + return false; + } + }, [validation.validator]); + return (
+ {enableExportSchema && ( +
+ } + onClick={generateValidationRules} + variant={ButtonVariant.Primary} + size="small" + > + Generate rules + + } + > + Clear existing rules before generating new ones + +
+ )} ({ isEditingEnabled: state.editMode.isEditingEnabledByUser, validation: state.validation, namespace: state.namespace.ns, + isRulesGenerationInProgress: state.rulesGeneration.isInProgress, }); /** @@ -339,4 +385,5 @@ export default connect(mapStateToProps, { onClickEnableEditRules: enableEditRules, validationActionChanged, validationLevelChanged, + generateValidationRules, })(ValidationEditor); diff --git a/packages/compass-schema-validation/src/components/validation-selectors.tsx b/packages/compass-schema-validation/src/components/validation-selectors.tsx index 06ec8d20ff4..90b951cc834 100644 --- a/packages/compass-schema-validation/src/components/validation-selectors.tsx +++ b/packages/compass-schema-validation/src/components/validation-selectors.tsx @@ -23,7 +23,7 @@ const LEVEL_HELP_URL = const validationOptionStyles = css({ display: 'flex', - marginRight: spacing[4], + marginLeft: spacing[4], alignItems: 'center', }); @@ -47,7 +47,7 @@ export function ActionSelector({ return (
- + - +