From 2327c13d1a67499213c8da32d73f70a6efb1da42 Mon Sep 17 00:00:00 2001 From: Kyle MacDonald Date: Sat, 17 Jun 2017 16:54:55 -0400 Subject: [PATCH 01/10] docs: additional documentation around cli generators (#711) --- bin/lux | 2 +- guide/getting-started.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bin/lux b/bin/lux index d1bc1ccb..ce076d57 100755 --- a/bin/lux +++ b/bin/lux @@ -148,7 +148,7 @@ cli cli .command('g [attrs...]') .alias('generate') - .description('Example: lux generate model user') + .description('Example: lux generate model user name:string email:string admin:boolean') .action((type, name, attrs) => { exec('generate', { type, name, attrs }) .then(exit) diff --git a/guide/getting-started.md b/guide/getting-started.md index ffd870f6..12950c04 100644 --- a/guide/getting-started.md +++ b/guide/getting-started.md @@ -12,6 +12,22 @@ Use the new command to create your first project. lux new ``` +### Generators + +Lux allows you to use the CLI to generate boilerplate for the following types: + +- `model` +- `controller` +- `serializer` +- `middleware` +- `migration` +- `resource` +- `util` + +```bash +lux generate [attrs...] +``` + ### Running To run your application use the serve command. From 0f406b590858b3557397b09797b70d3e1a40e2cc Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sat, 17 Jun 2017 20:41:46 -0400 Subject: [PATCH 02/10] fix: detailed error messages leak into production responses (#713) * fix: detailed error messages leak into production responses * fix: typo in env util --- .../server/responder/test/responder.test.js | 9 +++++++ .../server/responder/utils/data-for.js | 3 ++- src/utils/env.js | 7 ++++++ src/utils/test/env.test.js | 25 +++++++++++++++++++ test/utils/set-env.js | 9 +++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/utils/env.js create mode 100644 src/utils/test/env.test.js create mode 100644 test/utils/set-env.js diff --git a/src/packages/server/responder/test/responder.test.js b/src/packages/server/responder/test/responder.test.js index 006b91db..e5fcfc60 100644 --- a/src/packages/server/responder/test/responder.test.js +++ b/src/packages/server/responder/test/responder.test.js @@ -9,6 +9,7 @@ import { createRequest } from '../../request'; import { createResponse } from '../../response'; import { createResponder } from '../index'; +import setEnv from '../../../../../test/utils/set-env'; import { getTestApp } from '../../../../../test/utils/get-test-app'; const DOMAIN = 'http://localhost:4100'; @@ -198,6 +199,14 @@ describe('module "server/responder"', () => { }); describe('- responding with an error', () => { + beforeEach(() => { + setEnv('development'); + }); + + afterEach(() => { + setEnv('test'); + }); + it('works with vanilla errors', async () => { const result = await test((req, res) => { const respond = createResponder(req, res); diff --git a/src/packages/server/responder/utils/data-for.js b/src/packages/server/responder/utils/data-for.js index ba3af684..9c6303f8 100644 --- a/src/packages/server/responder/utils/data-for.js +++ b/src/packages/server/responder/utils/data-for.js @@ -1,6 +1,7 @@ // @flow import { VERSION } from '../../../jsonapi'; import { STATUS_CODES } from '../../constants'; +import * as env from '../../../../utils/env'; import type { JSONAPI$Document, JSONAPI$ErrorObject } from '../../../jsonapi'; // eslint-disable-line max-len, no-duplicate-imports /** @@ -23,7 +24,7 @@ export default function dataFor( errData.title = title; } - if (err) { + if (err && env.isDevelopment()) { errData.detail = err.message; } diff --git a/src/utils/env.js b/src/utils/env.js new file mode 100644 index 00000000..dc559dad --- /dev/null +++ b/src/utils/env.js @@ -0,0 +1,7 @@ +/* @flow */ + +const isEnv = value => () => process.env.NODE_ENV === value; + +export const isDevelopment: () => boolean = isEnv('development'); +export const isProduction: () => boolean = isEnv('production'); +export const isTest: () => boolean = isEnv('test'); diff --git a/src/utils/test/env.test.js b/src/utils/test/env.test.js new file mode 100644 index 00000000..f74909e2 --- /dev/null +++ b/src/utils/test/env.test.js @@ -0,0 +1,25 @@ +/* @flow */ + +import { expect } from 'chai'; +import { afterEach, test } from 'mocha'; + +import * as env from '../env'; +import setEnv from '../../../test/utils/set-env'; + +afterEach(() => { + setEnv('test'); +}); + +test('isDevelopment()', () => { + setEnv('development'); + expect(env.isDevelopment()).to.be.true; +}); + +test('isProduction()', () => { + setEnv('production'); + expect(env.isProduction()).to.be.true; +}); + +test('isTest()', () => { + expect(env.isTest()).to.be.true; +}); diff --git a/test/utils/set-env.js b/test/utils/set-env.js new file mode 100644 index 00000000..47337c1b --- /dev/null +++ b/test/utils/set-env.js @@ -0,0 +1,9 @@ +/* @flow */ + +type Environment = 'development' + | 'production' + | 'test'; + +export default function setEnv(value: Environment): void { + global.process.env.NODE_ENV = value; +} From 6b3547f436bacde850900ca97f4a051fe6859fab Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Tue, 11 Jul 2017 10:14:15 -0400 Subject: [PATCH 03/10] fix: filtering with an empty array as a value causes an error (#715) * fix: filtering with an empty array as a value causes an error * fix: linter error * fix: remove code duplication * fix: Unnecessary 'else' after 'return' no-else-return linter error --- src/packages/database/query/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/packages/database/query/index.js b/src/packages/database/query/index.js index be860e2e..7e672746 100644 --- a/src/packages/database/query/index.js +++ b/src/packages/database/query/index.js @@ -195,17 +195,17 @@ class Query<+T: any> extends Promise { } if (Array.isArray(value)) { - if (value.length > 1) { - this.snapshots.push([ - not ? 'whereNotIn' : 'whereIn', - [key, value] - ]); - } else { + if (value.length === 1) { return { ...obj, [key]: value[0] }; } + + this.snapshots.push([ + not ? 'whereNotIn' : 'whereIn', + [key, value] + ]); } else if (value === null) { this.snapshots.push([ not ? 'whereNotNull' : 'whereNull', From ea2b8f992603d2d54c758d38d0bec711b5d3ddfd Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Tue, 11 Jul 2017 10:41:39 -0400 Subject: [PATCH 04/10] =?UTF-8?q?release:=20v1.2.1=20=F0=9F=94=A7=20(#716)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ examples/social-network/package.json | 2 +- examples/todo/package.json | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0736bf30..3f874b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Lux Changelog +### v1.2.1 (July 11, 2017) + +* [[`6b3547f436`](https://github.com/postlight/lux/commit/6b3547f436)] - **fix**: filtering with an empty array as a value causes an error (#715) (Nick Schot) +* [[`0f406b5908`](https://github.com/postlight/lux/commit/0f406b5908)] - **fix**: detailed error messages leak into production responses (#713) (Zachary Golba) +* [[`2327c13d1a`](https://github.com/postlight/lux/commit/2327c13d1a)] - **docs**: additional documentation around cli generators (#711) (Kyle MacDonald) +* [[`1691f9ad50`](https://github.com/postlight/lux/commit/1691f9ad50)] - **release**: 1.2.0 ✨ (#705) (Zachary Golba) + ### v1.2.0 (May 16, 2017) ##### Commits diff --git a/examples/social-network/package.json b/examples/social-network/package.json index 60c693b8..c3286945 100644 --- a/examples/social-network/package.json +++ b/examples/social-network/package.json @@ -14,7 +14,7 @@ "babel-preset-lux": "2.0.2", "bcryptjs": "2.4.3", "knex": "0.13.0", - "lux-framework": "1.2.0", + "lux-framework": "1.2.1", "sqlite3": "3.1.8" }, "devDependencies": { diff --git a/examples/todo/package.json b/examples/todo/package.json index a6af67f0..77cb1087 100644 --- a/examples/todo/package.json +++ b/examples/todo/package.json @@ -13,7 +13,7 @@ "babel-core": "6.24.1", "babel-preset-lux": "2.0.2", "knex": "0.13.0", - "lux-framework": "1.2.0", + "lux-framework": "1.2.1", "sqlite3": "3.1.8" }, "devDependencies": { diff --git a/package.json b/package.json index b932d2ac..7c6e0ff5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lux-framework", - "version": "1.2.0", + "version": "1.2.1", "description": "Build scalable, Node.js-powered REST APIs with almost no code.", "repository": "github:postlight/lux", "keywords": [ From 6c22bdf071bb75c45e9b46802e1fc0f86973bbe5 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Mon, 28 Aug 2017 17:23:31 -0400 Subject: [PATCH 05/10] fix: do not validate param existence for patch requests (#721) --- src/packages/router/route/params/index.js | 4 ++-- .../router/route/params/utils/get-data-params.js | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/packages/router/route/params/index.js b/src/packages/router/route/params/index.js index f384ba3a..e439dde5 100644 --- a/src/packages/router/route/params/index.js +++ b/src/packages/router/route/params/index.js @@ -33,7 +33,7 @@ export function paramsFor({ if (method === 'POST' || method === 'PATCH') { params = [ ...params, - getDataParams(controller, true) + getDataParams(controller, method, true) ]; } } else if (type === 'collection') { @@ -45,7 +45,7 @@ export function paramsFor({ if (method === 'POST' || method === 'PATCH') { params = [ ...params, - getDataParams(controller, false) + getDataParams(controller, method, false) ]; } } else if (type === 'custom') { diff --git a/src/packages/router/route/params/utils/get-data-params.js b/src/packages/router/route/params/utils/get-data-params.js index 797a41bc..77d737da 100644 --- a/src/packages/router/route/params/utils/get-data-params.js +++ b/src/packages/router/route/params/utils/get-data-params.js @@ -41,17 +41,18 @@ function getTypeParam({ /** * @private */ -function getAttributesParam({ - model, - params -}: Controller): [string, ParameterLike] { +function getAttributesParam( + { model, params }: Controller, + method: 'PATCH' | 'POST', +): [string, ParameterLike] { return ['attributes', new ParameterGroup(params.reduce((group, param) => { const col = model.columnFor(param); if (col) { const type = typeForColumn(col); const path = `data.attributes.${param}`; - const required = !col.nullable && isNull(col.defaultValue); + const required = + method !== 'PATCH' && !col.nullable && isNull(col.defaultValue); return [ ...group, @@ -140,13 +141,14 @@ function getRelationshipsParam({ */ export default function getDataParams( controller: Controller, + method: 'PATCH' | 'POST', includeID: boolean ): [string, ParameterLike] { let params = [getTypeParam(controller)]; if (controller.hasModel) { params = [ - getAttributesParam(controller), + getAttributesParam(controller, method), getRelationshipsParam(controller), ...params ]; From 667febd98fd562de1010fc0eeac22d59936ca889 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Mon, 28 Aug 2017 18:01:30 -0400 Subject: [PATCH 06/10] =?UTF-8?q?release:=20v1.2.2=20=F0=9F=94=A7=20(#722)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++++ examples/social-network/package.json | 2 +- examples/todo/package.json | 2 +- package.json | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f874b95..e88fb871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Lux Changelog +### v1.2.2 (Aug 28, 2017) + +* [[`6c22bdf071`](https://github.com/postlight/lux/commit/6c22bdf071)] - **fix**: do not validate param existence for patch requests (#721) (Zachary Golba) +* [[`ea2b8f9926`](https://github.com/postlight/lux/commit/ea2b8f9926)] - **release**: v1.2.1 🔧 (#716) (Zachary Golba) + ### v1.2.1 (July 11, 2017) * [[`6b3547f436`](https://github.com/postlight/lux/commit/6b3547f436)] - **fix**: filtering with an empty array as a value causes an error (#715) (Nick Schot) diff --git a/examples/social-network/package.json b/examples/social-network/package.json index c3286945..ac20ef25 100644 --- a/examples/social-network/package.json +++ b/examples/social-network/package.json @@ -14,7 +14,7 @@ "babel-preset-lux": "2.0.2", "bcryptjs": "2.4.3", "knex": "0.13.0", - "lux-framework": "1.2.1", + "lux-framework": "1.2.2", "sqlite3": "3.1.8" }, "devDependencies": { diff --git a/examples/todo/package.json b/examples/todo/package.json index 77cb1087..d7b67e07 100644 --- a/examples/todo/package.json +++ b/examples/todo/package.json @@ -13,7 +13,7 @@ "babel-core": "6.24.1", "babel-preset-lux": "2.0.2", "knex": "0.13.0", - "lux-framework": "1.2.1", + "lux-framework": "1.2.2", "sqlite3": "3.1.8" }, "devDependencies": { diff --git a/package.json b/package.json index 7c6e0ff5..c79e46b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lux-framework", - "version": "1.2.1", + "version": "1.2.2", "description": "Build scalable, Node.js-powered REST APIs with almost no code.", "repository": "github:postlight/lux", "keywords": [ From 56225dac713afeaa15dca7c0e58fd586c27ef755 Mon Sep 17 00:00:00 2001 From: Nick Schot Date: Mon, 2 Jul 2018 18:58:19 +0200 Subject: [PATCH 07/10] fix: falsy IDs breaking relationships (#730) --- src/packages/database/query/runner/utils/build-results.js | 2 +- src/packages/serializer/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/database/query/runner/utils/build-results.js b/src/packages/database/query/runner/utils/build-results.js index 6db66fd1..ae65915f 100644 --- a/src/packages/database/query/runner/utils/build-results.js +++ b/src/packages/database/query/runner/utils/build-results.js @@ -108,7 +108,7 @@ export default async function buildResults({ .reduce((r, entry) => { let [key, value] = entry; - if (!value && pkPattern.test(key)) { + if (value == null && pkPattern.test(key)) { return r; } else if (key.indexOf('.') >= 0) { const [a, b] = key.split('.'); diff --git a/src/packages/serializer/index.js b/src/packages/serializer/index.js index 245f44eb..096c566c 100644 --- a/src/packages/serializer/index.js +++ b/src/packages/serializer/index.js @@ -616,7 +616,7 @@ class Serializer { }) ) }; - } else if (related && related.id) { + } else if (related && related.id != null) { return this.formatRelationship({ domain, included, From 812555a28565fcda62dd9993e35584ab3ddb9442 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Fri, 20 Jul 2018 09:48:45 -0400 Subject: [PATCH 08/10] =?UTF-8?q?release:=20v1.2.3=20=F0=9F=94=A7=20(#734)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++++ examples/social-network/package.json | 2 +- examples/todo/package.json | 2 +- package.json | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e88fb871..0e073b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Lux Changelog +### v1.2.3 (July 20, 2018) + +* [[`56225dac71`](https://github.com/postlight/lux/commit/56225dac71)] - **fix**: falsy IDs breaking relationships (#730) (Nick Schot) +* [[`667febd98f`](https://github.com/postlight/lux/commit/667febd98f)] - **release**: v1.2.2 🔧 (#722) (Zachary Golba) + ### v1.2.2 (Aug 28, 2017) * [[`6c22bdf071`](https://github.com/postlight/lux/commit/6c22bdf071)] - **fix**: do not validate param existence for patch requests (#721) (Zachary Golba) diff --git a/examples/social-network/package.json b/examples/social-network/package.json index ac20ef25..6b711111 100644 --- a/examples/social-network/package.json +++ b/examples/social-network/package.json @@ -14,7 +14,7 @@ "babel-preset-lux": "2.0.2", "bcryptjs": "2.4.3", "knex": "0.13.0", - "lux-framework": "1.2.2", + "lux-framework": "1.2.3", "sqlite3": "3.1.8" }, "devDependencies": { diff --git a/examples/todo/package.json b/examples/todo/package.json index d7b67e07..bc000a24 100644 --- a/examples/todo/package.json +++ b/examples/todo/package.json @@ -13,7 +13,7 @@ "babel-core": "6.24.1", "babel-preset-lux": "2.0.2", "knex": "0.13.0", - "lux-framework": "1.2.2", + "lux-framework": "1.2.3", "sqlite3": "3.1.8" }, "devDependencies": { diff --git a/package.json b/package.json index c79e46b7..70b996a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lux-framework", - "version": "1.2.2", + "version": "1.2.3", "description": "Build scalable, Node.js-powered REST APIs with almost no code.", "repository": "github:postlight/lux", "keywords": [ From 0b77eea0a793e11087dc13001cecac466139e7bb Mon Sep 17 00:00:00 2001 From: Nick Schot Date: Wed, 5 Dec 2018 17:50:07 +0100 Subject: [PATCH 09/10] fix: don't update belongsTo relationships for new records NOTE: side-effect is that duplicate ID's might occur when DB does not have a UNIQUE constraint on the FK --- src/packages/database/relationship/utils/update-relationship.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/database/relationship/utils/update-relationship.js b/src/packages/database/relationship/utils/update-relationship.js index 396c0509..b11c694f 100644 --- a/src/packages/database/relationship/utils/update-relationship.js +++ b/src/packages/database/relationship/utils/update-relationship.js @@ -115,7 +115,7 @@ function updateBelongsTo({ Reflect.set(record, opts.foreignKey, foreignKeyValue); - if (inverseOpts && inverseOpts.type === 'hasOne') { + if (inverseOpts && inverseOpts.type === 'hasOne' && !record.isNew) { return [ record.constructor .table() From c39cdd75b363717dab884dab0d5cdc0b60241087 Mon Sep 17 00:00:00 2001 From: Nick Schot Date: Wed, 23 Jan 2019 12:04:24 +0100 Subject: [PATCH 10/10] - restructure model create method to make sure we have a valid PK before updating relationships - undo isNew check in updateRelationships --- src/packages/database/model/index.js | 32 ++++++++++--------- .../relationship/utils/update-relationship.js | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/packages/database/model/index.js b/src/packages/database/model/index.js index 546951d9..6f4b5922 100644 --- a/src/packages/database/model/index.js +++ b/src/packages/database/model/index.js @@ -1166,20 +1166,6 @@ class Model { const run = async (trx: Object) => { const { hooks, logger, primaryKey } = this; const instance = Reflect.construct(this, [props, false]); - let statements = []; - - const associations = Object - .keys(props) - .filter(key => ( - Boolean(this.relationshipFor(key)) - )); - - if (associations.length) { - statements = associations.reduce((arr, key) => [ - ...arr, - ...updateRelationship(instance, key, trx) - ], []); - } await runHooks(instance, trx, hooks.beforeValidation); @@ -1191,12 +1177,28 @@ class Model { hooks.beforeSave ); - const runner = createRunner(logger, statements); + const runner = createRunner(logger, []); const [[primaryKeyValue]] = await runner(await create(instance, trx)); Reflect.set(instance, primaryKey, primaryKeyValue); Reflect.set(instance.rawColumnData, primaryKey, primaryKeyValue); + let statements = []; + const associations = Object + .keys(props) + .filter(key => ( + Boolean(this.relationshipFor(key)) + )); + + if (associations.length) { + statements = associations.reduce((arr, key) => [ + ...arr, + ...updateRelationship(instance, key, trx) + ], []); + } + + await Promise.all(statements); + Reflect.defineProperty(instance, 'initialized', { value: true, writable: false, diff --git a/src/packages/database/relationship/utils/update-relationship.js b/src/packages/database/relationship/utils/update-relationship.js index b11c694f..396c0509 100644 --- a/src/packages/database/relationship/utils/update-relationship.js +++ b/src/packages/database/relationship/utils/update-relationship.js @@ -115,7 +115,7 @@ function updateBelongsTo({ Reflect.set(record, opts.foreignKey, foreignKeyValue); - if (inverseOpts && inverseOpts.type === 'hasOne' && !record.isNew) { + if (inverseOpts && inverseOpts.type === 'hasOne') { return [ record.constructor .table()