Browse Source

Fix datasource types

pull/6300/head
mohamedsalem401 1 year ago
parent
commit
786333936c
  1. 7
      packages/core/src/data_sources/index.ts
  2. 4
      packages/core/src/data_sources/model/DataRecord.ts
  3. 49
      packages/core/src/data_sources/model/DataSource.ts
  4. 33
      packages/core/src/data_sources/types.ts
  5. 6
      packages/core/test/specs/data_sources/mutable.ts

7
packages/core/src/data_sources/index.ts

@ -42,7 +42,7 @@ import { get, stringToPath } from '../utils/mixins';
import DataRecord from './model/DataRecord'; import DataRecord from './model/DataRecord';
import DataSource from './model/DataSource'; import DataSource from './model/DataSource';
import DataSources from './model/DataSources'; import DataSources from './model/DataSources';
import { DataSourcesEvents, DataSourceProps } from './types'; import { DataSourcesEvents, DataSourceProps, DataRecordProps } from './types';
import { Events } from 'backbone'; import { Events } from 'backbone';
export default class DataSourceManager extends ItemManagerModule<ModuleConfig, DataSources> { export default class DataSourceManager extends ItemManagerModule<ModuleConfig, DataSources> {
@ -68,10 +68,11 @@ export default class DataSourceManager extends ItemManagerModule<ModuleConfig, D
* ] * ]
* }); * });
*/ */
add(props: DataSourceProps, opts: AddOptions = {}) { add<DRProps extends DataRecordProps>(props: DataSourceProps<DRProps>, opts: AddOptions = {}): DataSource<DRProps> {
const { all } = this; const { all } = this;
props.id = props.id || this._createId(); props.id = props.id || this._createId();
return all.add(props, opts);
return all.add(props, opts) as DataSource<DRProps>;
} }
/** /**

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

@ -25,7 +25,7 @@
import { keys } from 'underscore'; import { keys } from 'underscore';
import { Model, SetOptions } from '../../common'; import { Model, SetOptions } from '../../common';
import { DataRecordProps, DataSourcesEvents } from '../types'; import { DataRecordProps, DataSourcesEvents, DeepPartialDot } from '../types';
import DataRecords from './DataRecords'; import DataRecords from './DataRecords';
import DataSource from './DataSource'; import DataSource from './DataSource';
import EditorModel from '../../editor/model/Editor'; import EditorModel from '../../editor/model/Editor';
@ -135,7 +135,7 @@ export default class DataRecord<T extends DataRecordProps = DataRecordProps> ext
* // Sets 'name' property to 'newValue' * // Sets 'name' property to 'newValue'
*/ */
set<A extends _StringKey<T>>( set<A extends _StringKey<T>>(
attributeName: Partial<T> | A, attributeName: DeepPartialDot<T> | A,
value?: SetOptions | T[A] | undefined, value?: SetOptions | T[A] | undefined,
options?: SetOptions | undefined, options?: SetOptions | undefined,
): this; ): this;

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

@ -31,18 +31,15 @@
import { AddOptions, collectionEvents, CombinedModelConstructorOptions, Model, RemoveOptions } from '../../common'; import { AddOptions, collectionEvents, CombinedModelConstructorOptions, Model, RemoveOptions } from '../../common';
import EditorModel from '../../editor/model/Editor'; import EditorModel from '../../editor/model/Editor';
import { DataSourceProps } from '../types'; import { DataSourceTransformers, DataSourceType, DataSourceProps, RecordPropsType, DataRecordProps } from '../types';
import { DataSourceTransformers, DataSourceType, SingleRecordType } 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';
interface DataSourceOptions extends CombinedModelConstructorOptions<{ em: EditorModel }, DataSource> {} interface DataSourceOptions extends CombinedModelConstructorOptions<{ em: EditorModel }, DataSource> {}
export default class DataSource<DRProps extends DataRecordProps = DataRecordProps> extends Model<
export default class DataSource< DataSourceType<DRProps>
DS extends DataSourceType = DataSourceType, > {
DR extends SingleRecordType<DS['records']> = SingleRecordType<DS['records']>,
> extends Model<DS> {
transformers: DataSourceTransformers; transformers: DataSourceTransformers;
/** /**
@ -56,7 +53,7 @@ export default class DataSource<
return { return {
records: [], records: [],
transformers: {}, transformers: {},
} as unknown as Partial<DS>; } as unknown as DataSourceType<DRProps>;
} }
/** /**
@ -64,23 +61,23 @@ export default class DataSource<
* It sets up the transformers and initializes the collection of records. * It sets up the transformers and initializes the collection of records.
* If the `records` property is not an instance of `DataRecords`, it will be converted into one. * If the `records` property is not an instance of `DataRecords`, it will be converted into one.
* *
* @param {DataSourceProps} props - Properties to initialize the data source. * @param {DataSourceProps<DRProps>} props - Properties to initialize the data source.
* @param {DataSourceOptions} opts - Options to initialize the data source. * @param {DataSourceOptions} opts - Options to initialize the data source.
* @name constructor * @name constructor
*/ */
constructor(props: DataSourceProps<DS>, opts: DataSourceOptions) { constructor(props: DataSourceProps<DRProps>, opts: DataSourceOptions) {
super( super(
{ {
...props, ...props,
records: [], records: [],
} as unknown as DS, } as unknown as DataSourceType<DRProps>,
opts, opts,
); );
const { records, transformers } = props; const { records, transformers } = props;
this.transformers = transformers || {}; this.transformers = transformers || ({} as DataSourceTransformers);
if (!(records instanceof DataRecords)) { if (!(records instanceof DataRecords)) {
this.set({ records: new DataRecords(records!, { dataSource: this }) } as Partial<DS>); this.set({ records: new DataRecords(records!, { dataSource: this }) } as Partial<DataSourceType<DRProps>>);
} }
this.listenTo(this.records, 'add', this.onAdd); this.listenTo(this.records, 'add', this.onAdd);
@ -90,11 +87,11 @@ export default class DataSource<
/** /**
* Retrieves the collection of records associated with this data source. * Retrieves the collection of records associated with this data source.
* *
* @returns {DataRecords} The collection of data records. * @returns {DataRecords<DRProps>} The collection of data records.
* @name records * @name records
*/ */
get records() { get records() {
return this.attributes.records as NonNullable<DS['records']>; return this.attributes.records as NonNullable<DataRecords<DRProps>>;
} }
/** /**
@ -111,23 +108,23 @@ export default class DataSource<
* Handles the `add` event for records in the data source. * Handles the `add` event for records in the data source.
* This method triggers a change event on the newly added record. * This method triggers a change event on the newly added record.
* *
* @param {DataRecord} dr - The data record that was added. * @param {DataRecord<DRProps>} dr - The data record that was added.
* @private * @private
* @name onAdd * @name onAdd
*/ */
onAdd(dr: DataRecord) { onAdd(dr: DataRecord<DRProps>) {
dr.triggerChange(); dr.triggerChange();
} }
/** /**
* Adds a new record to the data source. * Adds a new record to the data source.
* *
* @param {DataRecordProps} record - The properties of the record to add. * @param {DRProps} record - The properties of the record to add.
* @param {AddOptions} [opts] - Options to apply when adding the record. * @param {AddOptions} [opts] - Options to apply when adding the record.
* @returns {DataRecord} The added data record. * @returns {DataRecord} The added data record.
* @name addRecord * @name addRecord
*/ */
addRecord(record: DR, opts?: AddOptions) { addRecord(record: DRProps, opts?: AddOptions) {
return this.records.add(record, opts); return this.records.add(record, opts);
} }
@ -135,18 +132,18 @@ export default class DataSource<
* Retrieves a record from the data source by its ID. * Retrieves a record from the data source by its ID.
* *
* @param {string | number} id - The ID of the record to retrieve. * @param {string | number} id - The ID of the record to retrieve.
* @returns {DataRecord | undefined} The data record, or `undefined` if no record is found with the given ID. * @returns {DataRecord<DRProps> | undefined} The data record, or `undefined` if no record is found with the given ID.
* @name getRecord * @name getRecord
*/ */
getRecord(id: string | number) { getRecord(id: string | number) {
return this.records.get(id) as DR | undefined; return this.records.get(id) as DataRecord | undefined;
} }
/** /**
* Retrieves all records from the data source. * Retrieves all records from the data source.
* Each record is processed with the `getRecord` method to apply any read transformers. * Each record is processed with the `getRecord` method to apply any read transformers.
* *
* @returns {Array<DataRecord | undefined>} An array of data records. * @returns {Array<DataRecord<DRProps> | undefined>} An array of data records.
* @name getRecords * @name getRecords
*/ */
getRecords() { getRecords() {
@ -158,10 +155,10 @@ export default class DataSource<
* *
* @param {string | number} id - The ID of the record to remove. * @param {string | number} id - The ID of the record to remove.
* @param {RemoveOptions} [opts] - Options to apply when removing the record. * @param {RemoveOptions} [opts] - Options to apply when removing the record.
* @returns {DataRecord | undefined} The removed data record, or `undefined` if no record is found with the given ID. * @returns {DataRecord<DRProps> | undefined} The removed data record, or `undefined` if no record is found with the given ID.
* @name removeRecord * @name removeRecord
*/ */
removeRecord(id: string | number, opts?: RemoveOptions): DataRecord | undefined { removeRecord(id: string | number, opts?: RemoveOptions) {
const record = this.getRecord(id); const record = this.getRecord(id);
if (record?.mutable === false && !opts?.dangerously) { if (record?.mutable === false && !opts?.dangerously) {
throw new Error('Cannot remove immutable record'); throw new Error('Cannot remove immutable record');
@ -173,11 +170,11 @@ export default class DataSource<
/** /**
* Replaces the existing records in the data source with a new set of records. * Replaces the existing records in the data source with a new set of records.
* *
* @param {Array<DataRecordProps>} records - An array of data record properties to set. * @param {Array<DRProps>} records - An array of data record properties to set.
* @returns {Array<DataRecord>} An array of the added data records. * @returns {Array<DataRecord>} An array of the added data records.
* @name setRecords * @name setRecords
*/ */
setRecords(records: DR[]) { setRecords(records: DRProps[]) {
this.records.reset([], { silent: true }); this.records.reset([], { silent: true });
records.forEach((record) => { records.forEach((record) => {

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

@ -17,6 +17,8 @@ export interface DataRecordProps extends ObjectAny {
* Specifies if the record is mutable. Defaults to `true`. * Specifies if the record is mutable. Defaults to `true`.
*/ */
mutable?: boolean; mutable?: boolean;
[key: string]: any;
} }
export interface DataVariableListener { export interface DataVariableListener {
@ -40,15 +42,13 @@ interface BaseDataSource {
*/ */
skipFromStorage?: boolean; skipFromStorage?: boolean;
} }
export interface DataSourceType<DR extends DataRecordProps = DataRecordProps> extends BaseDataSource { export interface DataSourceType<DR extends DataRecordProps> extends BaseDataSource {
records: DataRecords<DR>; records: DataRecords<DR>;
} }
export interface DataSourceProps<DS extends DataSourceType = DataSourceType> extends BaseDataSource { export interface DataSourceProps<DR extends DataRecordProps> extends BaseDataSource {
records?: DataRecords<ExtractRecordType<DS>> | DataRecord<ExtractRecordType<DS>>[] | ExtractRecordType<DS>[]; records?: DataRecords<DR> | DataRecord<DR>[] | DR[];
} }
export type ExtractRecordType<T> = T extends { records: DataRecords<infer DR> } ? DR : never; export type RecordPropsType<T> = T extends DataRecord<infer U> ? U : never;
export type SingleRecordType<T> = T extends Collection<infer U> ? U : never;
export interface DataSourceTransformers { export interface DataSourceTransformers {
onRecordSetValue?: (args: { id: string | number; key: string; value: any }) => any; onRecordSetValue?: (args: { id: string | number; key: string; value: any }) => any;
} }
@ -93,3 +93,24 @@ export enum DataSourcesEvents {
all = 'data', all = 'data',
} }
/**{END_EVENTS}*/ /**{END_EVENTS}*/
type DotSeparatedKeys<T> = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? `${K}` | `${K}.${DotSeparatedKeys<T[K]>}`
: `${K}`
: never;
}[keyof T]
: never;
export type DeepPartialDot<T> = {
[P in DotSeparatedKeys<T>]?: P extends `${infer K}.${infer Rest}`
? K extends keyof T
? Rest extends DotSeparatedKeys<T[K]>
? DeepPartialDot<T[K]>[Rest]
: never
: never
: P extends keyof T
? T[P]
: never;
};

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

@ -1,6 +1,7 @@
import DataSourceManager from '../../../src/data_sources'; import DataSourceManager from '../../../src/data_sources';
import { setupTestEditor } from '../../common'; import { setupTestEditor } from '../../common';
import EditorModel from '../../../src/editor/model/Editor'; import EditorModel from '../../../src/editor/model/Editor';
import { DataRecord } from '../../../src';
describe('DataSource Immutability', () => { describe('DataSource Immutability', () => {
let em: EditorModel; let em: EditorModel;
@ -48,9 +49,10 @@ describe('DataSource Immutability', () => {
}); });
test('addRecord creates an immutable record', () => { test('addRecord creates an immutable record', () => {
type RecordType = { id: string; name: string; value: number; mutable: boolean };
const ds = dsm.add({ const ds = dsm.add({
id: 'testDs4', id: 'testDs4',
records: [], records: [] as RecordType[],
}); });
ds.addRecord({ id: 'id1', name: 'Name1', value: 100, mutable: false }); ds.addRecord({ id: 'id1', name: 'Name1', value: 100, mutable: false });
@ -63,7 +65,7 @@ describe('DataSource Immutability', () => {
test('setRecords replaces all records with immutable ones', () => { test('setRecords replaces all records with immutable ones', () => {
const ds = dsm.add({ const ds = dsm.add({
id: 'testDs5', id: 'testDs5',
records: [], records: [{ id: 'id1', name: 'Name1', value: 100, mutable: false }],
}); });
ds.setRecords([ ds.setRecords([

Loading…
Cancel
Save