Comparing version 2.0.1 to 2.0.2
@@ -0,1 +1,18 @@ | ||
2.0.2 / 2021-02-10 | ||
================== | ||
## What's Changed | ||
* fix: order by alias should not throw by @cyjake in https://github.com/cyjake/leoric/pull/255 | ||
* fix: fix #257 DataType.uncast should skip Raw type at type checking by @JimmyDaddy in https://github.com/cyjake/leoric/pull/258 | ||
* docs: async function in transaction by @cyjake in https://github.com/cyjake/leoric/pull/259 | ||
* fix: fixed #256 static create instance should check all default attri… by @JimmyDaddy in https://github.com/cyjake/leoric/pull/262 | ||
* fix: fix #260 UPDATE with LIMIT and ORDER should be formatted(mysql only) by @JimmyDaddy in https://github.com/cyjake/leoric/pull/261 | ||
* refactor: keep the UPDATE ... ORDER BY ... LIMIT to mysql driver by @cyjake in https://github.com/cyjake/leoric/pull/264 | ||
* fix: fix #263 upsert attributes should use defaultValue while there i… by @JimmyDaddy in https://github.com/cyjake/leoric/pull/265 | ||
* fix: fix restore Error `Undefined attribute "deletedAt"` by @JimmyDaddy in https://github.com/cyjake/leoric/pull/267 | ||
* fix: type checking adaption by @JimmyDaddy in https://github.com/cyjake/leoric/pull/266 | ||
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.1...v2.0.2 | ||
2.0.1 / 2022-01-05 | ||
@@ -2,0 +19,0 @@ ================== |
{ | ||
"name": "leoric", | ||
"version": "2.0.1", | ||
"version": "2.0.2", | ||
"description": "JavaScript Object-relational mapping alchemy", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -241,5 +241,2 @@ 'use strict'; | ||
static build(values, options = {}) { | ||
if (options.validate !== false) { | ||
this._validateAttributes(values); | ||
} | ||
const { raw } = Object.assign({ raw: false, isNewRecord: true }, options); | ||
@@ -260,2 +257,3 @@ const { attributes } = this; | ||
} | ||
return instance; | ||
@@ -474,2 +472,3 @@ } | ||
let findSpell = this._find(where, options); | ||
translateOptions(findSpell, options); | ||
if (paranoid === false) findSpell = findSpell.unparanoid; | ||
@@ -490,2 +489,3 @@ const instances = await findSpell; | ||
const spell = super.update(whereConditions, values, { validate, hooks: false, ...options }); | ||
translateOptions(spell, options); | ||
if (!paranoid) return spell.unparanoid; | ||
@@ -492,0 +492,0 @@ return spell; |
@@ -18,2 +18,3 @@ 'use strict'; | ||
const { logger } = require('./utils/index'); | ||
const { TIMESTAMP_NAMES, LEGACY_TIMESTAMP_COLUMN_MAP } = require('./constants'); | ||
@@ -80,2 +81,19 @@ function looseReadonly(props) { | ||
function valuesValidate(values, attributes, ctx) { | ||
for (const valueKey in values) { | ||
const attribute = attributes[valueKey]; | ||
if (!attribute) continue; | ||
const { validate = {}, name, allowNull, defaultValue } = attribute; | ||
const value = values[valueKey]; | ||
if (value == null && defaultValue == null) { | ||
if (allowNull === false) throw new LeoricValidateError('notNull', name); | ||
if ((allowNull === true || allowNull === undefined) && validate.notNull === undefined ) continue; | ||
} | ||
if (!validate) continue; | ||
for (const key in validate) { | ||
if (validate.hasOwnProperty(key)) executeValidator(ctx, key, attribute, value); | ||
} | ||
} | ||
} | ||
/** | ||
@@ -285,17 +303,3 @@ * The base class that provides Object-relational mapping. This class is never intended to be used directly. We need to create models that extends from Bone. Most of the query features of Bone is implemented by {@link Spell} such as {@link Spell#$group} and {@link Spell#$join}. With Bone, you can create models like this: | ||
changedValues = Object.assign(changedValues, values); | ||
for (const valueKey in changedValues) { | ||
const attribute = attributes[valueKey]; | ||
if (!attribute) continue; | ||
const { validate = {}, name, allowNull, defaultValue } = attribute; | ||
const value = changedValues[valueKey]; | ||
if (value == null && defaultValue == null) { | ||
if (allowNull === false) throw new LeoricValidateError('notNull', name); | ||
if ((allowNull === true || allowNull === undefined) && validate.notNull === undefined ) return; | ||
} | ||
if (!validate) return; | ||
for (const key in validate) { | ||
if (validate.hasOwnProperty(key)) executeValidator(this, key, attribute, value); | ||
} | ||
} | ||
valuesValidate(changedValues, attributes, this); | ||
} | ||
@@ -312,18 +316,3 @@ | ||
const { attributes } = this; | ||
for (const valueKey in values) { | ||
const attribute = attributes[valueKey]; | ||
// If valueKey is not an attribute of the Model, go to the next loop instead of throw 'No Such Attribute' Error, | ||
// in case it is a custom property of the Model which defined by custom setters/getters. | ||
if (!attribute) return; | ||
const { validate = {}, name, allowNull, defaultValue } = attribute; | ||
const value = values[valueKey]; | ||
if (value == null && defaultValue == null) { | ||
if (allowNull === false) throw new LeoricValidateError('notNull', name); | ||
if ((allowNull === true || allowNull === undefined) && validate.notNull === undefined) return; | ||
} | ||
if (!validate) return; | ||
for (const key in validate) { | ||
if (validate.hasOwnProperty(key)) executeValidator(this, key, attribute, value); | ||
} | ||
} | ||
valuesValidate(values, attributes, this); | ||
} | ||
@@ -732,2 +721,3 @@ | ||
const validateValues = {}; | ||
for (const name in attributes) { | ||
@@ -741,6 +731,8 @@ const value = this.attribute(name); | ||
} | ||
if (attributes[name].primaryKey) continue; | ||
validateValues[name] = data[name]; | ||
} | ||
if (opts.validate !== false) { | ||
this._validateAttributes(); | ||
this._validateAttributes(validateValues); | ||
} | ||
@@ -831,6 +823,6 @@ | ||
[primaryKey]: this[primaryKey], | ||
deletedAt: { $ne: null }, | ||
[deletedAt]: { $ne: null }, | ||
}; | ||
if (shardingKey) conditions[shardingKey] = this[shardingKey]; | ||
await this.update({ deletedAt: null }, { ...opts, paranoid: false }); | ||
await this.update({ [deletedAt]: null }, { ...opts, paranoid: false }); | ||
return this; | ||
@@ -850,3 +842,3 @@ } | ||
} | ||
return Bone.update.call(this, conditions, { deletedAt: null }, { ...opts, paranoid: false }); | ||
return Bone.update.call(this, conditions, { [deletedAt]: null }, { ...opts, paranoid: false }); | ||
} | ||
@@ -866,5 +858,9 @@ | ||
const { attributes } = Model; | ||
for (const name in values) { | ||
if (this.hasAttribute(name)) { | ||
data[name] = values[name]; | ||
for (const key in attributes) { | ||
const attribute = attributes[key]; | ||
if (attribute.primaryKey) continue; | ||
if (values[key] == null && attribute.defaultValue != null) { | ||
data[key] = attribute.defaultValue; | ||
} else if (values[key] !== undefined){ | ||
data[key] = values[key]; | ||
} | ||
@@ -952,2 +948,11 @@ } | ||
attributes[name] = attribute; | ||
if (TIMESTAMP_NAMES.includes(name)) { | ||
const { columnName } = attribute; | ||
const legacyColumnName = LEGACY_TIMESTAMP_COLUMN_MAP[columnName]; | ||
if (!columnMap[columnName] && legacyColumnName && columnMap[legacyColumnName]) { | ||
// correct columname | ||
attribute.columnName = legacyColumnName; | ||
attributeMap[attribute.columnName] = attribute; | ||
} | ||
} | ||
const columnInfo = columnMap[attribute.columnName]; | ||
@@ -962,3 +967,3 @@ // if datetime or timestamp precision not defined, default to column info | ||
const timestamps = {}; | ||
for (const key of [ 'createdAt', 'updatedAt', 'deletedAt' ]) { | ||
for (const key of TIMESTAMP_NAMES) { | ||
const name = attributes.hasOwnProperty(key) ? key : snakeCase(key); | ||
@@ -1332,9 +1337,5 @@ const attribute = attributes[name]; | ||
const instance = new this(data); | ||
if (opts.validate !== false) { | ||
instance._validateAttributes(data); // call instance._validateAttributes manually to validate the raw value | ||
} | ||
// static create proxy to instance.create | ||
return instance.create({ | ||
...opts, | ||
validate: false, // should not validate again | ||
}); | ||
@@ -1341,0 +1342,0 @@ } |
'use strict'; | ||
const { AGGREGATOR_MAP } = require('./contants'); | ||
const { AGGREGATOR_MAP } = require('./constants'); | ||
@@ -5,0 +5,0 @@ const AGGREGATORS = Object.values(AGGREGATOR_MAP); |
@@ -5,2 +5,3 @@ 'use strict'; | ||
const invokable = require('./utils/invokable'); | ||
const Raw = require('./raw'); | ||
@@ -118,3 +119,3 @@ /** | ||
uncast(value) { | ||
if (value == null) return value; | ||
if (value == null || value instanceof Raw) return value; | ||
return '' + value; | ||
@@ -191,11 +192,14 @@ } | ||
cast(value) { | ||
if (value == null) return value; | ||
if (value == null || isNaN(value)) return value; | ||
return Number(value); | ||
} | ||
uncast(value) { | ||
uncast(value, strict = true) { | ||
const originValue = value; | ||
if (value == null) return value; | ||
if (value == null || value instanceof Raw) return value; | ||
if (typeof value === 'string') value = parseInt(value, 10); | ||
if (isNaN(value)) throw new Error(util.format('invalid integer: %s', originValue)); | ||
if (isNaN(value)) { | ||
if (strict) throw new Error(util.format('invalid integer: %s', originValue)); | ||
return originValue; | ||
} | ||
return value; | ||
@@ -249,4 +253,6 @@ } | ||
cast(value) { | ||
const original = value; | ||
if (value == null) return value; | ||
if (!(value instanceof Date)) value = new Date(value); | ||
if (isNaN(value.getTime())) return original; | ||
return this._round(value); | ||
@@ -258,3 +264,3 @@ } | ||
if (value == null) return value; | ||
if (value == null || value instanceof Raw) return value; | ||
if (typeof value.toDate === 'function') { | ||
@@ -297,4 +303,6 @@ value = value.toDate(); | ||
cast(value) { | ||
const original = value; | ||
if (value == null) return value; | ||
if (!(value instanceof Date)) value = new Date(value); | ||
if (isNaN(value.getTime())) return original; | ||
return this._round(value); | ||
@@ -306,3 +314,3 @@ } | ||
if (value == null) return value; | ||
if (value == null || value instanceof Raw) return value; | ||
if (typeof value.toDate === 'function') { | ||
@@ -319,3 +327,3 @@ value = value.toDate(); | ||
if (!(value instanceof Date)) value = new Date(value); | ||
if (isNaN(value)) throw new Error(util.format('invalid date: %s', originValue));; | ||
if (isNaN(value)) throw new Error(util.format('invalid date: %s', originValue)); | ||
@@ -404,3 +412,3 @@ return this._round(value); | ||
uncast(value) { | ||
if (value == null) return value; | ||
if (value == null || value instanceof Raw) return value; | ||
return global.JSON.stringify(value); | ||
@@ -407,0 +415,0 @@ } |
@@ -129,7 +129,7 @@ 'use strict'; | ||
uncast(value) { | ||
uncast(value, strict = true) { | ||
if (Array.isArray(value) && this.jsType !== JSON) { | ||
return value.map(entry => this.type.uncast(entry)); | ||
return value.map(entry => this.type.uncast(entry, strict)); | ||
} | ||
return this.type.uncast(value); | ||
return this.type.uncast(value, strict); | ||
} | ||
@@ -136,0 +136,0 @@ } |
@@ -132,3 +132,3 @@ 'use strict'; | ||
const [token, direction] = order; | ||
const { type, qualifiers, value } = token; | ||
const { type, qualifiers = [], value } = token; | ||
if (type == 'id' && qualifiers[0] == baseName) { | ||
@@ -224,3 +224,3 @@ subspell.orders.push([{ type, value }, direction]); | ||
if (hoistable) { | ||
function checkQualifier({ type, qualifiers }) { | ||
function checkQualifier({ type, qualifiers = [] }) { | ||
if (type === 'id' && qualifiers.length> 0 && !qualifiers.includes(baseName)) { | ||
@@ -481,2 +481,3 @@ hoistable = false; | ||
} | ||
return { | ||
@@ -612,2 +613,3 @@ sql: chunks.join(' '), | ||
formatReturning, | ||
formatOrders | ||
}; |
@@ -61,2 +61,19 @@ 'use strict'; | ||
}, | ||
/** | ||
* UPDATE ... ORDER BY ... LIMIT ${rowCount} | ||
* - https://dev.mysql.com/doc/refman/8.0/en/update.html | ||
* @param {Spell} spell | ||
*/ | ||
formatUpdate(spell) { | ||
const result = spellbook.formatUpdate.call(this, spell); | ||
const { rowCount, orders } = spell; | ||
const chunks = []; | ||
if (orders.length > 0) chunks.push(`ORDER BY ${this.formatOrders(spell, orders).join(', ')}`); | ||
if (rowCount > 0) chunks.push(`LIMIT ${rowCount}`); | ||
if (chunks.length > 0) result.sql += ` ${chunks.join(' ')}`; | ||
return result; | ||
}, | ||
}; |
'use strict'; | ||
const DataTypes = require('../../data_types'); | ||
const util = require('util'); | ||
const Raw = require('../../raw'); | ||
class Postgres_DATE extends DataTypes.DATE { | ||
@@ -40,2 +43,23 @@ constructor(precision, timezone = true) { | ||
class Postgres_INTEGER extends DataTypes.INTEGER { | ||
constructor(length) { | ||
super(length); | ||
} | ||
uncast(value) { | ||
const originValue = value; | ||
if (value == null || value instanceof Raw) return value; | ||
if (typeof value === 'string') value = parseInt(value, 10); | ||
if (isNaN(value)) throw new Error(util.format('invalid integer: %s', originValue)); | ||
return value; | ||
} | ||
} | ||
class Postgres_BIGINT extends Postgres_INTEGER { | ||
constructor() { | ||
super(); | ||
this.dataType = 'bigint'; | ||
} | ||
} | ||
class Postgres_DataTypes extends DataTypes { | ||
@@ -47,4 +71,6 @@ static DATE = Postgres_DATE; | ||
static BLOB = Postgres_BINARY; | ||
static INTEGER = Postgres_INTEGER; | ||
static BIGINT = Postgres_BIGINT; | ||
} | ||
module.exports = Postgres_DataTypes; |
@@ -10,4 +10,38 @@ 'use strict'; | ||
} | ||
uncast(value) { | ||
try { | ||
return super.uncast(value); | ||
} catch (error) { | ||
console.error(new Error(`unable to cast ${value} to DATE`)); | ||
return value; | ||
} | ||
} | ||
} | ||
class Sqlite_DATEONLY extends DataTypes.DATEONLY { | ||
constructor() { | ||
super(); | ||
} | ||
uncast(value) { | ||
try { | ||
return super.uncast(value); | ||
} catch (error) { | ||
console.error(new Error(`unable to cast ${value} to DATEONLY`)); | ||
return value; | ||
} | ||
} | ||
} | ||
class Sqlite_INTEGER extends DataTypes.INTEGER { | ||
constructor(length) { | ||
super(length); | ||
} | ||
uncast(value) { | ||
return super.uncast(value, false); | ||
} | ||
} | ||
class Sqlite_BIGINT extends DataTypes.BIGINT { | ||
@@ -62,4 +96,12 @@ constructor() { | ||
} | ||
static get DATEONLY() { | ||
return Sqlite_DATEONLY; | ||
} | ||
static get INTEGER() { | ||
return Sqlite_INTEGER; | ||
} | ||
} | ||
module.exports = Sqlite_DataTypes; |
@@ -211,3 +211,3 @@ 'use strict'; | ||
if (attribute.jsType === JSON && typeof arg.value === 'string') continue; | ||
arg.value = attribute.uncast(arg.value); | ||
arg.value = attribute.uncast(arg.value, false); | ||
} | ||
@@ -214,0 +214,0 @@ } |
@@ -11,2 +11,3 @@ 'use strict'; | ||
const Raw = require('./raw'); | ||
const { LEGACY_TIMESTAMP_MAP } = require('./constants'); | ||
@@ -43,8 +44,2 @@ /** | ||
const LEGACY_TIMESTAMP_MAP = { | ||
gmtCreate: 'createdAt', | ||
gmtModified: 'updatedAt', | ||
gmtDeleted: 'deletedAt', | ||
}; | ||
/** | ||
@@ -51,0 +46,0 @@ * construct model attributes entirely from column definitions |
@@ -14,3 +14,3 @@ 'use strict'; | ||
const Raw = require('./raw'); | ||
const { AGGREGATOR_MAP } = require('./contants'); | ||
const { AGGREGATOR_MAP } = require('./constants'); | ||
@@ -17,0 +17,0 @@ /** |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
287294
7941