From 26b1f057733be0ab401ee6afdb1e9d7dbbdee2d5 Mon Sep 17 00:00:00 2001 From: Leo Lamprecht Date: Thu, 27 Feb 2025 12:23:07 +0100 Subject: [PATCH] Export list of query types (#158) --- src/index.ts | 13 ++++- src/instructions/selecting.ts | 4 +- src/instructions/to.ts | 9 +--- src/instructions/using.ts | 3 +- src/model/defaults.ts | 3 +- src/model/index.ts | 7 ++- src/types/model.ts | 2 +- src/types/query.ts | 10 ++-- src/utils/constants.ts | 67 +++++++++++++++++++++++++ src/utils/helpers.ts | 54 +------------------- src/utils/index.ts | 5 +- src/utils/statement.ts | 9 +--- tests/errors.test.ts | 4 +- tests/instructions/before-after.test.ts | 3 +- tests/instructions/including.test.ts | 3 +- tests/instructions/ordered-by.test.ts | 3 +- tests/instructions/to.test.ts | 10 +++- tests/instructions/using.test.ts | 9 +++- tests/meta.test.ts | 4 +- tests/options.test.ts | 3 +- 20 files changed, 127 insertions(+), 98 deletions(-) create mode 100644 src/utils/constants.ts diff --git a/src/index.ts b/src/index.ts index 551cb52..c2b5ad5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -578,5 +578,14 @@ const CLEAN_ROOT_MODEL = omit(ROOT_MODEL, ['system']) as PublicModel; // Expose the main `Transaction` entrypoint and the root model export { Transaction, CLEAN_ROOT_MODEL as ROOT_MODEL }; -// Expose the main error class and query symbols -export { RoninError, QUERY_SYMBOLS, getQuerySymbol } from '@/src/utils/helpers'; +// Expose the main error class and helper functions +export { RoninError, getQuerySymbol } from '@/src/utils/helpers'; + +// Expose constants +export { + QUERY_SYMBOLS, + DML_READ_QUERY_TYPES, + DML_WRITE_QUERY_TYPES, + DML_QUERY_TYPES, + DDL_QUERY_TYPES, +} from '@/src/utils/constants'; diff --git a/src/instructions/selecting.ts b/src/instructions/selecting.ts index d5fde2a..f3a9b1c 100644 --- a/src/instructions/selecting.ts +++ b/src/instructions/selecting.ts @@ -2,10 +2,8 @@ import { getFieldFromModel, getModelBySlug } from '@/src/model'; import type { InternalModelField, Model, ModelField } from '@/src/types/model'; import type { Instructions } from '@/src/types/query'; import { compileQueryInput } from '@/src/utils'; +import { QUERY_SYMBOLS, RAW_FIELD_TYPES, type RawFieldType } from '@/src/utils/constants'; import { - QUERY_SYMBOLS, - RAW_FIELD_TYPES, - type RawFieldType, composeMountingPath, flatten, getQuerySymbol, diff --git a/src/instructions/to.ts b/src/instructions/to.ts index ab45dd1..2c12cbd 100644 --- a/src/instructions/to.ts +++ b/src/instructions/to.ts @@ -10,13 +10,8 @@ import type { InternalDependencyStatement, SetInstructions, } from '@/src/types/query'; -import { - CURRENT_TIME_EXPRESSION, - flatten, - getQuerySymbol, - isObject, - splitQuery, -} from '@/src/utils/helpers'; +import { CURRENT_TIME_EXPRESSION } from '@/src/utils/constants'; +import { flatten, getQuerySymbol, isObject, splitQuery } from '@/src/utils/helpers'; import { compileQueryInput } from '@/src/utils/index'; import { composeConditions, filterSelectedFields } from '@/src/utils/statement'; diff --git a/src/instructions/using.ts b/src/instructions/using.ts index 2daa537..d2161a4 100644 --- a/src/instructions/using.ts +++ b/src/instructions/using.ts @@ -1,6 +1,7 @@ import type { Model, ModelField, ModelPreset } from '@/src/types/model'; import type { Instructions, SetInstructions } from '@/src/types/query'; -import { QUERY_SYMBOLS, RoninError, findInObject, isObject } from '@/src/utils/helpers'; +import { QUERY_SYMBOLS } from '@/src/utils/constants'; +import { RoninError, findInObject, isObject } from '@/src/utils/helpers'; /** * Generates the SQL syntax for the `using` query instruction, which allows for quickly diff --git a/src/model/defaults.ts b/src/model/defaults.ts index 0364388..73120fe 100644 --- a/src/model/defaults.ts +++ b/src/model/defaults.ts @@ -1,6 +1,7 @@ import { getModelBySlug, getSystemFields } from '@/src/model'; import type { Model, ModelField, ModelPreset, PartialModel } from '@/src/types/model'; -import { QUERY_SYMBOLS, convertToSnakeCase } from '@/src/utils/helpers'; +import { QUERY_SYMBOLS } from '@/src/utils/constants'; +import { convertToSnakeCase } from '@/src/utils/helpers'; import title from 'title'; /** diff --git a/src/model/index.ts b/src/model/index.ts index 80a64f6..e3b1a93 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -25,8 +25,11 @@ import type { } from '@/src/types/query'; import { CURRENT_TIME_EXPRESSION, - MODEL_ENTITY_ERROR_CODES, + type DDL_QUERY_TYPES, QUERY_SYMBOLS, +} from '@/src/utils/constants'; +import { + MODEL_ENTITY_ERROR_CODES, RoninError, convertToCamelCase, convertToSnakeCase, @@ -470,7 +473,7 @@ export const PLURAL_MODEL_ENTITIES_VALUES = Object.values(PLURAL_MODEL_ENTITIES) const handleSystemModel = ( models: Array, dependencyStatements: Array, - action: 'create' | 'alter' | 'drop', + action: (typeof DDL_QUERY_TYPES)[number], inlineDefaults: boolean, systemModel: PartialModel, newModel?: PartialModel, diff --git a/src/types/model.ts b/src/types/model.ts index b040ed5..1b42bce 100644 --- a/src/types/model.ts +++ b/src/types/model.ts @@ -4,7 +4,7 @@ import type { Query, WithInstruction, } from '@/src/types/query'; -import type { QUERY_SYMBOLS } from '@/src/utils/helpers'; +import type { QUERY_SYMBOLS } from '@/src/utils/constants'; type ModelFieldCollation = 'BINARY' | 'NOCASE' | 'RTRIM'; diff --git a/src/types/query.ts b/src/types/query.ts index 86d75b1..5c4fe75 100644 --- a/src/types/query.ts +++ b/src/types/query.ts @@ -7,11 +7,15 @@ import type { Model as PrivateModel, PublicModel, } from '@/src/types/model'; -import { QUERY_SYMBOLS } from '@/src/utils/helpers'; +import { + type DDL_QUERY_TYPES, + type DML_QUERY_TYPES, + QUERY_SYMBOLS, +} from '@/src/utils/constants'; // Query Types -export type QueryTypeEnum = 'get' | 'set' | 'add' | 'remove' | 'count'; -export type ModelQueryTypeEnum = 'create' | 'alter' | 'drop'; +export type QueryTypeEnum = (typeof DML_QUERY_TYPES)[number]; +export type ModelQueryTypeEnum = (typeof DDL_QUERY_TYPES)[number]; export type ModelEntityEnum = 'field' | 'index' | 'trigger' | 'preset'; // Field and Expressions diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 0000000..5468014 --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,67 @@ +/** Query types used for reading data. */ +export const DML_READ_QUERY_TYPES = ['get', 'count'] as const; + +/** Query types used for writing data. */ +export const DML_WRITE_QUERY_TYPES = ['set', 'add', 'remove'] as const; + +/** Query types used for interacting with data. */ +export const DML_QUERY_TYPES = [ + ...DML_READ_QUERY_TYPES, + ...DML_WRITE_QUERY_TYPES, +] as const; + +/** Query types used for interacting with the database schema. */ +export const DDL_QUERY_TYPES = ['create', 'alter', 'drop'] as const; + +/** + * A list of placeholders that can be located inside queries after those queries were + * serialized into JSON objects. + * + * These placeholders are used to represent special keys and values. For example, if a + * query is nested into a query, the nested query will be marked with `__RONIN_QUERY`, + * which allows for distinguishing that nested query from an object of instructions. + */ +export const QUERY_SYMBOLS = { + // Represents a sub query. + QUERY: '__RONIN_QUERY', + + // Represents an expression that should be evaluated. + EXPRESSION: '__RONIN_EXPRESSION', + + // Represents the value of a field in the model. + FIELD: '__RONIN_FIELD_', + + // Represents the value of a field in the model of a parent query. + FIELD_PARENT: '__RONIN_FIELD_PARENT_', + + // Represents the old value of a field in the parent model. Used for triggers. + FIELD_PARENT_OLD: '__RONIN_FIELD_PARENT_OLD_', + + // Represents the new value of a field in the parent model. Used for triggers. + FIELD_PARENT_NEW: '__RONIN_FIELD_PARENT_NEW_', + + // Represents a value provided to a query preset. + VALUE: '__RONIN_VALUE', +} as const; + +/** + * A regular expression for matching the symbol that represents a field of a model. + */ +export const RONIN_MODEL_FIELD_REGEX = new RegExp( + `${QUERY_SYMBOLS.FIELD}[_a-zA-Z0-9.]+`, + 'g', +); + +// JavaScript types that can directly be used as field types in RONIN. +export const RAW_FIELD_TYPES = ['string', 'number', 'boolean'] as const; +export type RawFieldType = (typeof RAW_FIELD_TYPES)[number]; + +// An expression that produces a timestamp in the format "YYYY-MM-DDTHH:MM:SS.SSSZ", +// which matches the output of `new Date().toISOString()` in JavaScript (ISO 8601). +export const CURRENT_TIME_EXPRESSION = { + [QUERY_SYMBOLS.EXPRESSION]: `strftime('%Y-%m-%dT%H:%M:%f', 'now') || 'Z'`, +}; + +// A regular expression for splitting up the components of a field mounting path, meaning +// the path within a record under which a particular field's value should be mounted. +export const MOUNTING_PATH_SUFFIX = /(.*?)(\{(\d+)\})?$/; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index dec6636..21d035f 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -4,59 +4,7 @@ import type { QuerySchemaType, QueryType, } from '@/src/types/query'; - -/** - * A list of placeholders that can be located inside queries after those queries were - * serialized into JSON objects. - * - * These placeholders are used to represent special keys and values. For example, if a - * query is nested into a query, the nested query will be marked with `__RONIN_QUERY`, - * which allows for distinguishing that nested query from an object of instructions. - */ -export const QUERY_SYMBOLS = { - // Represents a sub query. - QUERY: '__RONIN_QUERY', - - // Represents an expression that should be evaluated. - EXPRESSION: '__RONIN_EXPRESSION', - - // Represents the value of a field in the model. - FIELD: '__RONIN_FIELD_', - - // Represents the value of a field in the model of a parent query. - FIELD_PARENT: '__RONIN_FIELD_PARENT_', - - // Represents the old value of a field in the parent model. Used for triggers. - FIELD_PARENT_OLD: '__RONIN_FIELD_PARENT_OLD_', - - // Represents the new value of a field in the parent model. Used for triggers. - FIELD_PARENT_NEW: '__RONIN_FIELD_PARENT_NEW_', - - // Represents a value provided to a query preset. - VALUE: '__RONIN_VALUE', -} as const; - -/** - * A regular expression for matching the symbol that represents a field of a model. - */ -export const RONIN_MODEL_FIELD_REGEX = new RegExp( - `${QUERY_SYMBOLS.FIELD}[_a-zA-Z0-9.]+`, - 'g', -); - -// JavaScript types that can directly be used as field types in RONIN. -export const RAW_FIELD_TYPES = ['string', 'number', 'boolean'] as const; -export type RawFieldType = (typeof RAW_FIELD_TYPES)[number]; - -// An expression that produces a timestamp in the format "YYYY-MM-DDTHH:MM:SS.SSSZ", -// which matches the output of `new Date().toISOString()` in JavaScript (ISO 8601). -export const CURRENT_TIME_EXPRESSION = { - [QUERY_SYMBOLS.EXPRESSION]: `strftime('%Y-%m-%dT%H:%M:%f', 'now') || 'Z'`, -}; - -// A regular expression for splitting up the components of a field mounting path, meaning -// the path within a record under which a particular field's value should be mounted. -const MOUNTING_PATH_SUFFIX = /(.*?)(\{(\d+)\})?$/; +import { MOUNTING_PATH_SUFFIX, QUERY_SYMBOLS } from '@/src/utils/constants'; /** * Determines the mounting path and table alias for a sub query. diff --git a/src/utils/index.ts b/src/utils/index.ts index 6bbafe9..40af201 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -20,7 +20,8 @@ import type { SetInstructions, Statement, } from '@/src/types/query'; -import { QUERY_SYMBOLS, RoninError, isObject, splitQuery } from '@/src/utils/helpers'; +import { DML_WRITE_QUERY_TYPES, QUERY_SYMBOLS } from '@/src/utils/constants'; +import { RoninError, isObject, splitQuery } from '@/src/utils/helpers'; import { formatIdentifiers } from '@/src/utils/statement'; /** @@ -321,7 +322,7 @@ export const compileQueryInput = ( // For queries that modify records, we want to make sure that the modified record is // returned after the modification has been performed. - if (['add', 'set', 'remove'].includes(queryType) && returning) { + if ((DML_WRITE_QUERY_TYPES as ReadonlyArray).includes(queryType) && returning) { statement += `RETURNING ${columns}`; } diff --git a/src/utils/statement.ts b/src/utils/statement.ts index 4c46b45..37e5aa4 100644 --- a/src/utils/statement.ts +++ b/src/utils/statement.ts @@ -18,13 +18,8 @@ import type { SetInstructions, WithInstruction, } from '@/src/types/query'; -import { - QUERY_SYMBOLS, - RONIN_MODEL_FIELD_REGEX, - RoninError, - getQuerySymbol, - isObject, -} from '@/src/utils/helpers'; +import { QUERY_SYMBOLS, RONIN_MODEL_FIELD_REGEX } from '@/src/utils/constants'; +import { RoninError, getQuerySymbol, isObject } from '@/src/utils/helpers'; import { compileQueryInput } from '@/src/utils/index'; /** diff --git a/tests/errors.test.ts b/tests/errors.test.ts index 92e104e..3fdd2c9 100644 --- a/tests/errors.test.ts +++ b/tests/errors.test.ts @@ -1,7 +1,5 @@ import { expect, test } from 'bun:test'; -import { type Model, type Query, Transaction } from '@/src/index'; - -import { RoninError } from '@/src/utils/helpers'; +import { type Model, type Query, RoninError, Transaction } from '@/src/index'; test('get single record with non-existing field', () => { const queries: Array = [ diff --git a/tests/instructions/before-after.test.ts b/tests/instructions/before-after.test.ts index b140884..675cdc1 100644 --- a/tests/instructions/before-after.test.ts +++ b/tests/instructions/before-after.test.ts @@ -4,9 +4,8 @@ import { RECORD_TIMESTAMP_REGEX, queryEphemeralDatabase, } from '@/fixtures/utils'; -import { type Model, type Query, Transaction } from '@/src/index'; +import { type Model, type Query, RoninError, Transaction } from '@/src/index'; import type { AmountResult, MultipleRecordResult } from '@/src/types/result'; -import { RoninError } from '@/src/utils/helpers'; import { CURSOR_NULL_PLACEHOLDER } from '@/src/utils/pagination'; test('get multiple records before cursor', async () => { diff --git a/tests/instructions/including.test.ts b/tests/instructions/including.test.ts index b0bcc31..d9bf589 100644 --- a/tests/instructions/including.test.ts +++ b/tests/instructions/including.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'bun:test'; -import { type Model, type Query, Transaction } from '@/src/index'; +import { type Model, QUERY_SYMBOLS, type Query, Transaction } from '@/src/index'; import { RECORD_ID_REGEX, @@ -7,7 +7,6 @@ import { queryEphemeralDatabase, } from '@/fixtures/utils'; import type { MultipleRecordResult, SingleRecordResult } from '@/src/types/result'; -import { QUERY_SYMBOLS } from '@/src/utils/helpers'; test('get single record including unrelated record without filter', async () => { const queries: Array = [ diff --git a/tests/instructions/ordered-by.test.ts b/tests/instructions/ordered-by.test.ts index 95e1b9d..ba8ab72 100644 --- a/tests/instructions/ordered-by.test.ts +++ b/tests/instructions/ordered-by.test.ts @@ -1,8 +1,7 @@ import { expect, test } from 'bun:test'; import { queryEphemeralDatabase } from '@/fixtures/utils'; -import { type Model, type Query, Transaction } from '@/src/index'; +import { type Model, QUERY_SYMBOLS, type Query, Transaction } from '@/src/index'; import type { MultipleRecordResult } from '@/src/types/result'; -import { QUERY_SYMBOLS } from '@/src/utils/helpers'; test('get multiple records ordered by field', async () => { const queries: Array = [ diff --git a/tests/instructions/to.test.ts b/tests/instructions/to.test.ts index eac840a..641e978 100644 --- a/tests/instructions/to.test.ts +++ b/tests/instructions/to.test.ts @@ -1,9 +1,15 @@ import { expect, test } from 'bun:test'; -import { type Model, type Query, type StoredObject, Transaction } from '@/src/index'; +import { + type Model, + QUERY_SYMBOLS, + type Query, + RoninError, + type StoredObject, + Transaction, +} from '@/src/index'; import { RECORD_TIMESTAMP_REGEX, queryEphemeralDatabase } from '@/fixtures/utils'; import type { MultipleRecordResult, SingleRecordResult } from '@/src/types/result'; -import { QUERY_SYMBOLS, RoninError } from '@/src/utils/helpers'; test('set single record to new string field', async () => { const queries: Array = [ diff --git a/tests/instructions/using.test.ts b/tests/instructions/using.test.ts index 3f1deda..f81db2c 100644 --- a/tests/instructions/using.test.ts +++ b/tests/instructions/using.test.ts @@ -1,5 +1,11 @@ import { expect, test } from 'bun:test'; -import { type Model, type Query, Transaction } from '@/src/index'; +import { + type Model, + QUERY_SYMBOLS, + type Query, + RoninError, + Transaction, +} from '@/src/index'; import { RECORD_ID_REGEX, @@ -7,7 +13,6 @@ import { queryEphemeralDatabase, } from '@/fixtures/utils'; import type { SingleRecordResult } from '@/src/types/result'; -import { QUERY_SYMBOLS, RoninError } from '@/src/utils/helpers'; test('get single record using preset', async () => { const queries: Array = [ diff --git a/tests/meta.test.ts b/tests/meta.test.ts index fd4ffad..1f61b5d 100644 --- a/tests/meta.test.ts +++ b/tests/meta.test.ts @@ -5,8 +5,10 @@ import { type ModelIndex, type ModelPreset, type ModelTrigger, + QUERY_SYMBOLS, type Query, ROOT_MODEL, + RoninError, Transaction, } from '@/src/index'; @@ -18,7 +20,7 @@ import { import { getSystemFields } from '@/src/model'; import { slugToName } from '@/src/model/defaults'; import type { MultipleRecordResult } from '@/src/types/result'; -import { QUERY_SYMBOLS, RoninError, omit } from '@/src/utils/helpers'; +import { omit } from '@/src/utils/helpers'; test('create new model', () => { const fields: Model['fields'] = { diff --git a/tests/options.test.ts b/tests/options.test.ts index 968c441..3dccc76 100644 --- a/tests/options.test.ts +++ b/tests/options.test.ts @@ -4,10 +4,9 @@ import { RECORD_TIMESTAMP_REGEX, queryEphemeralDatabase, } from '@/fixtures/utils'; -import { type Model, type Query, Transaction } from '@/src/index'; +import { type Model, QUERY_SYMBOLS, type Query, Transaction } from '@/src/index'; import { getSystemFields } from '@/src/model'; import type { SingleRecordResult } from '@/src/types/result'; -import { QUERY_SYMBOLS } from '@/src/utils/helpers'; test('inline statement parameters', async () => { const queries: Array = [