Browse Source

init: DataSourceTransformers

ci/dependabot
danstarns 2 years ago
parent
commit
1b4e092319
  1. 2
      src/common/index.ts
  2. 21
      src/data_sources/model/DataRecords.ts
  3. 13
      src/data_sources/model/DataSource.ts
  4. 13
      src/data_sources/types.ts
  5. 55
      test/specs/data_sources/index.ts

2
src/common/index.ts

@ -5,7 +5,7 @@ interface NOOP {}
export type Debounced = Function & { cancel(): void }; export type Debounced = Function & { cancel(): void };
export type SetOptions = Backbone.ModelSetOptions & { avoidStore?: boolean }; export type SetOptions = Backbone.ModelSetOptions & { avoidStore?: boolean; avoidTransformers?: boolean };
export type AddOptions = Backbone.AddOptions & { temporary?: boolean; action?: string }; export type AddOptions = Backbone.AddOptions & { temporary?: boolean; action?: string };

21
src/data_sources/model/DataRecords.ts

@ -1,8 +1,11 @@
import { AddOptions as _AddOptions } from 'backbone';
import { Collection } from '../../common'; import { Collection } from '../../common';
import { DataRecordProps } from '../types'; import { DataRecordProps } from '../types';
import DataRecord from './DataRecord'; import DataRecord from './DataRecord';
import DataSource from './DataSource'; import DataSource from './DataSource';
type AddOptions = _AddOptions & { avoidTransformers?: boolean };
export default class DataRecords extends Collection<DataRecord> { export default class DataRecords extends Collection<DataRecord> {
dataSource: DataSource; dataSource: DataSource;
@ -10,6 +13,24 @@ export default class DataRecords extends Collection<DataRecord> {
super(models, options); super(models, options);
this.dataSource = options.dataSource; this.dataSource = options.dataSource;
} }
add(model: {} | DataRecord<DataRecordProps>, options?: AddOptions): DataRecord<DataRecordProps>;
add(models: ({} | DataRecord<DataRecordProps>)[], options?: AddOptions): DataRecord<DataRecordProps>[];
add(models: unknown, options?: AddOptions): DataRecord<DataRecordProps> | DataRecord<DataRecordProps>[] {
const onRecordInsert = this.dataSource?.transformers?.onRecordInsert;
if (options?.avoidTransformers) {
return super.add(models as DataRecord<DataRecordProps>, options);
}
if (onRecordInsert) {
const m = (Array.isArray(models) ? models : [models]).map(onRecordInsert);
return super.add(m, options);
} else {
return super.add(models as DataRecord<DataRecordProps>, options);
}
}
} }
DataRecords.prototype.model = DataRecord; DataRecords.prototype.model = DataRecord;

13
src/data_sources/model/DataSource.ts

@ -1,6 +1,6 @@
import { AddOptions, CombinedModelConstructorOptions, Model, RemoveOptions } from '../../common'; import { AddOptions, CombinedModelConstructorOptions, Model, RemoveOptions } from '../../common';
import EditorModel from '../../editor/model/Editor'; import EditorModel from '../../editor/model/Editor';
import { DataRecordProps, DataSourceProps } from '../types'; import { DataRecordProps, DataSourceProps, DataSourceTransformers } from '../types';
import DataRecord from './DataRecord'; import DataRecord from './DataRecord';
import DataRecords from './DataRecords'; import DataRecords from './DataRecords';
import DataSources from './DataSources'; import DataSources from './DataSources';
@ -8,15 +8,19 @@ import DataSources from './DataSources';
interface DataSourceOptions extends CombinedModelConstructorOptions<{ em: EditorModel }, DataSource> {} interface DataSourceOptions extends CombinedModelConstructorOptions<{ em: EditorModel }, DataSource> {}
export default class DataSource extends Model<DataSourceProps> { export default class DataSource extends Model<DataSourceProps> {
transformers: DataSourceTransformers;
defaults() { defaults() {
return { return {
records: [], records: [],
transformers: {},
}; };
} }
constructor(props: DataSourceProps, opts: DataSourceOptions) { constructor(props: DataSourceProps, opts: DataSourceOptions) {
super(props, opts); super(props, opts);
const { records } = props; const { records, transformers } = props;
this.transformers = transformers || {};
if (!(records instanceof DataRecords)) { if (!(records instanceof DataRecords)) {
this.set({ records: new DataRecords(records!, { dataSource: this }) }); this.set({ records: new DataRecords(records!, { dataSource: this }) });
@ -38,6 +42,11 @@ export default class DataSource extends Model<DataSourceProps> {
} }
addRecord(record: DataRecordProps, opts?: AddOptions) { addRecord(record: DataRecordProps, opts?: AddOptions) {
const onRecordInsert = this.transformers.onRecordInsert;
if (onRecordInsert) {
record = onRecordInsert(record);
}
return this.records.add(record, opts); return this.records.add(record, opts);
} }

13
src/data_sources/types.ts

@ -12,6 +12,19 @@ export interface DataSourceProps {
* DataSource records. * DataSource records.
*/ */
records?: DataRecords | DataRecord[] | DataRecordProps[]; records?: DataRecords | DataRecord[] | DataRecordProps[];
/**
* DataSource validation and transformation factories.
*/
transformers?: DataSourceTransformers;
}
export interface DataSourceTransformers {
onRecordInsert?: (record: DataRecordProps) => DataRecordProps;
onRecordUpdate?: (record: DataRecord) => DataRecord;
onRecordDelete?: (record: DataRecord) => DataRecord;
onRecordRead?: (record: DataRecord) => DataRecord;
} }
export interface DataRecordProps extends ObjectAny { export interface DataRecordProps extends ObjectAny {

55
test/specs/data_sources/index.ts

@ -126,6 +126,61 @@ describe('DataSourceManager', () => {
}); });
}); });
describe.only('Transformers', () => {
let fixtures: HTMLElement;
let cmpRoot: ComponentWrapper;
beforeEach(() => {
document.body.innerHTML = '<div id="fixtures"></div>';
const { Pages, Components } = em;
Pages.onLoad();
cmpRoot = Components.getWrapper()!;
const View = Components.getType('wrapper')!.view;
const wrapperEl = new View({
model: cmpRoot,
config: { ...cmpRoot.config, em },
});
wrapperEl.render();
fixtures = document.body.querySelector('#fixtures')!;
fixtures.appendChild(wrapperEl.el);
});
test('onRecordInsert', () => {
const testDataSource: DataSourceProps = {
id: 'test-data-source',
records: [],
transformers: {
onRecordInsert: record => {
record.content = record.content.toUpperCase();
return record;
},
},
};
dsm.add(testDataSource);
const cmp = cmpRoot.append({
tagName: 'h1',
type: 'text',
components: [
{
type: 'data-variable',
value: 'default',
path: 'test-data-source.id1.content',
},
],
})[0];
const ds = dsm.get('test-data-source');
ds.addRecord({ id: 'id1', content: 'i love grapes' });
const el = cmp.getEl();
expect(el?.innerHTML).toContain('I LOVE GRAPES');
const result = ds.getRecord('id1')?.get('content');
expect(result).toBe('I LOVE GRAPES');
});
});
test('add DataSource with records', () => { test('add DataSource with records', () => {
const eventAdd = jest.fn(); const eventAdd = jest.fn();
em.on(dsm.events.add, eventAdd); em.on(dsm.events.add, eventAdd);

Loading…
Cancel
Save