Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(datasource-replica): add replica datasource #783

Merged
merged 71 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
cc464d8
feat(datasource-replica): add replica datasource
SkyHuss Aug 1, 2023
c4f826e
fix: fix rebase
SkyHuss Aug 1, 2023
24b291a
test: skip a test
SkyHuss Aug 1, 2023
e674877
test: skip a test
SkyHuss Aug 1, 2023
be0325c
test: add git test workflow for datasouce replica
SkyHuss Aug 1, 2023
cb92472
test: remove test for ci
SkyHuss Aug 1, 2023
2ad7405
test: fix lint
SkyHuss Aug 1, 2023
6b6a950
test: fix test
SkyHuss Aug 1, 2023
497060f
fix: split hubspot datasource
SkyHuss Aug 2, 2023
9976de9
test: add test on collection name getter
SkyHuss Aug 2, 2023
ba37334
test: add flatten manual mode test
SkyHuss Aug 2, 2023
eda88d9
fix: review
SkyHuss Aug 3, 2023
43fe9c4
test: add manual flatten option test
SkyHuss Aug 3, 2023
cb76b8e
fix: comment console log
SkyHuss Aug 3, 2023
f28d9ef
fix: remove console log
SkyHuss Aug 3, 2023
abb4e78
fix: remove log
SkyHuss Aug 3, 2023
e516e05
test: improve coverage with flattenOptions
SkyHuss Aug 3, 2023
bf03cd0
fix: remove log
SkyHuss Aug 3, 2023
75580ad
test: add queueDelta test on create, update and delete
SkyHuss Aug 4, 2023
07d0d1e
test: a relaxed name test
SkyHuss Aug 4, 2023
a1b01ab
fix: remove collection name getter
SkyHuss Aug 4, 2023
263972a
test: improve flattener coverage
SkyHuss Aug 4, 2023
c56858f
test: remove log
SkyHuss Aug 4, 2023
7a27d20
fix: fix a lot of ts issues
jeffladiray Aug 4, 2023
957b519
fix: fix ts not building
jeffladiray Aug 4, 2023
a1c74ff
fix: push strategy
arnaud-moncel Aug 7, 2023
c9aa463
test: add more tests on flattener
jeffladiray Aug 7, 2023
caa2a28
test: add more tests
jeffladiray Aug 7, 2023
2425c37
test: add tests on schedule
arnaud-moncel Aug 7, 2023
6aa7537
chore: fix lint
arnaud-moncel Aug 7, 2023
bb54bb6
test: fix several tests
SkyHuss Aug 8, 2023
e8456a9
fix: remove outdated comments
SkyHuss Aug 8, 2023
0cce161
test: add more tests
jeffladiray Aug 8, 2023
bbe56af
chore: remove useless checkCollection function
jeffladiray Aug 8, 2023
58614b3
fix: review fix
jeffladiray Aug 8, 2023
4690ef1
test: fix serializer test
SkyHuss Aug 8, 2023
a204614
fix: handle applyDump in a transaction
SkyHuss Aug 8, 2023
2c56c5a
fix: review fixes
jeffladiray Aug 8, 2023
da08812
fix: review fixes
jeffladiray Aug 8, 2023
6f969ff
fix: handle pullDelta in a transaction
SkyHuss Aug 8, 2023
f01f254
fix: remove useless parameter
SkyHuss Aug 9, 2023
6c79543
test: add test on replica sequelize
SkyHuss Aug 9, 2023
7c57e35
test: remove .only
SkyHuss Aug 9, 2023
0d45683
test: remove .only
SkyHuss Aug 9, 2023
21c051d
test: add test on logger
SkyHuss Aug 9, 2023
f0e1e88
test: add enum case to convertType test
SkyHuss Aug 9, 2023
310cf14
test: add dataonly case to convertType test
SkyHuss Aug 9, 2023
5e97f5c
test: add unit test for AnalysisPassThough (test)
SkyHuss Aug 9, 2023
a86efae
test: add pullDelta schedule test
SkyHuss Aug 10, 2023
a4e830e
test: final test
SkyHuss Aug 10, 2023
4e6ab6b
test: remove .only
SkyHuss Aug 10, 2023
27c851e
test: remove date
SkyHuss Aug 10, 2023
21b59ee
test: final test
SkyHuss Aug 10, 2023
e3f9011
test: remove .only
SkyHuss Aug 10, 2023
fcbfe03
test: json test
SkyHuss Aug 10, 2023
e460279
test: complete delta cache target test
SkyHuss Aug 10, 2023
993b32d
fix: review
SkyHuss Aug 10, 2023
ab15ebc
fix: review
SkyHuss Aug 10, 2023
670420a
fix: review imporve data naming
SkyHuss Aug 10, 2023
3b83c34
fix: analysis passthrought
SkyHuss Aug 10, 2023
24cc8a8
test: review
SkyHuss Aug 10, 2023
a30a561
test: reorder schema flatten test
SkyHuss Aug 10, 2023
52b1db9
test: re-improve coverage
jeffladiray Aug 10, 2023
d014766
test: improve coverage
jeffladiray Aug 10, 2023
c43ab7c
test: add more tests
jeffladiray Aug 10, 2023
45bf16a
fix: fix
jeffladiray Aug 10, 2023
4857f71
test: add tests on previousDeltaState
arnaud-moncel Aug 11, 2023
31b98ca
test: improve flattener test
SkyHuss Aug 11, 2023
5585d44
test: rename attribute for sort test
SkyHuss Aug 11, 2023
a4865f7
fix: remove useless if in write actions
SkyHuss Aug 11, 2023
a27b8d0
fix: cancel ifs deletion
SkyHuss Aug 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
- datasource-customizer
- datasource-dummy
- datasource-mongoose
- datasource-replica
- datasource-sequelize
- datasource-sql
- datasource-toolkit
Expand Down Expand Up @@ -126,6 +127,7 @@ jobs:
${{github.workspace}}/reports/datasource-customizer/clover.xml:clover
${{github.workspace}}/reports/datasource-dummy/clover.xml:clover
${{github.workspace}}/reports/datasource-mongoose/clover.xml:clover
${{github.workspace}}/reports/datasource-replica/clover.xml:clover
${{github.workspace}}/reports/datasource-sequelize/clover.xml:clover
${{github.workspace}}/reports/datasource-sql/clover.xml:clover
${{github.workspace}}/reports/datasource-toolkit/clover.xml:clover
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.sqlite
.DS_Store
coverage
dist
Expand Down
2 changes: 1 addition & 1 deletion packages/_example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@
"devDependencies": {
"nodemon": "^2.0.20"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Caller,
Collection,
CompositeId,
Deferred,
Projection,
ProjectionValidator,
RecordData,
Expand All @@ -12,19 +13,6 @@ import {
import CollectionCustomizationContext from '../../../context/collection-context';
import { TCollectionName, TFieldName, TFilter, TRow, TSchema } from '../../../templates';

class Deferred<T> {
promise: Promise<T>;
resolve: (result: T) => void;
reject: (error: Error) => void;

constructor() {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
});
}
}

export default class ActionContext<
S extends TSchema = TSchema,
N extends TCollectionName<S> = TCollectionName<S>,
Expand Down
674 changes: 674 additions & 0 deletions packages/datasource-replica/LICENSE

Large diffs are not rendered by default.

Empty file.
8 changes: 8 additions & 0 deletions packages/datasource-replica/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable import/no-relative-packages */
import jestConfig from '../../jest.config';

export default {
...jestConfig,
collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
testMatch: ['<rootDir>/test/**/*.test.ts'],
};
33 changes: 33 additions & 0 deletions packages/datasource-replica/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@forestadmin/datasource-replica",
"version": "1.0.0",
"main": "dist/index.js",
"license": "GPL-3.0",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ForestAdmin/agent-nodejs.git",
"directory": "packages/datasource-replica"
},
"files": [
"dist/src/**/*.js",
"dist/src/**/*.d.ts"
],
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"clean": "rm -rf coverage dist",
"lint": "eslint src test",
"test": "jest"
},
"dependencies": {
"@forestadmin/datasource-customizer": "^1.9.1",
"@forestadmin/datasource-sequelize": "^1.3.1",
"@forestadmin/datasource-sql": "1.7.0",
"@forestadmin/datasource-toolkit": "^1.5.0",
"croner": "^6.0.6",
"sequelize": "^6.28.0"
}
}
123 changes: 123 additions & 0 deletions packages/datasource-replica/src/cache-interface/collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
TAggregateResult,
TAggregation,
TFilter,
TPaginatedFilter,
TRow,
} from '@forestadmin/datasource-customizer';
import {
Aggregation,
Caller,
Collection,
ConditionTreeFactory,
Filter,
Page,
PaginatedFilter,
Projection,
Sort,
} from '@forestadmin/datasource-toolkit';

export default class CacheCollectionInterface {
private collection: Collection;
private caller: Caller;

constructor(collection: Collection, caller: Caller) {
this.caller = caller;
this.collection = collection;
}

/**
* List multiple records
* @param filter the filter used to select the records to list
* @param projection an array of fields name representing the data to select
* @example
* .list({
* conditionTree: {
* aggregator: 'And',
* conditions: [{
* field: 'amountInEur',
* operator: 'GreaterThan',
* value: 1000
* }, {
* field: 'description',
* operator: 'Contains',
* value: 'Refund',
* }],
* page: { limit: 10, skip: 0 },
* sort: [{ field: 'id', ascending: true }]
* }
* }, ['id', 'amountInEur', 'description']);
*/
list(filter: TPaginatedFilter, projection: string[]): Promise<TRow[]> {
const filterInstance = this.buildPaginatedFilter(filter);
const projectionInstance = this.buildProjection(projection);
const rows = this.collection.list(this.caller, filterInstance, projectionInstance);

return rows as Promise<TRow[]>;
}

/**
* Aggregate a list of records
* @param filter the filter used to list the records to aggregate
* @param aggregation the aggregation to apply
* @param limit the maximum number of result to return
* @example
* .aggregate({
* conditionTree: {
* field: "user:company:id",
* operator: "In",
* value: records.map((r) => r.id),
* },
* }, {
* operation: "Sum",
* field: "amountInEur",
* groups: [{ field: "user:company:id" }],
* }, 10);
*/
async aggregate(
filter: TFilter,
aggregation: TAggregation,
limit?: number,
): Promise<TAggregateResult[]> {
const filterInstance = this.buildFilter(filter);
const aggregationInstance = this.buildAggregation(aggregation);
const result = await this.collection.aggregate(
this.caller,
filterInstance,
aggregationInstance,
limit,
);

return result as TAggregateResult[];
}

private buildFilter(filter: TFilter): Filter {
return filter
? new Filter({
...filter,
conditionTree: filter.conditionTree
? ConditionTreeFactory.fromPlainObject(filter.conditionTree)
: undefined,
})
: null;
}

private buildPaginatedFilter(filter: TPaginatedFilter): PaginatedFilter {
return new PaginatedFilter({
...filter,
conditionTree: filter?.conditionTree
? ConditionTreeFactory.fromPlainObject(filter.conditionTree)
: undefined,
sort: filter.sort ? new Sort(...filter.sort) : undefined,
page: filter.page ? new Page(filter.page.skip, filter.page.limit) : undefined,
});
}

private buildProjection(projection: string[]): Projection {
return new Projection(...projection);
}

private buildAggregation(aggregation: TAggregation): Aggregation {
return new Aggregation(aggregation);
}
}
32 changes: 32 additions & 0 deletions packages/datasource-replica/src/cache-interface/datasource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Caller, DataSource } from '@forestadmin/datasource-toolkit';

import CacheCollectionInterface from './collection';

/** DataSource wrapper which accepts plain objects in all methods */
export default class CacheDataSourceInterface {
private dataSource: DataSource;
private caller: Caller = {
id: 0,
email: 'datasource-replica@forestadmin.com',
firstName: 'Datasource',
lastName: 'Replica',
renderingId: 0,
role: 'system',
tags: {},
team: 'system',
timezone: 'UTC',
requestId: '',
};

constructor(dataSource: DataSource) {
this.dataSource = dataSource;
}

/**
* Get a collection from a datasource
* @param name the name of the collection
*/
getCollection(name: string): CacheCollectionInterface {
return new CacheCollectionInterface(this.dataSource.getCollection(name), this.caller);
}
}
42 changes: 42 additions & 0 deletions packages/datasource-replica/src/decorators/schema/collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { CollectionDecorator, CollectionSchema, ColumnType } from '@forestadmin/datasource-toolkit';

import SchemaDataSourceDecorator from './data-source';
import { Field, LeafField, isLeafField } from '../../types';

export default class SchemaCollectionDecorator extends CollectionDecorator {
override dataSource: SchemaDataSourceDecorator;

protected override refineSchema(subSchema: CollectionSchema): CollectionSchema {
const baseFields = this.dataSource.getFields(this.name);
const newSchema = { ...subSchema, fields: { ...subSchema.fields } };

for (const [key, field] of Object.entries(newSchema.fields)) {
if (field.type === 'Column') {
const baseField = baseFields[key] as LeafField;
newSchema.fields[key] = {
...field,
columnType: this.computeType(baseField),
isReadOnly: baseField.isReadOnly,
defaultValue: baseField.defaultValue,
validation: baseField.validation,
};
}
}

return newSchema;
}

private computeType(field: Field): ColumnType {
if (isLeafField(field)) {
return field.type === 'Integer' ? 'Number' : field.type;
}

if (Array.isArray(field)) {
return [this.computeType(field[0])];
}

const entries = Object.entries(field).map(([key, value]) => [key, this.computeType(value)]);

return Object.fromEntries(entries);
}
}
18 changes: 18 additions & 0 deletions packages/datasource-replica/src/decorators/schema/data-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DataSource, DataSourceDecorator } from '@forestadmin/datasource-toolkit';

import SchemaCollectionDecorator from './collection';
import { CollectionReplicaSchema, ResolvedOptions } from '../../types';

export default class SchemaDataSourceDecorator extends DataSourceDecorator<SchemaCollectionDecorator> {
private readonly flatSchema: CollectionReplicaSchema[];

constructor(childDataSource: DataSource, options: ResolvedOptions) {
super(childDataSource, SchemaCollectionDecorator);

this.flatSchema = options.flattenSchema;
}

getFields(name: string): CollectionReplicaSchema['fields'] {
return this.flatSchema.find(c => c.name === name).fields;
}
}
Loading
Loading