Skip to content

Commit bc69016

Browse files
committed
feat(schema-validation): add default template
1 parent b4e727c commit bc69016

File tree

3 files changed

+132
-93
lines changed

3 files changed

+132
-93
lines changed

packages/compass-schema-validation/src/modules/edit-mode.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1+
import type { AnyAction } from 'redux';
12
import type { RootAction } from '.';
3+
import {
4+
SET_VALIDATION_TO_DEFAULT,
5+
type SetValidationToDefaultAction,
6+
} from './validation';
7+
8+
export function isAction<A extends AnyAction>(
9+
action: AnyAction,
10+
type: A['type']
11+
): action is A {
12+
return action.type === type;
13+
}
214

315
/**
416
* The edit mode changed action.
@@ -71,6 +83,12 @@ export default function reducer(
7183
return { ...state, isEditingEnabledByUser: false };
7284
}
7385

86+
if (
87+
isAction<SetValidationToDefaultAction>(action, SET_VALIDATION_TO_DEFAULT)
88+
) {
89+
return { ...state, isEditingEnabledByUser: true };
90+
}
91+
7492
return state;
7593
}
7694

packages/compass-schema-validation/src/modules/validation.spec.ts

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import reducer, {
1010
validationCanceled,
1111
validationSaveFailed,
1212
syntaxErrorOccurred,
13-
validationFromCollection,
1413
VALIDATOR_CHANGED,
1514
VALIDATION_CANCELED,
1615
VALIDATION_SAVE_FAILED,
@@ -128,62 +127,6 @@ describe('validation module', function () {
128127
});
129128
});
130129

131-
describe('validationFromCollection', function () {
132-
context('when an error occurs listing the collection', function () {
133-
it('includes the error', function () {
134-
const error = new Error('Fake error');
135-
expect(validationFromCollection(error)).to.deep.equal({
136-
validationAction: 'error',
137-
validationLevel: 'strict',
138-
error,
139-
});
140-
});
141-
});
142-
143-
context('when the options contains no options', function () {
144-
it('returns defaults', function () {
145-
const data = {};
146-
expect(validationFromCollection(null, data)).to.deep.equal({
147-
validationAction: 'error',
148-
validationLevel: 'strict',
149-
});
150-
});
151-
});
152-
153-
context(
154-
'when the options contains no validation-related options',
155-
function () {
156-
it('returns defaults', function () {
157-
const data = { validation: {} };
158-
expect(validationFromCollection(null, data)).to.deep.equal({
159-
validationAction: 'error',
160-
validationLevel: 'strict',
161-
});
162-
});
163-
}
164-
);
165-
166-
context(
167-
'when the options contains validation-related options',
168-
function () {
169-
it('overrides the defaults', function () {
170-
const data = {
171-
validation: {
172-
validationAction: 'new-validationAction',
173-
validationLevel: 'new-validationLevel',
174-
validator: { foo: 'bar' },
175-
},
176-
};
177-
expect(validationFromCollection(null, data)).to.deep.equal({
178-
validationAction: 'new-validationAction',
179-
validationLevel: 'new-validationLevel',
180-
validator: { foo: 'bar' },
181-
});
182-
});
183-
}
184-
);
185-
});
186-
187130
describe('#reducer', function () {
188131
context(
189132
'when the action is not presented in validation module',

packages/compass-schema-validation/src/modules/validation.ts

Lines changed: 114 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ interface ValidationLevelChangedAction {
7575
validationLevel: ValidationLevel;
7676
}
7777

78+
export const SET_VALIDATION_TO_DEFAULT =
79+
`${PREFIX}/SET_VALIDATION_TO_DEFAULT` as const;
80+
export interface SetValidationToDefaultAction {
81+
type: typeof SET_VALIDATION_TO_DEFAULT;
82+
validator: string;
83+
}
84+
7885
/**
7986
* Syntax error occurred action name.
8087
*/
@@ -89,6 +96,7 @@ export type ValidationAction =
8996
| ValidationCanceledAction
9097
| ValidationSaveFailedAction
9198
| ValidationFetchedAction
99+
| SetValidationToDefaultAction
92100
| ValidationActionChangedAction
93101
| ValidationLevelChangedAction
94102
| SyntaxErrorOccurredAction;
@@ -113,6 +121,36 @@ export interface ValidationState extends Validation {
113121
prevValidation?: Validation;
114122
}
115123

124+
export const VALIDATION_TEMPLATE = `/**
125+
* This is a starter template for a schema validation rule for a collection.
126+
* More information on schema validation rules can be found at:
127+
* https://www.mongodb.com/docs/manual/core/schema-validation/
128+
*/
129+
{
130+
$jsonSchema: {
131+
title: "Library.books",
132+
bsonType: "object",
133+
required: ["fieldname1", "fieldname2"],
134+
properties: {
135+
fieldname1: {
136+
bsonType: "string",
137+
description: "Fieldname1 must be a string",
138+
},
139+
fieldname2: {
140+
bsonType: "int",
141+
description: "Fieldname2 must be an integer",
142+
},
143+
arrayFieldName: {
144+
bsonType: "array",
145+
items: {
146+
bsonType: "string"
147+
},
148+
description: "arrayFieldName must be an array of strings"
149+
},
150+
}
151+
}
152+
}`;
153+
116154
/**
117155
* The initial state.
118156
*/
@@ -276,6 +314,23 @@ const changeValidationLevel = (
276314
};
277315
};
278316

317+
const changeValidationToDefault = (
318+
state: ValidationState,
319+
action: SetValidationToDefaultAction
320+
): ValidationState => {
321+
return {
322+
...state,
323+
validationAction: INITIAL_STATE.validationAction,
324+
validationLevel: INITIAL_STATE.validationLevel,
325+
validator: action.validator,
326+
327+
// Set the previous validation to undefined so that the isChanged
328+
// flag is set to true in future checks.
329+
prevValidation: undefined,
330+
isChanged: true,
331+
};
332+
};
333+
279334
/**
280335
* To not have a huge switch statement in the reducer.
281336
*/
@@ -288,6 +343,7 @@ const MAPPINGS: {
288343
[VALIDATOR_CHANGED]: changeValidator,
289344
[VALIDATION_CANCELED]: setValidation,
290345
[VALIDATION_FETCHED]: setValidation,
346+
[SET_VALIDATION_TO_DEFAULT]: changeValidationToDefault,
291347
[VALIDATION_SAVE_FAILED]: setError,
292348
[VALIDATION_ACTION_CHANGED]: changeValidationAction,
293349
[VALIDATION_LEVEL_CHANGED]: changeValidationLevel,
@@ -348,6 +404,11 @@ export const validationFetched = (
348404
validation,
349405
});
350406

407+
export const setValidationToDefault = (validator: string) => ({
408+
type: SET_VALIDATION_TO_DEFAULT,
409+
validator,
410+
});
411+
351412
/**
352413
* Action creator for validation canceled events.
353414
*/
@@ -385,40 +446,55 @@ export const syntaxErrorOccurred = (
385446
export const fetchValidation = (namespace: {
386447
database: string;
387448
collection: string;
388-
}): SchemaValidationThunkAction<void> => {
389-
return (dispatch, _getState, { dataService }) => {
390-
dataService.collectionInfo(namespace.database, namespace.collection).then(
391-
(collInfo) => {
392-
const validation = validationFromCollection(null, collInfo ?? {});
393-
394-
if (!validation.validator) {
395-
const newValidation = { ...validation, validator: '{}' };
396-
dispatch(validationFetched(newValidation));
397-
dispatch(isLoadedChanged(true));
398-
return;
399-
}
400-
401-
// TODO(COMPASS-4989): EJSON??
402-
const newValidation = {
403-
...validation,
404-
validator: EJSON.stringify(validation.validator, undefined, 2),
405-
};
406-
407-
dispatch(validationFetched(newValidation));
408-
dispatch(zeroStateChanged(false));
409-
dispatch(isLoadedChanged(true));
410-
},
411-
(err: Error) => {
449+
}): SchemaValidationThunkAction<Promise<void>> => {
450+
return async (dispatch, _getState, { dataService, preferences }) => {
451+
try {
452+
const collInfo = await dataService.collectionInfo(
453+
namespace.database,
454+
namespace.collection
455+
);
456+
if (!collInfo?.validation?.validator) {
412457
dispatch(
413-
validationFetched({
414-
...validationFromCollection(err),
415-
validator: undefined,
416-
})
458+
setValidationToDefault(
459+
preferences.getPreferences().enableExportSchema
460+
? VALIDATION_TEMPLATE
461+
: '{}'
462+
)
417463
);
418-
dispatch(zeroStateChanged(false));
419464
dispatch(isLoadedChanged(true));
465+
return;
420466
}
421-
);
467+
468+
dispatch(
469+
validationFetched({
470+
validationAction:
471+
(collInfo.validation.validationAction as ValidationServerAction) ??
472+
INITIAL_STATE.validationAction,
473+
validationLevel:
474+
(collInfo.validation.validationLevel as ValidationLevel) ??
475+
INITIAL_STATE.validationLevel,
476+
// TODO(COMPASS-4989): EJSON??
477+
validator: EJSON.stringify(
478+
collInfo.validation.validator,
479+
undefined,
480+
2
481+
),
482+
})
483+
);
484+
dispatch(zeroStateChanged(false));
485+
dispatch(isLoadedChanged(true));
486+
} catch (err) {
487+
dispatch(
488+
validationFetched({
489+
validationAction: INITIAL_STATE.validationAction,
490+
validationLevel: INITIAL_STATE.validationLevel,
491+
error: err as Error,
492+
validator: undefined,
493+
})
494+
);
495+
dispatch(zeroStateChanged(false));
496+
dispatch(isLoadedChanged(true));
497+
}
422498
};
423499
};
424500

@@ -489,7 +565,7 @@ export const saveValidation = (
489565
validationLevel: savedValidation.validationLevel,
490566
}
491567
);
492-
dispatch(fetchValidation(namespace));
568+
void dispatch(fetchValidation(namespace));
493569
openToast(toastId, {
494570
title: 'New validation rules applied',
495571
variant: 'success',
@@ -506,7 +582,7 @@ export const saveValidation = (
506582
*
507583
* @returns {Function} The function.
508584
*/
509-
export const cancelValidation = () => {
585+
export const cancelValidation = (): SchemaValidationThunkAction<void> => {
510586
return (
511587
dispatch: ThunkDispatch<RootState, unknown, RootAction>,
512588
getState: () => RootState
@@ -518,9 +594,11 @@ export const cancelValidation = () => {
518594
dispatch(
519595
validationCanceled({
520596
isChanged: false,
521-
validator: prevValidation!.validator,
522-
validationAction: prevValidation!.validationAction,
523-
validationLevel: prevValidation!.validationLevel,
597+
validator: prevValidation?.validator ?? '{}',
598+
validationAction:
599+
prevValidation?.validationAction ?? INITIAL_STATE.validationAction,
600+
validationLevel:
601+
prevValidation?.validationLevel ?? INITIAL_STATE.validationLevel,
524602
error: null,
525603
})
526604
);
@@ -540,6 +618,6 @@ export const activateValidation = (): SchemaValidationThunkAction<void> => {
540618
const state = getState();
541619
const namespace = state.namespace;
542620

543-
dispatch(fetchValidation(namespace));
621+
void dispatch(fetchValidation(namespace));
544622
};
545623
};

0 commit comments

Comments
 (0)