diff --git a/example_config.json b/example_config.json index 88374b8..8839f11 100644 --- a/example_config.json +++ b/example_config.json @@ -147,6 +147,12 @@ "name": "created_at", "type": "datetime", "supportedOperations": ["lessThan", "greaterThan", "sortable"] + }, + { + "name": "decimal_number", + "type": "decimal", + "supportedOperations": ["lessThan", "greaterThan", "sortable"], + "supportedAggregation": ["count"] } ], "indexes": [ diff --git a/src/interfaces/config.ts b/src/interfaces/config.ts index b23016a..322f0b9 100644 --- a/src/interfaces/config.ts +++ b/src/interfaces/config.ts @@ -2,7 +2,13 @@ import {HTTPMethod} from './index'; export type DBEngine = 'sqlite' | 'pg'; export type CacheDbEngine = 'redis'; -export type DataType = 'integer' | 'string' | 'boolean' | 'text' | 'datetime'; +export type DataType = + | 'integer' + | 'string' + | 'boolean' + | 'text' + | 'datetime' + | 'decimal'; export type LogLevel = | 'trace' | 'debug' diff --git a/src/migrator/index.ts b/src/migrator/index.ts index d1e6aac..5ccd78c 100644 --- a/src/migrator/index.ts +++ b/src/migrator/index.ts @@ -42,6 +42,9 @@ function generateSchemaFile(config: ModelConfig, engine: DBEngine): string { case 'datetime': col = `integer('${f.name}', { mode: 'timestamp' })`; break; + case 'decimal': + col = `real('${f.name}')`; + break; default: col = `text('${f.name}')`; break; // fallback @@ -96,6 +99,9 @@ ${columns} case 'datetime': col = `timestamp('${f.name}')`; break; + case 'decimal': + col = `doublePrecision('${f.name}')`; + break; default: col = `text('${f.name}')`; break; // fallback diff --git a/src/routes/custom-queries/custom-queries.ts b/src/routes/custom-queries/custom-queries.ts index 302d6b8..3b21f2b 100644 --- a/src/routes/custom-queries/custom-queries.ts +++ b/src/routes/custom-queries/custom-queries.ts @@ -35,6 +35,8 @@ const cast = (value: unknown, type: DataType): unknown => { return String(value); case 'datetime': return String(value); + case 'decimal': + return Number(value); default: return String(value); } diff --git a/src/routes/schema-helpers.ts b/src/routes/schema-helpers.ts index 34ab365..cd58de8 100644 --- a/src/routes/schema-helpers.ts +++ b/src/routes/schema-helpers.ts @@ -24,6 +24,8 @@ export function mapDataTypeToJsonSchema(type: DataType): { return {type: 'string'}; case 'datetime': return {type: 'string', format: 'date-time'}; + case 'decimal': + return {type: 'number'}; default: return {type: 'string'}; } diff --git a/src/validators/config/schema.ts b/src/validators/config/schema.ts index be7a4a8..a49d0bd 100644 --- a/src/validators/config/schema.ts +++ b/src/validators/config/schema.ts @@ -184,7 +184,7 @@ const fieldSchema = { }, type: { type: 'string', - enum: ['integer', 'string', 'boolean', 'text', 'datetime'], + enum: ['integer', 'string', 'boolean', 'text', 'datetime', 'decimal'], }, primaryKey: {type: 'boolean', default: false}, nullable: {type: 'boolean', default: true}, diff --git a/src/validators/config/validate-model.ts b/src/validators/config/validate-model.ts index 849b089..f0bc291 100644 --- a/src/validators/config/validate-model.ts +++ b/src/validators/config/validate-model.ts @@ -19,6 +19,18 @@ const ALLOWED_OPERATIONS: Record = { 'oneOf', 'indexable', ], + decimal: [ + 'sortable', + 'editable', + 'deletable', + 'lessThan', + 'lessThanEqual', + 'greaterThan', + 'greaterThanEqual', + 'equal', + 'oneOf', + 'indexable', + ], string: [ 'searchable', 'sortable', @@ -43,6 +55,7 @@ const ALLOWED_OPERATIONS: Record = { const ALLOWED_AGGREGATIONS: Record = { integer: ['mean', 'max', 'min', 'count', 'sum'], + decimal: ['mean', 'max', 'min', 'count', 'sum'], string: ['count'], boolean: ['count', 'frequency'], text: [], @@ -53,6 +66,8 @@ function mapModelTypeToJsonSchema(type: string): string { switch (type) { case 'integer': return 'integer'; + case 'decimal': + return 'number'; case 'string': case 'text': return 'string'; diff --git a/tests/routes/custom-queries.test.ts b/tests/routes/custom-queries.test.ts index 0becf28..950489a 100644 --- a/tests/routes/custom-queries.test.ts +++ b/tests/routes/custom-queries.test.ts @@ -88,7 +88,7 @@ describe('test custom-queries api', () => { method: 'POST', path: '/all-types', query: - 'INSERT INTO test (b, t, d) VALUES (@@b:boolean@@, @@t:text@@, @@d:datetime@@);', + 'INSERT INTO test (b, t, d, dec) VALUES (@@b:boolean@@, @@t:text@@, @@d:datetime@@, @@dec:decimal@@);', }, ], }; @@ -106,6 +106,7 @@ describe('test custom-queries api', () => { b: true, t: 'some long text', d: '2023-01-01T00:00:00Z', + dec: 12.34, }, }); diff --git a/tests/routes/schema-helper.test.ts b/tests/routes/schema-helper.test.ts index 72d5ac6..43d5040 100644 --- a/tests/routes/schema-helper.test.ts +++ b/tests/routes/schema-helper.test.ts @@ -22,6 +22,7 @@ describe('test schema helper', () => { dataType: 'datetime', expectedSchema: {type: 'string', format: 'date-time'}, }, + {dataType: 'decimal', expectedSchema: {type: 'number'}}, {dataType: 'array', expectedSchema: {type: 'string'}}, {dataType: 'null', expectedSchema: {type: 'string'}}, ])('should map $dataType to JSON schema', ({dataType, expectedSchema}) => { diff --git a/tests/validators/config.test.ts b/tests/validators/config.test.ts index e768b9d..2edc494 100644 --- a/tests/validators/config.test.ts +++ b/tests/validators/config.test.ts @@ -559,6 +559,17 @@ describe('validateInvalidModelFieldsConfig', () => { expected: '/models/0/fields/0/supportedOperations: "searchable" is not allowed for type "integer"', }, + { + name: 'field.supportedOperations contains invalid value for type=decimal', + patch: { + name: 'test', + fields: [ + {name: 'test', type: 'decimal', supportedOperations: ['searchable']}, + ], + }, + expected: + '/models/0/fields/0/supportedOperations: "searchable" is not allowed for type "decimal"', + }, { name: 'field.supportedOperations contains invalid value for type=string', patch: { @@ -927,6 +938,17 @@ describe('validateInvalidModelFieldsConfig', () => { expected: '/models/0/fields/0/supportedAggregation: "frequency" is not allowed for type "integer"', }, + { + name: 'field.supportedAggregation contains invalid value for type=decimal', + patch: { + name: 'test', + fields: [ + {name: 'test', type: 'decimal', supportedAggregation: ['frequency']}, + ], + }, + expected: + '/models/0/fields/0/supportedAggregation: "frequency" is not allowed for type "decimal"', + }, { name: 'field.supportedAggregation contains invalid value for type=string', patch: {