diff --git a/packages/cormo/lib/adapters/mongodb.js b/packages/cormo/lib/adapters/mongodb.js index 0849bffe..90b9c5bc 100644 --- a/packages/cormo/lib/adapters/mongodb.js +++ b/packages/cormo/lib/adapters/mongodb.js @@ -822,6 +822,9 @@ class MongoDBAdapter extends base_1.AdapterBase { return value && _objectIdToString(value); } } + else if (property.type_class === types.Blob) { + return value.read(0, value.length); + } else { return value; } diff --git a/packages/cormo/lib/adapters/mysql.js b/packages/cormo/lib/adapters/mysql.js index 97d56ef4..5720263c 100644 --- a/packages/cormo/lib/adapters/mysql.js +++ b/packages/cormo/lib/adapters/mysql.js @@ -76,6 +76,8 @@ function _typeToSQL(property, support_fractional_seconds, major_version) { return 'TEXT'; case types.Text: return 'TEXT'; + case types.Blob: + return 'BLOB'; } } function _propertyToSQL(property, support_fractional_seconds, major_version) { @@ -1015,7 +1017,9 @@ class MySQLAdapter extends sql_base_1.SQLAdapterBase { ? new types.Date() : /^text/i.test(column.Type) ? new types.Text() - : undefined; + : /^blob/i.test(column.Type) + ? new types.Blob() + : undefined; schema.columns[column.Field] = { required: column.Null === 'NO', type, diff --git a/packages/cormo/lib/adapters/postgresql.js b/packages/cormo/lib/adapters/postgresql.js index 9c244406..54cb5b9b 100644 --- a/packages/cormo/lib/adapters/postgresql.js +++ b/packages/cormo/lib/adapters/postgresql.js @@ -70,6 +70,8 @@ function _typeToSQL(property) { return 'JSON'; case types.Text: return 'TEXT'; + case types.Blob: + return 'BYTEA'; } } function _propertyToSQL(property) { @@ -742,7 +744,9 @@ class PostgreSQLAdapter extends sql_base_1.SQLAdapterBase { ? new types.Object() : column.data_type === 'text' ? new types.Text() - : undefined; + : column.data_type === 'bytea' + ? new types.Blob() + : undefined; let adapter_type_string = column.data_type.toUpperCase(); if (column.data_type === 'character varying') { adapter_type_string += `(${column.character_maximum_length || 255})`; diff --git a/packages/cormo/lib/adapters/sqlite3.js b/packages/cormo/lib/adapters/sqlite3.js index c2014fd2..569da084 100644 --- a/packages/cormo/lib/adapters/sqlite3.js +++ b/packages/cormo/lib/adapters/sqlite3.js @@ -62,6 +62,8 @@ function _typeToSQL(property) { return 'TEXT'; case types.Text: return 'TEXT'; + case types.Blob: + return 'BLOB'; } } function _propertyToSQL(property) { @@ -695,7 +697,9 @@ class SQLite3Adapter extends sql_base_1.SQLAdapterBase { ? new types.Date() : /^text/i.test(column.type) ? new types.Text() - : undefined; + : /^blob/i.test(column.type) + ? new types.Blob() + : undefined; schema.columns[column.name] = { required: column.notnull === 1, type, diff --git a/packages/cormo/lib/types.d.ts b/packages/cormo/lib/types.d.ts index 64a854f8..0dafa875 100644 --- a/packages/cormo/lib/types.d.ts +++ b/packages/cormo/lib/types.d.ts @@ -139,17 +139,30 @@ interface CormoTypesTextConstructor { (): CormoTypesText; } declare const CormoTypesText: CormoTypesTextConstructor; -export type ColumnTypeInternal = CormoTypesString | CormoTypesNumber | CormoTypesBoolean | CormoTypesDate | CormoTypesObject | CormoTypesInteger | CormoTypesBigInteger | CormoTypesGeoPoint | CormoTypesRecordID | CormoTypesText; -export type ColumnTypeInternalConstructor = CormoTypesStringConstructor | CormoTypesNumberConstructor | CormoTypesBooleanConstructor | CormoTypesDateConstructor | CormoTypesObjectConstructor | CormoTypesIntegerConstructor | CormoTypesBigIntegerConstructor | CormoTypesGeoPointConstructor | CormoTypesRecordIDConstructor | CormoTypesTextConstructor; +/** + * Represents a blob, used in model schemas. + * @namespace types + * @class Blob + */ +export interface CormoTypesBlob { + _type: 'blob'; +} +interface CormoTypesBlobConstructor { + new (): CormoTypesBlob; + (): CormoTypesBlob; +} +declare const CormoTypesBlob: CormoTypesBlobConstructor; +export type ColumnTypeInternal = CormoTypesString | CormoTypesNumber | CormoTypesBoolean | CormoTypesDate | CormoTypesObject | CormoTypesInteger | CormoTypesBigInteger | CormoTypesGeoPoint | CormoTypesRecordID | CormoTypesText | CormoTypesBlob; +export type ColumnTypeInternalConstructor = CormoTypesStringConstructor | CormoTypesNumberConstructor | CormoTypesBooleanConstructor | CormoTypesDateConstructor | CormoTypesObjectConstructor | CormoTypesIntegerConstructor | CormoTypesBigIntegerConstructor | CormoTypesGeoPointConstructor | CormoTypesRecordIDConstructor | CormoTypesTextConstructor | CormoTypesBlobConstructor; type ColumnTypeNativeConstructor = StringConstructor | NumberConstructor | BooleanConstructor | DateConstructor | ObjectConstructor; -type ColumnTypeString = 'string' | 'number' | 'boolean' | 'date' | 'object' | 'integer' | 'biginteger' | 'geopoint' | 'recordid' | 'text'; +type ColumnTypeString = 'string' | 'number' | 'boolean' | 'date' | 'object' | 'integer' | 'biginteger' | 'geopoint' | 'recordid' | 'text' | 'blob'; export type ColumnType = ColumnTypeInternal | ColumnTypeInternalConstructor | ColumnTypeNativeConstructor | ColumnTypeString; /** * Converts JavaScript built-in class to CORMO type * @private */ declare function _toCORMOType(type: ColumnType): ColumnTypeInternal; -export { CormoTypesString as String, CormoTypesNumber as Number, CormoTypesBoolean as Boolean, CormoTypesInteger as Integer, CormoTypesBigInteger as BigInteger, CormoTypesGeoPoint as GeoPoint, CormoTypesDate as Date, CormoTypesObject as Object, CormoTypesRecordID as RecordID, CormoTypesText as Text, _toCORMOType, }; +export { CormoTypesString as String, CormoTypesNumber as Number, CormoTypesBoolean as Boolean, CormoTypesInteger as Integer, CormoTypesBigInteger as BigInteger, CormoTypesGeoPoint as GeoPoint, CormoTypesDate as Date, CormoTypesObject as Object, CormoTypesRecordID as RecordID, CormoTypesText as Text, CormoTypesBlob as Blob, _toCORMOType, }; /** * A pseudo type represents a record's unique identifier. * diff --git a/packages/cormo/lib/types.js b/packages/cormo/lib/types.js index 066951b6..69fb834e 100644 --- a/packages/cormo/lib/types.js +++ b/packages/cormo/lib/types.js @@ -5,7 +5,7 @@ * @namespace cormo */ Object.defineProperty(exports, "__esModule", { value: true }); -exports._toCORMOType = exports.Text = exports.RecordID = exports.Object = exports.Date = exports.GeoPoint = exports.BigInteger = exports.Integer = exports.Boolean = exports.Number = exports.String = void 0; +exports._toCORMOType = exports.Blob = exports.Text = exports.RecordID = exports.Object = exports.Date = exports.GeoPoint = exports.BigInteger = exports.Integer = exports.Boolean = exports.Number = exports.String = void 0; const CormoTypesString = function (length) { if (!(this instanceof CormoTypesString)) { return new CormoTypesString(length); @@ -77,6 +77,13 @@ const CormoTypesText = function () { this.toString = () => 'text'; }; exports.Text = CormoTypesText; +const CormoTypesBlob = function () { + if (!(this instanceof CormoTypesBlob)) { + return new CormoTypesBlob(); + } + this.toString = () => 'blob'; +}; +exports.Blob = CormoTypesBlob; /** * Converts JavaScript built-in class to CORMO type * @private @@ -108,6 +115,8 @@ function _toCORMOType(type) { return new CormoTypesRecordID(); case 'text': return new CormoTypesText(); + case 'blob': + return new CormoTypesBlob(); } throw new Error(`unknown type: ${type}`); } diff --git a/packages/cormo/src/adapters/mongodb.ts b/packages/cormo/src/adapters/mongodb.ts index 6ebf351d..97bee5a5 100644 --- a/packages/cormo/src/adapters/mongodb.ts +++ b/packages/cormo/src/adapters/mongodb.ts @@ -856,6 +856,8 @@ export class MongoDBAdapter extends AdapterBase { } else { return value && _objectIdToString(value); } + } else if (property.type_class === types.Blob) { + return value.read(0, value.length); } else { return value; } diff --git a/packages/cormo/src/adapters/mysql.ts b/packages/cormo/src/adapters/mysql.ts index 0ea3e01b..0be74b51 100644 --- a/packages/cormo/src/adapters/mysql.ts +++ b/packages/cormo/src/adapters/mysql.ts @@ -90,6 +90,8 @@ function _typeToSQL(property: ColumnPropertyInternal, support_fractional_seconds return 'TEXT'; case types.Text: return 'TEXT'; + case types.Blob: + return 'BLOB'; } } @@ -1101,7 +1103,9 @@ export class MySQLAdapter extends SQLAdapterBase { ? new types.Date() : /^text/i.test(column.Type) ? new types.Text() - : undefined; + : /^blob/i.test(column.Type) + ? new types.Blob() + : undefined; schema.columns[column.Field] = { required: column.Null === 'NO', type, diff --git a/packages/cormo/src/adapters/postgresql.ts b/packages/cormo/src/adapters/postgresql.ts index 331eeda0..b6f95985 100644 --- a/packages/cormo/src/adapters/postgresql.ts +++ b/packages/cormo/src/adapters/postgresql.ts @@ -63,6 +63,8 @@ function _typeToSQL(property: ColumnPropertyInternal) { return 'JSON'; case types.Text: return 'TEXT'; + case types.Blob: + return 'BYTEA'; } } @@ -793,7 +795,9 @@ export class PostgreSQLAdapter extends SQLAdapterBase { ? new types.Object() : column.data_type === 'text' ? new types.Text() - : undefined; + : column.data_type === 'bytea' + ? new types.Blob() + : undefined; let adapter_type_string = column.data_type.toUpperCase(); if (column.data_type === 'character varying') { adapter_type_string += `(${column.character_maximum_length || 255})`; diff --git a/packages/cormo/src/adapters/sqlite3.ts b/packages/cormo/src/adapters/sqlite3.ts index b84586f4..c9a9c90d 100644 --- a/packages/cormo/src/adapters/sqlite3.ts +++ b/packages/cormo/src/adapters/sqlite3.ts @@ -51,6 +51,8 @@ function _typeToSQL(property: ColumnPropertyInternal) { return 'TEXT'; case types.Text: return 'TEXT'; + case types.Blob: + return 'BLOB'; } } @@ -740,7 +742,9 @@ export class SQLite3Adapter extends SQLAdapterBase { ? new types.Date() : /^text/i.test(column.type) ? new types.Text() - : undefined; + : /^blob/i.test(column.type) + ? new types.Blob() + : undefined; schema.columns[column.name] = { required: column.notnull === 1, type, diff --git a/packages/cormo/src/types.ts b/packages/cormo/src/types.ts index fe1a8dc0..68eee2c1 100644 --- a/packages/cormo/src/types.ts +++ b/packages/cormo/src/types.ts @@ -221,6 +221,27 @@ const CormoTypesText: CormoTypesTextConstructor = function (this: CormoTypesText this.toString = () => 'text'; } as CormoTypesTextConstructor; +/** + * Represents a blob, used in model schemas. + * @namespace types + * @class Blob + */ +export interface CormoTypesBlob { + _type: 'blob'; +} + +interface CormoTypesBlobConstructor { + new (): CormoTypesBlob; + (): CormoTypesBlob; +} + +const CormoTypesBlob: CormoTypesBlobConstructor = function (this: CormoTypesBlob): void { + if (!(this instanceof CormoTypesBlob)) { + return new (CormoTypesBlob as any)(); + } + this.toString = () => 'blob'; +} as CormoTypesBlobConstructor; + export type ColumnTypeInternal = | CormoTypesString | CormoTypesNumber @@ -231,7 +252,8 @@ export type ColumnTypeInternal = | CormoTypesBigInteger | CormoTypesGeoPoint | CormoTypesRecordID - | CormoTypesText; + | CormoTypesText + | CormoTypesBlob; export type ColumnTypeInternalConstructor = | CormoTypesStringConstructor @@ -243,7 +265,8 @@ export type ColumnTypeInternalConstructor = | CormoTypesBigIntegerConstructor | CormoTypesGeoPointConstructor | CormoTypesRecordIDConstructor - | CormoTypesTextConstructor; + | CormoTypesTextConstructor + | CormoTypesBlobConstructor; type ColumnTypeNativeConstructor = | StringConstructor @@ -262,7 +285,8 @@ type ColumnTypeString = | 'biginteger' | 'geopoint' | 'recordid' - | 'text'; + | 'text' + | 'blob'; export type ColumnType = | ColumnTypeInternal @@ -301,6 +325,8 @@ function _toCORMOType(type: ColumnType): ColumnTypeInternal { return new CormoTypesRecordID(); case 'text': return new CormoTypesText(); + case 'blob': + return new CormoTypesBlob(); } throw new Error(`unknown type: ${type}`); } else if (type === String) { @@ -331,6 +357,7 @@ export { CormoTypesObject as Object, CormoTypesRecordID as RecordID, CormoTypesText as Text, + CormoTypesBlob as Blob, _toCORMOType, }; diff --git a/packages/cormo/test/cases/type.ts b/packages/cormo/test/cases/type.ts index 547e4494..3f74f0aa 100644 --- a/packages/cormo/test/cases/type.ts +++ b/packages/cormo/test/cases/type.ts @@ -13,6 +13,7 @@ export class Type extends cormo.BaseModel { public recordid?: any; public recordid_array?: any[]; public text?: string; + public blob?: Buffer; } export default function (models: { Type: typeof Type; connection: cormo.Connection | null }) { @@ -268,4 +269,11 @@ export default function (models: { Type: typeof Type; connection: cormo.Connecti expect(record.recordid_array).to.eql(type_ids.map((id) => (id ? String(id) : null))); expect(record_lean.recordid_array).to.eql(type_ids.map((id) => (id ? String(id) : null))); }); + + it('blob', async () => { + let type = await models.Type.create({ blob: Buffer.from([1, 2, 3, 4]) }); + expect(type.blob).to.deep.equal(Buffer.from([1, 2, 3, 4])); + type = await models.Type.find(type.id); + expect(type.blob).to.deep.equal(Buffer.from([1, 2, 3, 4])); + }); } diff --git a/packages/cormo/test/type.ts b/packages/cormo/test/type.ts index 7341e22d..380dd188 100644 --- a/packages/cormo/test/type.ts +++ b/packages/cormo/test/type.ts @@ -56,6 +56,9 @@ _dbs.forEach((db) => { @cormo.Column('text') public text?: string; + + @cormo.Column('blob') + public blob?: Buffer; } models.Type = Type; } else { @@ -71,6 +74,7 @@ _dbs.forEach((db) => { recordid_array: [_g.cormo.types.RecordID], string: String, text: _g.cormo.types.Text, + blob: _g.cormo.types.Blob, }); }