Browse Source

feat: data source mutable

pull/6176/head
danstarns 2 years ago
parent
commit
1653dbce2e
  1. 7
      packages/core/src/data_sources/model/DataRecord.ts
  2. 5
      packages/core/src/data_sources/model/DataSource.ts
  3. 5
      packages/core/src/data_sources/types.ts
  4. 128
      packages/core/test/specs/data_sources/mutable.ts

7
packages/core/src/data_sources/model/DataRecord.ts

@ -32,8 +32,11 @@ import EditorModel from '../../editor/model/Editor';
import { _StringKey } from 'backbone';
export default class DataRecord<T extends DataRecordProps = DataRecordProps> extends Model<T> {
public mutable: boolean;
constructor(props: T, opts = {}) {
super(props, opts);
this.mutable = props.mutable ?? true;
this.on('change', this.handleChange);
}
@ -137,6 +140,10 @@ export default class DataRecord<T extends DataRecordProps = DataRecordProps> ext
options?: SetOptions | undefined,
): this;
set(attributeName: unknown, value?: unknown, options?: SetOptions): DataRecord {
if (!this.isNew() && this.attributes.mutable === false) {
throw new Error('Cannot modify immutable record');
}
const onRecordSetValue = this.dataSource?.transformers?.onRecordSetValue;
const applySet = (key: string, val: unknown) => {

5
packages/core/src/data_sources/model/DataSource.ts

@ -152,6 +152,11 @@ export default class DataSource extends Model<DataSourceProps> {
* @name removeRecord
*/
removeRecord(id: string | number, opts?: RemoveOptions): DataRecord | undefined {
const record = this.getRecord(id);
if (record?.mutable === false) {
throw new Error('Cannot remove immutable record');
}
return this.records.remove(id, opts);
}

5
packages/core/src/data_sources/types.ts

@ -7,6 +7,11 @@ export interface DataRecordProps extends ObjectAny {
* Record id.
*/
id: string;
/**
* Specifies if the record is mutable. Defaults to `true`.
*/
mutable?: boolean;
}
export interface DataVariableListener {

128
packages/core/test/specs/data_sources/mutable.ts

@ -0,0 +1,128 @@
import DataSourceManager from '../../../src/data_sources';
import { setupTestEditor } from '../../common';
import EditorModel from '../../../src/editor/model/Editor';
describe('DataSource Immutability', () => {
let em: EditorModel;
let dsm: DataSourceManager;
beforeEach(() => {
({ em, dsm } = setupTestEditor());
});
afterEach(() => {
em.destroy();
});
test('set throws error for immutable record', () => {
const ds = dsm.add({
id: 'testDs1',
records: [{ id: 'id1', name: 'Name1', value: 100, mutable: false }],
});
const record = ds.getRecord('id1');
expect(() => record?.set('name', 'UpdatedName')).toThrow('Cannot modify immutable record');
expect(record?.get('name')).toBe('Name1');
});
test('set throws error for multiple attributes on immutable record', () => {
const ds = dsm.add({
id: 'testDs2',
records: [{ id: 'id1', name: 'Name1', value: 100, mutable: false }],
});
const record = ds.getRecord('id1');
expect(() => record?.set({ name: 'UpdatedName', value: 150 })).toThrow('Cannot modify immutable record');
expect(record?.get('name')).toBe('Name1');
expect(record?.get('value')).toBe(100);
});
test('removeRecord throws error for immutable record', () => {
const ds = dsm.add({
id: 'testDs3',
records: [{ id: 'id1', name: 'Name1', value: 100, mutable: false }],
});
expect(() => ds.removeRecord('id1')).toThrow('Cannot remove immutable record');
expect(ds.getRecord('id1')).toBeTruthy();
});
test('addRecord creates an immutable record', () => {
const ds = dsm.add({
id: 'testDs4',
records: [],
});
ds.addRecord({ id: 'id1', name: 'Name1', value: 100, mutable: false });
const newRecord = ds.getRecord('id1');
expect(() => newRecord?.set('name', 'UpdatedName')).toThrow('Cannot modify immutable record');
expect(newRecord?.get('name')).toBe('Name1');
});
test('setRecords replaces all records with immutable ones', () => {
const ds = dsm.add({
id: 'testDs5',
records: [],
});
ds.setRecords([
{ id: 'id1', name: 'Name1', value: 100, mutable: false },
{ id: 'id2', name: 'Name2', value: 200, mutable: false },
]);
const record1 = ds.getRecord('id1');
const record2 = ds.getRecord('id2');
expect(() => record1?.set('name', 'UpdatedName1')).toThrow('Cannot modify immutable record');
expect(() => record2?.set('name', 'UpdatedName2')).toThrow('Cannot modify immutable record');
expect(record1?.get('name')).toBe('Name1');
expect(record2?.get('name')).toBe('Name2');
});
test('batch update throws error for immutable records', () => {
const ds = dsm.add({
id: 'testDs6',
records: [
{ id: 'id1', name: 'Name1', value: 100, mutable: false },
{ id: 'id2', name: 'Name2', value: 200, mutable: false },
],
});
expect(() => {
ds.records.set([
{ id: 'id1', name: 'BatchUpdate1' },
{ id: 'id2', name: 'BatchUpdate2' },
]);
}).toThrow('Cannot modify immutable record');
expect(ds.getRecord('id1')?.get('name')).toBe('Name1');
expect(ds.getRecord('id2')?.get('name')).toBe('Name2');
});
test('nested property update throws error for immutable record', () => {
const ds = dsm.add({
id: 'testDs7',
records: [{ id: 'nested-id', nested: { prop: 'NestedValue' }, mutable: false }],
});
const record = ds.getRecord('nested-id');
expect(() => record?.set('nested.prop', 'UpdatedNestedValue')).toThrow('Cannot modify immutable record');
});
test('record remains immutable after serialization and deserialization', () => {
const ds = dsm.add({
id: 'testDs8',
records: [{ id: 'id1', name: 'Name1', value: 100, mutable: false }],
});
const serialized = JSON.parse(JSON.stringify(ds.toJSON()));
dsm.remove(ds.id as string);
const newDs = dsm.add(serialized);
const record = newDs.getRecord('id1');
expect(() => record?.set('name', 'SerializedUpdate')).toThrow('Cannot modify immutable record');
expect(record?.get('name')).toBe('Name1');
});
});
Loading…
Cancel
Save