2021-05-18 10:16:03 +02:00
'use strict' ;
2021-06-28 12:34:29 +02:00
const _ = require ( 'lodash/fp' ) ;
2021-06-28 21:37:44 +02:00
const dateFns = require ( 'date-fns' ) ;
2021-10-20 17:30:05 +02:00
const { InvalidTimeError , InvalidDateError , InvalidDateTimeError } = require ( './errors' ) ;
2021-06-28 12:34:29 +02:00
class Field {
constructor ( config ) {
this . config = config ;
}
toDB ( value ) {
return value ;
}
fromDB ( value ) {
return value ;
}
}
class StringField extends Field {
toDB ( value ) {
return _ . toString ( value ) ;
}
2021-06-28 21:37:44 +02:00
fromDB ( value ) {
return _ . toString ( value ) ;
}
2021-06-28 12:34:29 +02:00
}
class JSONField extends Field {
toDB ( value ) {
return JSON . stringify ( value ) ;
}
2021-06-28 21:37:44 +02:00
fromDB ( value ) {
if ( typeof value === 'string' ) return JSON . parse ( value ) ;
return value ;
}
2021-06-28 12:34:29 +02:00
}
class BooleanField extends Field {
toDB ( value ) {
if ( typeof value === 'boolean' ) return value ;
if ( [ 'true' , 't' , '1' , 1 ] . includes ( value ) ) {
return true ;
}
if ( [ 'false' , 'f' , '0' , 0 ] . includes ( value ) ) {
return false ;
}
2021-06-28 21:37:44 +02:00
return Boolean ( value ) ;
}
fromDB ( value ) {
if ( typeof value === 'boolean' ) {
return value ;
}
const strVal = _ . toString ( value ) ;
if ( strVal === '1' ) {
return true ;
} else if ( strVal === '0' ) {
return false ;
} else {
return null ;
}
}
}
class NumberField extends Field {
toDB ( value ) {
const numberValue = _ . toNumber ( value ) ;
if ( Number . isNaN ( numberValue ) ) {
throw new Error ( ` Expected a valid Number, got ${ value } ` ) ;
}
return numberValue ;
}
fromDB ( value ) {
return _ . toNumber ( value ) ;
}
}
class BigIntegerField extends NumberField {
toDB ( value ) {
return _ . toString ( value ) ;
}
fromDB ( value ) {
return _ . toString ( value ) ;
}
}
const timeRegex = new RegExp ( '^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$' ) ;
2022-01-18 17:12:25 +01:00
const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/ ;
2022-02-11 19:01:28 +01:00
const partialDateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])/g ;
2021-06-28 21:37:44 +02:00
const parseTime = value => {
if ( dateFns . isDate ( value ) ) return dateFns . format ( value , 'HH:mm:ss.SSS' ) ;
if ( typeof value !== 'string' ) {
2021-10-20 17:30:05 +02:00
throw new InvalidTimeError ( ` Expected a string, got a ${ typeof value } ` ) ;
2021-06-28 21:37:44 +02:00
}
const result = value . match ( timeRegex ) ;
if ( result === null ) {
2021-10-20 17:30:05 +02:00
throw new InvalidTimeError ( 'Invalid time format, expected HH:mm:ss.SSS' ) ;
2021-06-28 21:37:44 +02:00
}
const [ , hours , minutes , seconds , fraction = '.000' ] = result ;
2021-07-08 21:53:30 +02:00
const fractionPart = _ . padCharsEnd ( '0' , 3 , fraction . slice ( 1 ) ) ;
2021-06-28 21:37:44 +02:00
return ` ${ hours } : ${ minutes } : ${ seconds } . ${ fractionPart } ` ;
} ;
const parseDate = value => {
2022-02-11 19:01:28 +01:00
const found = _ . isString ( value ) ? value . match ( partialDateRegex ) : [ ] ;
const extractedValue = found [ 0 ] ;
if ( extractedValue && ! dateRegex . test ( value ) ) {
// TODO V5: throw an error when format yyyy-MM-dd is not respected
// throw new InvalidDateError(`Invalid format, expected yyyy-MM-dd`);
process . emitWarning (
` [deprecated] Using a date format other than yyyy-MM-dd will be deprecated. Use format yyyy-MM-dd instead. Value found: ${ value } . Date kept: ${ extractedValue } . `
) ;
2022-01-18 17:12:25 +01:00
}
2021-06-28 21:37:44 +02:00
2022-02-11 19:01:28 +01:00
let date = dateFns . parseISO ( extractedValue ) ;
2022-01-18 17:12:25 +01:00
if ( ! dateFns . isValid ( date ) ) {
throw new InvalidDateError ( ` Invalid date ` ) ;
2021-06-28 21:37:44 +02:00
}
2022-01-18 17:12:25 +01:00
2022-02-11 19:01:28 +01:00
return extractedValue ;
2021-06-28 21:37:44 +02:00
} ;
const parseDateTimeOrTimestamp = value => {
if ( dateFns . isDate ( value ) ) return value ;
try {
const date = dateFns . parseISO ( value ) ;
if ( dateFns . isValid ( date ) ) return date ;
const milliUnixDate = dateFns . parse ( value , 'T' , new Date ( ) ) ;
if ( dateFns . isValid ( milliUnixDate ) ) return milliUnixDate ;
2021-10-20 17:30:05 +02:00
throw new InvalidDateTimeError ( ` Invalid format, expected a timestamp or an ISO date ` ) ;
2021-06-28 21:37:44 +02:00
} catch ( error ) {
2021-10-20 17:30:05 +02:00
throw new InvalidDateTimeError ( ` Invalid format, expected a timestamp or an ISO date ` ) ;
2021-06-28 21:37:44 +02:00
}
} ;
class DateField extends Field {
toDB ( value ) {
return parseDate ( value ) ;
}
fromDB ( value ) {
2022-01-18 17:12:25 +01:00
return value ;
2021-06-28 21:37:44 +02:00
}
}
class DatetimeField extends Field {
toDB ( value ) {
return parseDateTimeOrTimestamp ( value ) ;
}
fromDB ( value ) {
const cast = new Date ( value ) ;
return dateFns . isValid ( cast ) ? cast . toISOString ( ) : null ;
}
}
class TimeField extends Field {
toDB ( value ) {
return parseTime ( value ) ;
}
fromDB ( value ) {
// make sure that's a string with valid format ?
return value ;
}
}
class TimestampField extends Field {
toDB ( value ) {
return parseDateTimeOrTimestamp ( value ) ;
}
fromDB ( value ) {
const cast = new Date ( value ) ;
return dateFns . isValid ( cast ) ? dateFns . format ( cast , 'T' ) : null ;
2021-06-28 12:34:29 +02:00
}
}
const typeToFieldMap = {
increments : Field ,
password : StringField ,
email : StringField ,
string : StringField ,
uid : StringField ,
richtext : StringField ,
text : StringField ,
enumeration : StringField ,
2021-06-28 21:37:44 +02:00
json : JSONField ,
biginteger : BigIntegerField ,
integer : NumberField ,
float : NumberField ,
decimal : NumberField ,
date : DateField ,
time : TimeField ,
datetime : DatetimeField ,
timestamp : TimestampField ,
2021-06-28 12:34:29 +02:00
boolean : BooleanField ,
2021-05-18 10:16:03 +02:00
} ;
2021-08-12 16:12:40 +02:00
const createField = attribute => {
const { type } = attribute ;
2021-06-28 12:34:29 +02:00
if ( _ . has ( type , typeToFieldMap ) ) {
return new typeToFieldMap [ type ] ( { } ) ;
}
throw new Error ( ` Undefined field for type ${ type } ` ) ;
2021-05-18 10:16:03 +02:00
} ;
module . exports = {
2021-06-28 12:34:29 +02:00
createField ,
2021-05-18 10:16:03 +02:00
} ;