Skip to content

Commit

Permalink
chore: merge master into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveBunlon authored May 26, 2021
2 parents ca96548 + 43c1162 commit bde6574
Show file tree
Hide file tree
Showing 35 changed files with 1,591 additions and 953 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ module.exports = {
plugins: [
'sonarjs',
],
ignorePatterns: [
'dist/**'
],
env: {
node: true,
},
Expand Down
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,73 @@
## [7.9.3](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.9.2...v7.9.3) (2021-05-25)


### Bug Fixes

* **smart-actions-change-hook:** record is no longer altered and is sent correctly ([#728](https://github.com/ForestAdmin/forest-express-sequelize/issues/728)) ([2ac7af8](https://github.com/ForestAdmin/forest-express-sequelize/commit/2ac7af8105e7d543556ca9b8497557a675e58d91))

## [7.9.2](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.9.1...v7.9.2) (2021-05-21)


### Bug Fixes

* distribution charts using groupby on a relationship throws 403 Forbidden ([#725](https://github.com/ForestAdmin/forest-express-sequelize/issues/725)) ([30e6744](https://github.com/ForestAdmin/forest-express-sequelize/commit/30e6744b02a9f95d44bb2265a187c4c3cf0a4027))

## [7.9.1](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.9.0...v7.9.1) (2021-05-12)


### Bug Fixes

* regression when fetching has-many and not selecting any fields on a hasone/belongsto relation ([#720](https://github.com/ForestAdmin/forest-express-sequelize/issues/720)) ([74ed623](https://github.com/ForestAdmin/forest-express-sequelize/commit/74ed6230b35c41e264854fafcb4c60667ff7ac99))

# [7.9.0](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.8.0...v7.9.0) (2021-05-11)


### Features

* **filters:** add support for the \`model.field IN array\` filter condition ([#719](https://github.com/ForestAdmin/forest-express-sequelize/issues/719)) ([5f58457](https://github.com/ForestAdmin/forest-express-sequelize/commit/5f5845758d8e93f493d82cf796ba1de9b058b9ac))

# [7.8.0](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.7.0...v7.8.0) (2021-05-06)


### Features

* add support for belongsTo and hasOne filters on related data ([#715](https://github.com/ForestAdmin/forest-express-sequelize/issues/715)) ([2bc769e](https://github.com/ForestAdmin/forest-express-sequelize/commit/2bc769e97f7c2807a6af3a8f68aba4be698bec77))

# [7.7.0](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.6.4...v7.7.0) (2021-04-28)


### Features

* support yarn 2 plug n play install mode ([#698](https://github.com/ForestAdmin/forest-express-sequelize/issues/698)) ([64b5734](https://github.com/ForestAdmin/forest-express-sequelize/commit/64b5734af0e6da5d2a3f73ddb44966162aa5a317))

## [7.6.4](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.6.3...v7.6.4) (2021-04-27)


### Bug Fixes

* **schema:** do not remove primary key fields from the schema when tables have foreign keys that are primary keys ([8844fb5](https://github.com/ForestAdmin/forest-express-sequelize/commit/8844fb5631ce6335dc66d7ee33433b12e2618611))

## [7.6.3](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.6.2...v7.6.3) (2021-04-22)


### Bug Fixes

* **search:** enable to search for a big integer in an ID field ([#695](https://github.com/ForestAdmin/forest-express-sequelize/issues/695)) ([9f8132c](https://github.com/ForestAdmin/forest-express-sequelize/commit/9f8132c3c920b4a29178fd35cf4dc56179cc8c8b))

## [7.6.2](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.6.1...v7.6.2) (2021-04-22)


### Bug Fixes

* **search:** searching for a big int value should not break if there is a regular integer field ([#694](https://github.com/ForestAdmin/forest-express-sequelize/issues/694)) ([af076ad](https://github.com/ForestAdmin/forest-express-sequelize/commit/af076ad208b0bfb9e186568b13ea488d0a2c545f))

## [7.6.1](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.6.0...v7.6.1) (2021-04-21)


### Bug Fixes

* **security:** patch ssri dependency vulnerability ([#690](https://github.com/ForestAdmin/forest-express-sequelize/issues/690)) ([6b0770d](https://github.com/ForestAdmin/forest-express-sequelize/commit/6b0770d91ffdecff0a78d5c623b3a28d40c17744))

# [8.0.0-beta.2](https://github.com/ForestAdmin/forest-express-sequelize/compare/v8.0.0-beta.1...v8.0.0-beta.2) (2021-04-16)


Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@babel/runtime": "7.10.1",
"bluebird": "2.9.25",
"core-js": "3.6.5",
"forest-express": "8.3.1",
"forest-express": "8.5.2",
"http-errors": "1.6.1",
"lodash": "4.17.21",
"moment": "2.19.4",
Expand Down Expand Up @@ -67,8 +67,8 @@
"simple-git": "1.65.0"
},
"scripts": {
"build": "babel src --out-dir dist && echo '\n\\033[0;34m[+] \\033[0;32mBuild done\\033[0m'",
"build:watch": "onchange 'src/**/*.js' 'node_modules/forest-express/dist/*' --no-exclude -i -- yarn build",
"build": "babel src --out-dir dist",
"build:watch": "onchange 'src/**/*.js' 'node_modules/forest-express/dist/*' --no-exclude -i -- babel --source-maps inline --out-dir dist src",
"lint": "./node_modules/eslint/bin/eslint.js .eslint-bin src test",
"test": "jest",
"test:coverage": "jest --coverage"
Expand Down
14 changes: 7 additions & 7 deletions src/adapters/sequelize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const P = require('bluebird');
const Interface = require('forest-express');
const ApimapFieldBuilder = require('../services/apimap-field-builder');
const ApimapFieldTypeDetector = require('../services/apimap-field-type-detector');
const primaryKeyIsForeignKey = require('../utils/primaryKey-is-ForeignKey');
const isPrimaryKeyAForeignKey = require('../utils/is-primary-key-a-foreign-key');

module.exports = (model, opts) => {
const fields = [];
Expand Down Expand Up @@ -84,17 +84,17 @@ module.exports = (model, opts) => {
}

Object.entries(model.associations).forEach(([, association]) => {
const pkIsFk = primaryKeyIsForeignKey(association);
if (pkIsFk) {
const fk = fields.find((field) => field.reference === `${association.associationAccessor}.${association.foreignKey}`);
if (fk) {
fk.foreignAndPrimaryKey = true;
const primaryKeyIsAForeignKey = isPrimaryKeyAForeignKey(association);
if (primaryKeyIsAForeignKey) {
const FieldWithForeignKey = fields.find((field) => field.reference === `${association.associationAccessor}.${association.foreignKey}`);
if (FieldWithForeignKey) {
FieldWithForeignKey.foreignAndPrimaryKey = true;
}
}
});

_.remove(fields, (field) =>
_.includes(fieldNamesToExclude, field.columnName));
_.includes(fieldNamesToExclude, field.columnName) && !field.isPrimaryKey);

return {
name: model.name,
Expand Down
54 changes: 0 additions & 54 deletions src/services/composite-keys-manager.js

This file was deleted.

2 changes: 2 additions & 0 deletions src/services/filters-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ function FiltersParser(modelSchema, timezone, options) {
return { [this.OPERATORS.EQ]: value };
case 'includes_all':
return { [this.OPERATORS.CONTAINS]: value };
case 'in':
return { [this.OPERATORS.IN]: value };
default:
throw new NoMatchingOperatorError();
}
Expand Down
149 changes: 36 additions & 113 deletions src/services/has-many-getter.js
Original file line number Diff line number Diff line change
@@ -1,133 +1,56 @@
const _ = require('lodash');
const P = require('bluebird');
const Interface = require('forest-express');
const orm = require('../utils/orm');
const QueryBuilder = require('./query-builder');
const SearchBuilder = require('./search-builder');
const FiltersParser = require('./filters-parser');
const CompositeKeysManager = require('./composite-keys-manager');
const extractRequestedFields = require('./requested-fields-extractor');
const Operators = require('../utils/operators');
import { pick } from 'lodash';
import Operators from '../utils/operators';
import QueryUtils from '../utils/query';
import PrimaryKeysManager from './primary-keys-manager';
import ResourcesGetter from './resources-getter';

class HasManyGetter {
constructor(model, association, options, params) {
this.model = model;
this.association = association;
this.params = params;
this.queryBuilder = new QueryBuilder(model, options, params);
this.schema = Interface.Schemas.schemas[association.name];
[this.primaryKeyModel] = _.keys(model.primaryKeys);
this.operators = Operators.getInstance(options);
this.filtersParser = new FiltersParser(this.schema, params.timezone, options);
this.fieldNamesRequested = extractRequestedFields(
params.fields, association, Interface.Schemas.schemas,
);
this.searchBuilder = new SearchBuilder(
association,
options,
params,
this.fieldNamesRequested,
);
}

async buildWhereConditions({ associationName, search, filters }) {
const { AND } = this.operators;
const where = { [AND]: [] };

if (search) {
const searchCondition = this.searchBuilder.perform(associationName);
where[AND].push(searchCondition);
}


if (filters) {
const formattedFilters = await this.filtersParser.perform(filters);
where[AND].push(formattedFilters);
}
class HasManyGetter extends ResourcesGetter {
constructor(model, association, lianaOptions, params) {
super(association, lianaOptions, params);

return where;
this._parentModel = model.unscoped();
}

async findQuery(queryOptions) {
if (!queryOptions) { queryOptions = {}; }
const { associationName, recordId } = this.params;

const where = await this.buildWhereConditions(this.params);
const include = this.queryBuilder.getIncludes(this.association, this.fieldNamesRequested);

const record = await orm.findRecord(this.model, recordId, {
order: queryOptions.order,
subQuery: false,
offset: queryOptions.offset,
limit: queryOptions.limit,
// NOTICE: by default, all fields from the parent model
// are retrieved, which can cause performance issues,
// whereas we are only requesting the child model here
// and we don't need the parent's attributes
attributes: [],
include: [{
model: this.association,
as: associationName,
scope: false,
required: false,
where,
include,
}],
});

return (record && record[associationName]) || [];
async _getRecords() {
const options = await this._buildQueryOptions();
const record = await this._parentModel.findOne(options);
return (record && record[this._params.associationName]) || [];
}

async count() {
const { associationName, recordId } = this.params;
const where = await this.buildWhereConditions(this.params);
const include = this.queryBuilder.getIncludes(this.association, this.fieldNamesRequested);
const options = await this._buildQueryOptions({ forCount: true });
return this._parentModel.count(options);
}

return this.model.count({
where: { [this.primaryKeyModel]: recordId },
async _buildQueryOptions(buildOptions = {}) {
const operators = Operators.getInstance({ Sequelize: this._parentModel.sequelize.constructor });
const { associationName, recordId } = this._params;
const [model, options] = await super._buildQueryOptions({
...buildOptions, tableAlias: associationName,
});

const parentOptions = QueryUtils.bubbleWheresInPlace(operators, {
where: new PrimaryKeysManager(this._parentModel).getRecordsConditions([recordId]),
include: [{
model: this.association,
model,
as: associationName,
where,
required: true,
scope: false,
include,
required: !!buildOptions.forCount, // Why?
...pick(options, ['where', 'include']),
}],
});
}

async getRecords() {
const { associationName } = this.params;

const queryOptions = {
order: this.queryBuilder.getOrder(associationName, this.schema),
offset: this.queryBuilder.getSkip(),
limit: this.queryBuilder.getLimit(),
};

const records = await this.findQuery(queryOptions);
return P.map(records, (record) => {
if (this.schema.isCompositePrimary) {
record.forestCompositePrimary = new CompositeKeysManager(
this.association, this.schema, record,
)
.createCompositePrimary();
}

return record;
});
}

async perform() {
const records = await this.getRecords();

let fieldsSearched = null;
if (!buildOptions.forCount) {
parentOptions.subQuery = false; // Why?
parentOptions.attributes = []; // Don't fetch parent attributes (perf)
parentOptions.offset = options.offset;
parentOptions.limit = options.limit;

if (this.params.search) {
fieldsSearched = this.searchBuilder.getFieldsSearched();
// Order with the relation (https://github.com/sequelize/sequelize/issues/4553)
parentOptions.order = (options.order || []).map((fields) => [associationName, ...fields]);
}

return [records, fieldsSearched];
return parentOptions;
}
}

Expand Down
Loading

0 comments on commit bde6574

Please sign in to comment.