2021-06-28 21:37:44 +02:00

231 lines
4.7 KiB
JavaScript

'use strict';
const _ = require('lodash/fp');
const dateFns = require('date-fns');
class Field {
constructor(config) {
this.config = config;
}
// TODO: impl
validate() {
// // use config validators directly
// if (this.config.validators) {
// this.config.validators.forEach(validator => {
// validator(value)
// })
// }
}
toDB(value) {
return value;
}
fromDB(value) {
return value;
}
}
class StringField extends Field {
toDB(value) {
return _.toString(value);
}
fromDB(value) {
return _.toString(value);
}
}
class JSONField extends Field {
toDB(value) {
return JSON.stringify(value);
}
fromDB(value) {
if (typeof value === 'string') return JSON.parse(value);
return value;
}
}
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;
}
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})?$');
const parseTime = value => {
if (dateFns.isDate(value)) return dateFns.format(value, 'HH:mm:ss.SSS');
if (typeof value !== 'string') {
throw new Error(`Expected a string, got a ${typeof value}`);
}
const result = value.match(timeRegex);
if (result === null) {
throw new Error('Invalid time format, expected HH:mm:ss.SSS');
}
const [, hours, minutes, seconds, fraction = '.000'] = result;
const fractionPart = _.padEnd(fraction.slice(1), 3, '0');
return `${hours}:${minutes}:${seconds}.${fractionPart}`;
};
const parseDate = value => {
if (dateFns.isDate(value)) return dateFns.format(value, 'yyyy-MM-dd');
try {
let date = dateFns.parseISO(value);
if (dateFns.isValid(date)) return dateFns.format(date, 'yyyy-MM-dd');
throw new Error(`Invalid format, expected an ISO compatible date`);
} catch (error) {
throw new Error(`Invalid format, expected an ISO compatible date`);
}
};
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;
throw new Error(`Invalid format, expected a timestamp or an ISO date`);
} catch (error) {
throw new Error(`Invalid format, expected a timestamp or an ISO date`);
}
};
class DateField extends Field {
toDB(value) {
return parseDate(value);
}
fromDB(value) {
const cast = new Date(value);
return dateFns.isValid(cast) ? dateFns.formatISO(cast, { representation: 'date' }) : null;
}
}
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;
}
}
const typeToFieldMap = {
increments: Field,
password: StringField,
email: StringField,
string: StringField,
uid: StringField,
richtext: StringField,
text: StringField,
enumeration: StringField,
json: JSONField,
biginteger: BigIntegerField,
integer: NumberField,
float: NumberField,
decimal: NumberField,
date: DateField,
time: TimeField,
datetime: DatetimeField,
timestamp: TimestampField,
boolean: BooleanField,
};
const createField = (type /*attribute*/) => {
if (_.has(type, typeToFieldMap)) {
return new typeToFieldMap[type]({});
}
throw new Error(`Undefined field for type ${type}`);
};
module.exports = {
createField,
};