Skip to content

Commit 52fe5ad

Browse files
author
Guillaume Gautreau
committed
chore: merge origin/master
2 parents e4213d7 + 208f230 commit 52fe5ad

File tree

10 files changed

+236
-6
lines changed

10 files changed

+236
-6
lines changed

.github/workflows/build.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ jobs:
3939
needs: [lint]
4040
steps:
4141
- uses: actions/checkout@v2
42-
with:
43-
fetch-depth: 0
4442
- uses: actions/setup-node@v1
4543
- name: Cache node_modules
4644
uses: actions/cache@v2

CHANGELOG.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
1-
# [7.0.0-beta.10](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.0.0-beta.9...v7.0.0-beta.10) (2021-02-09)
1+
## [6.7.10](https://github.com/ForestAdmin/forest-express-sequelize/compare/v6.7.9...v6.7.10) (2021-02-22)
2+
3+
4+
### Bug Fixes
5+
6+
* support foreign and primary key column ([#630](https://github.com/ForestAdmin/forest-express-sequelize/issues/630)) ([2a289b8](https://github.com/ForestAdmin/forest-express-sequelize/commit/2a289b805727d51d112719ee29af28b7745bff73))
7+
8+
## [6.7.9](https://github.com/ForestAdmin/forest-express-sequelize/compare/v6.7.8...v6.7.9) (2021-02-22)
9+
10+
11+
### Bug Fixes
12+
13+
* fix record creation with unconventional pk field acting as a fk ([#598](https://github.com/ForestAdmin/forest-express-sequelize/issues/598)) ([d3779b7](https://github.com/ForestAdmin/forest-express-sequelize/commit/d3779b75f553830574c51850dfdec5c6ab3eabc6))
14+
15+
## [6.7.8](https://github.com/ForestAdmin/forest-express-sequelize/compare/v6.7.7...v6.7.8) (2021-02-19)
16+
217

18+
### Bug Fixes
19+
20+
* **search:** don't convert float values to bigint ([75c1517](https://github.com/ForestAdmin/forest-express-sequelize/commit/75c151720bb9c260272325f1f2c088b0c9050ac7))
21+
* **search:** handle large numbers in search queries ([c0c1c70](https://github.com/ForestAdmin/forest-express-sequelize/commit/c0c1c70686ecdaab9c9e6bbf432f44a414cf8c2e))
22+
* **search:** handle large numbers in search queries ([#621](https://github.com/ForestAdmin/forest-express-sequelize/issues/621)) ([ec6ab89](https://github.com/ForestAdmin/forest-express-sequelize/commit/ec6ab8989cde26af379f814fa7ee4bd28a1cddd6))
23+
* **search:** handle tables that contain floats and bigints ([7ac2fe1](https://github.com/ForestAdmin/forest-express-sequelize/commit/7ac2fe152ae01af98484d88e8c98c612bcb50573))
24+
* **search:** revert changes when numbers are below MAX_SAFE_INTEGER ([1d95021](https://github.com/ForestAdmin/forest-express-sequelize/commit/1d95021f55a64a519d71c301ceb810196abfdd34))
25+
26+
# [7.0.0-beta.10](https://github.com/ForestAdmin/forest-express-sequelize/compare/v7.0.0-beta.9...v7.0.0-beta.10) (2021-02-09)
327

428
### Bug Fixes
529

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Forest Admin in Nodejs (Express.js & Sequelize)
2+
23
[![npm package](https://badge.fury.io/js/forest-express-sequelize.svg)](https://badge.fury.io/js/forest-express-sequelize)
3-
[![CI status](https://github.com/ForestAdmin/forest-express-sequelize/workflows/.github/workflows/build.yml/badge.svg)](https://github.com/ForestAdmin/forest-express-sequelize/actions)
4+
[![CI status](https://github.com/ForestAdmin/forest-express-sequelize/workflows/Build,%20Test%20and%20Deploy/badge.svg?branch=master)](https://github.com/ForestAdmin/forest-express-sequelize/actions)
45
[![Test Coverage](https://api.codeclimate.com/v1/badges/42d6d0fce013a6b96ae2/test_coverage)](https://codeclimate.com/github/ForestAdmin/forest-express-sequelize/test_coverage)
56
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
67

@@ -9,6 +10,7 @@ Forest Admin provides an off-the-shelf administration panel based on a highly-ex
910
This project has been designed with scalability in mind to fit requirements from small projects to mature companies.
1011

1112
## Who Uses Forest Admin
13+
1214
- [Apartmentlist](https://www.apartmentlist.com)
1315
- [Carbon Health](https://carbonhealth.com)
1416
- [Ebanx](https://www.ebanx.com)
@@ -22,9 +24,11 @@ This project has been designed with scalability in mind to fit requirements from
2224
- And hundreds more…
2325

2426
## Getting started
27+
2528
[https://docs.forestadmin.com/documentation/getting-started/installation](https://docs.forestadmin.com/documentation/getting-started/installation)
2629

2730
## Documentation
31+
2832
[https://docs.forestadmin.com/documentation/](https://docs.forestadmin.com/documentation/)
2933

3034
## How it works
@@ -34,6 +38,7 @@ This project has been designed with scalability in mind to fit requirements from
3438
</p>
3539

3640
Forest Admin consists of two components:
41+
3742
- The Admin Frontend is the user interface where you'll manage your data and configuration.
3843
- The Admin Backend API hosted on your servers where you can find and extend your data models and all the business logic (routes, actions, …) related to your admin panel.
3944

@@ -45,27 +50,31 @@ user and your application data through the Admin API.
4550
## Features
4651

4752
### CRUD
53+
4854
All of your CRUD operations are natively supported. The API automatically
4955
supports your data models' validation and allows you to easily extend or
5056
override any API routes' with your very own custom logic.
5157

5258
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-CRUD.jpg" alt="CRUD">
5359

5460
### Search & Filters
61+
5562
Forest Admin has a built-in search allowing you to run basic queries to
5663
retrieve your application's data. Set advanced filters based on fields and
5764
relationships to handle complex search use cases.
5865

5966
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Search.jpg" alt="Search and Filters">
6067

6168
### Sorting & Pagination
69+
6270
Sorting and pagination features are natively handled by the Admin API. We're
6371
continuously optimizing how queries are run in order to display results faster
6472
and reduce the load of your servers.
6573

6674
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Sorting.jpg" alt="Sorting and Pagination">
6775

6876
### Custom action
77+
6978
A custom action is a button which allows you to trigger an API call to execute
7079
a custom logic. With virtually no limitations, you can extend the way you
7180
manipulate data and trigger actions (e.g. refund a customer, apply a coupon,
@@ -74,42 +83,49 @@ ban a user, etc.)
7483
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Custom.jpg" alt="Sorting and Pagination">
7584

7685
### Export
86+
7787
Sometimes you need to export your data to a good old fashioned CSV. Yes, we
7888
know this can come in handy sometimes :-)
7989

8090
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Export.jpg" alt="Export">
8191

8292
### Segments
93+
8394
Get in app access to a subset of your application data by doing a basic search
8495
or typing an SQL query or implementing an API route.
8596

8697
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Segments.jpg" alt="Segments">
8798

8899
### Dashboards
100+
89101
Forest Admin is able to tap into your actual data to chart out your metrics
90102
using a simple UI panel, a SQL query or a custom API call.
91103

92104
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Dashboard.jpg" alt="Dashboard">
93105

94106
### WYSIWYG
107+
95108
The WYSIWYG interface saves you a tremendous amount of frontend development
96109
time using drag'n'drop as well as advanced widgets to build customizable views.
97110

98111
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Wysiwyg.jpg" alt="WYSIWYG">
99112

100113
### Custom HTML/JS/CSS
114+
101115
Code your own views using JS, HTML, and CSS to display your application data in
102116
a more appropriate way (e.g. Kanban, Map, Calendar, Gallery, etc.).
103117

104118
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-CustomHTML.jpg" alt="Custom views">
105119

106120
### Team-based permissions
121+
107122
Without any lines of code, manage directly from the UI who has access or can
108123
act on which data using a team-based permission system.
109124

110125
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Teams.jpg" alt="Team based permissions">
111126

112127
### Third-party integrations
128+
113129
Leverage data from third-party services by reconciling it with your
114130
application’s data and providing it directly to your Admin Panel. All your
115131
actions can be performed at the same place, bringing additional intelligence to
@@ -118,12 +134,14 @@ your Admin Panel and ensuring consistency.
118134
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-3rdParty.jpg" alt="Third-party integrations">
119135

120136
### Notes & Comments
137+
121138
Assign your teammates to specific tasks, leave a note or simply comment a
122139
record, thereby simplifying collaboration all across your organization.
123140

124141
<img src="https://www.forestadmin.com/public/img/illustrations-dev/screens/ForestWebsite20-Notes.jpg" alt="Notes and Comments">
125142

126143
### Activity logs
144+
127145
Monitor each action executed and follow the trail of modification on any data
128146
with an extensive activity log system.
129147

src/adapters/sequelize.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const P = require('bluebird');
33
const Interface = require('forest-express');
44
const ApimapFieldBuilder = require('../services/apimap-field-builder');
55
const ApimapFieldTypeDetector = require('../services/apimap-field-type-detector');
6+
const primaryKeyIsForeignKey = require('../utils/primaryKey-is-ForeignKey');
67

78
module.exports = (model, opts) => {
89
const fields = [];
@@ -82,8 +83,16 @@ module.exports = (model, opts) => {
8283
idField = 'forestCompositePrimary';
8384
}
8485

86+
Object.entries(model.associations).forEach(([, association]) => {
87+
const pkIsFk = primaryKeyIsForeignKey(association);
88+
if (pkIsFk) {
89+
const fk = fields.find((field) => field.reference === `${association.associationAccessor}.${association.foreignKey}`);
90+
fk.foreignAndPrimaryKey = true;
91+
}
92+
});
93+
8594
_.remove(fields, (field) =>
86-
_.includes(fieldNamesToExclude, field.columnName) && !field.primaryKey);
95+
_.includes(fieldNamesToExclude, field.columnName));
8796

8897
return {
8998
name: model.name,

src/services/resource-creator.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { ErrorHTTP422 } = require('./errors');
55
const ResourceGetter = require('./resource-getter');
66
const CompositeKeysManager = require('./composite-keys-manager');
77
const associationRecord = require('../utils/association-record');
8+
const primaryKeyIsForeignKey = require('../utils/primaryKey-is-ForeignKey');
89

910
class ResourceCreator {
1011
constructor(model, params) {
@@ -28,6 +29,10 @@ class ResourceCreator {
2829
if (association.associationType === 'BelongsTo') {
2930
const setterName = `set${_.upperFirst(name)}`;
3031
const targetKey = await this._getTargetKey(name, association);
32+
const pkIsFk = primaryKeyIsForeignKey(association);
33+
if (pkIsFk) {
34+
record[association.source.primaryKeyAttribute] = this.params[name];
35+
}
3136
return record[setterName](targetKey, { save: false });
3237
}
3338
return null;

src/services/search-builder.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,15 @@ function SearchBuilder(model, opts, params, fieldNamesRequested) {
148148
pushCondition(condition, columnName);
149149
}
150150
} else if (field.type === 'Number') {
151-
const value = Number(params.search);
151+
let value = Number(params.search);
152+
152153
if (!Number.isNaN(value)) {
154+
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
155+
// NOTE: Numbers higher than MAX_SAFE_INTEGER need to be handled as
156+
// strings to circumvent precision problems
157+
value = params.search;
158+
}
159+
153160
condition[field.field] = value;
154161
pushCondition(condition, field.field);
155162
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = (association) =>
2+
Object.values(association.source.rawAttributes).filter((attr) =>
3+
attr.field === association.source.primaryKeyField).length > 1;

test/adapters/sequelize.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,36 @@ function getField(schema, name) {
2525
},
2626
});
2727

28+
models.customer = sequelize.define('customer', {
29+
name: { type: Sequelize.STRING },
30+
});
31+
32+
models.picture = sequelize.define('picture', {
33+
name: { type: Sequelize.STRING },
34+
customerId: {
35+
type: Sequelize.INTEGER,
36+
primaryKey: true,
37+
allowNull: false,
38+
},
39+
}, {
40+
underscored: true,
41+
});
42+
43+
models.customer.hasOne(models.picture, {
44+
foreignKey: {
45+
name: 'customerIdKey',
46+
field: 'customer_id',
47+
},
48+
as: 'picture',
49+
});
50+
models.picture.belongsTo(models.customer, {
51+
foreignKey: {
52+
name: 'customerIdKey',
53+
field: 'customer_id',
54+
},
55+
as: 'customer',
56+
});
57+
2858
describe(`with dialect ${connectionManager.getDialect()} (port: ${connectionManager.getPort()})`, () => {
2959
describe('with model `users`', () => {
3060
it('should set name correctly', async () => {
@@ -120,5 +150,14 @@ function getField(schema, name) {
120150
});
121151
});
122152
});
153+
154+
describe('with association', () => {
155+
it('should set foreignAndPrimaryKey to true', async () => {
156+
expect.assertions(1);
157+
158+
const schema = await getSchema(models.picture, sequelizeOptions);
159+
expect(schema.fields.find((x) => x.field === 'customer').foreignAndPrimaryKey).toBeTrue();
160+
});
161+
});
123162
});
124163
});

0 commit comments

Comments
 (0)