diff --git a/packages/core/database/src/connection.ts b/packages/core/database/src/connection.ts index 10714da88b..4826d5b900 100644 --- a/packages/core/database/src/connection.ts +++ b/packages/core/database/src/connection.ts @@ -1,11 +1,9 @@ -/* eslint-disable import/no-extraneous-dependencies */ - import knex, { Knex } from 'knex'; - import SqliteClient from 'knex/lib/dialects/sqlite3/index'; class LegacySqliteClient extends SqliteClient { _driver() { + /* eslint-disable-next-line import/no-extraneous-dependencies */ return require('sqlite3'); } } diff --git a/packages/core/database/src/entity-manager/entity-repository.ts b/packages/core/database/src/entity-manager/entity-repository.ts index 237e42927c..c1924c7462 100644 --- a/packages/core/database/src/entity-manager/entity-repository.ts +++ b/packages/core/database/src/entity-manager/entity-repository.ts @@ -1,6 +1,6 @@ import { isString } from 'lodash/fp'; import type { Database } from '..'; -import type { ID } from '../typings'; +import type { ID } from '../types'; type Params = Record; type Data = Record; diff --git a/packages/core/database/src/entity-manager/index.ts b/packages/core/database/src/entity-manager/index.ts index 74c6c5a484..439444ea6f 100644 --- a/packages/core/database/src/entity-manager/index.ts +++ b/packages/core/database/src/entity-manager/index.ts @@ -26,7 +26,7 @@ import { } from 'lodash/fp'; import { mapAsync } from '@strapi/utils'; -import * as types from '../types'; +import * as types from '../utils/types'; import { createField } from '../fields'; import { createQueryBuilder } from '../query'; import { createRepository } from './entity-repository'; @@ -38,7 +38,7 @@ import { isOneToAny, hasOrderColumn, hasInverseOrderColumn, -} from '../metadata/relations'; +} from '../metadata'; import { deletePreviousOneToAnyRelations, deletePreviousAnyToOneRelations, @@ -52,8 +52,8 @@ import { } from './relations/cloning/regular-relations'; import { DatabaseError } from '../errors'; import type { Database } from '..'; -import type { Meta } from '../metadata/types'; -import type { ID } from '../typings'; +import type { Meta } from '../metadata'; +import type { ID } from '../types'; type Params = { where?: any; diff --git a/packages/core/database/src/entity-manager/morph-relations.ts b/packages/core/database/src/entity-manager/morph-relations.ts index 01caec98d6..22f4587d77 100644 --- a/packages/core/database/src/entity-manager/morph-relations.ts +++ b/packages/core/database/src/entity-manager/morph-relations.ts @@ -3,7 +3,7 @@ import type { Knex } from 'knex'; import { createQueryBuilder } from '../query'; import type { Database } from '..'; -import type { MorphJoinTable, Relation } from '../metadata/types'; +import type { MorphJoinTable, Relation } from '../types'; type Rows = Record[]; diff --git a/packages/core/database/src/entity-manager/regular-relations.ts b/packages/core/database/src/entity-manager/regular-relations.ts index e2aa18c8d7..3cad133c7d 100644 --- a/packages/core/database/src/entity-manager/regular-relations.ts +++ b/packages/core/database/src/entity-manager/regular-relations.ts @@ -10,12 +10,11 @@ import { isAnyToOne, hasOrderColumn, hasInverseOrderColumn, -} from '../metadata/relations'; +} from '../metadata'; import { createQueryBuilder } from '../query'; import { addSchema } from '../utils/knex'; import type { Database } from '..'; -import type { ID } from '../typings'; -import type { Relation } from '../metadata/types'; +import type { ID, Relation } from '../types'; declare module 'knex' { namespace Knex { diff --git a/packages/core/database/src/entity-manager/relations-orderer.ts b/packages/core/database/src/entity-manager/relations-orderer.ts index 577daa7996..c2e4330c80 100644 --- a/packages/core/database/src/entity-manager/relations-orderer.ts +++ b/packages/core/database/src/entity-manager/relations-orderer.ts @@ -2,7 +2,7 @@ import { castArray, maxBy } from 'lodash/fp'; import _ from 'lodash'; import { InvalidRelationError } from '../errors'; -import type { ID } from '../typings'; +import type { ID } from '../types'; interface Link { id: ID; diff --git a/packages/core/database/src/entity-manager/relations/cloning/regular-relations.ts b/packages/core/database/src/entity-manager/relations/cloning/regular-relations.ts index b8d1779140..fad75fa346 100644 --- a/packages/core/database/src/entity-manager/relations/cloning/regular-relations.ts +++ b/packages/core/database/src/entity-manager/relations/cloning/regular-relations.ts @@ -1,8 +1,7 @@ import { Knex } from 'knex'; import { cleanInverseOrderColumn } from '../../regular-relations'; -import type { Relation } from '../../../metadata/types'; -import { ID } from '../../../typings'; +import type { ID, Relation } from '../../../types'; const replaceRegularRelations = async ({ targetId, diff --git a/packages/core/database/src/fields/index.ts b/packages/core/database/src/fields/index.ts index 9b4a3a98b7..3c01f619ba 100644 --- a/packages/core/database/src/fields/index.ts +++ b/packages/core/database/src/fields/index.ts @@ -11,7 +11,7 @@ import DatetimeField from './datetime'; import TimestampField from './timestamp'; import BooleanField from './boolean'; -import type { Attribute } from '../metadata/types'; +import type { Attribute } from '../types'; const typeToFieldMap: Record = { increments: Field, diff --git a/packages/core/database/src/lifecycles/types.ts b/packages/core/database/src/lifecycles/types.ts index f6c1bc95cf..cf97c8baa2 100644 --- a/packages/core/database/src/lifecycles/types.ts +++ b/packages/core/database/src/lifecycles/types.ts @@ -1,4 +1,4 @@ -import type { Meta, Model } from '../metadata/types'; +import type { Meta } from '../metadata'; export type Action = | 'beforeCreate' diff --git a/packages/core/database/src/metadata/index.ts b/packages/core/database/src/metadata/index.ts index 83e2bc34da..15f7471b94 100644 --- a/packages/core/database/src/metadata/index.ts +++ b/packages/core/database/src/metadata/index.ts @@ -1,11 +1,31 @@ import _ from 'lodash/fp'; -import * as types from '../types'; -import { createRelation } from './relations'; -import { Metadata, Relation } from './types'; -import type { Attribute, Model, Meta, ComponentLinkMeta } from './types'; +import * as types from '../utils/types'; +import { + createRelation, + getJoinTableName, + isPolymorphic, + isBidirectional, + isAnyToOne, + isOneToAny, + hasOrderColumn, + hasInverseOrderColumn, + isManyToAny, +} from './relations'; +import { Metadata, Meta, ComponentLinkMeta } from './metadata'; +import type { Attribute, Model, Relation } from '../types'; -export { Metadata }; +export type { Metadata, Meta }; +export { + getJoinTableName, + isPolymorphic, + isBidirectional, + isAnyToOne, + isOneToAny, + hasOrderColumn, + hasInverseOrderColumn, + isManyToAny, +}; // TODO: check if there isn't an attribute with an id already /** @@ -21,17 +41,16 @@ export const createMetadata = (models: Model[] = []): Metadata => { } metadata.add({ - singularName: model.singularName, - uid: model.uid, - tableName: model.tableName, + ...model, attributes: { id: { type: 'increments', }, ...model.attributes, }, - lifecycles: model.lifecycles ?? ({} as Meta['lifecycles']), + lifecycles: model.lifecycles ?? {}, indexes: model.indexes || [], + foreignKeys: model.foreignKeys || [], columnToAttribute: {}, }); } @@ -98,11 +117,13 @@ const hasComponentsOrDz = (model: Meta): model is ComponentLinkMeta => { // NOTE: we might just move the compo logic outside this layer too at some point const createCompoLinkModelMeta = (baseModelMeta: Meta): Meta => { + const name = `${baseModelMeta.tableName}_components`; + return { // TODO: make sure there can't be any conflicts with a prefix - // singularName: 'compo', - uid: `${baseModelMeta.tableName}_components`, - tableName: `${baseModelMeta.tableName}_components`, + singularName: name, + uid: name, + tableName: name, attributes: { id: { type: 'increments', @@ -161,6 +182,7 @@ const createCompoLinkModelMeta = (baseModelMeta: Meta): Meta => { onDelete: 'CASCADE', }, ], + lifecycles: {}, columnToAttribute: {}, }; }; diff --git a/packages/core/database/src/metadata/metadata.ts b/packages/core/database/src/metadata/metadata.ts new file mode 100644 index 0000000000..074477a24c --- /dev/null +++ b/packages/core/database/src/metadata/metadata.ts @@ -0,0 +1,44 @@ +import type { Model } from '../types'; +import type { ForeignKey, Index } from '../schema/types'; +import type { Action, SubscriberFn } from '../lifecycles'; + +export interface ComponentLinkMeta extends Meta { + componentLink: Meta; +} + +export interface Meta extends Model { + columnToAttribute: Record; + componentLink?: Meta; + indexes: Index[]; + foreignKeys: ForeignKey[]; + lifecycles: Partial>; +} + +export class Metadata extends Map { + get(key: string): Meta { + if (!super.has(key)) { + throw new Error(`Metadata for "${key}" not found`); + } + + return super.get(key) as Meta; + } + + add(meta: Meta) { + return this.set(meta.uid, meta); + } + + /** + * Validate the DB metadata, throwing an error if a duplicate DB table name is detected + */ + validate() { + const seenTables = new Map(); + for (const meta of this.values()) { + if (seenTables.get(meta.tableName)) { + throw new Error( + `DB table "${meta.tableName}" already exists. Change the collectionName of the related content type.` + ); + } + seenTables.set(meta.tableName, true); + } + } +} diff --git a/packages/core/database/src/metadata/relations.ts b/packages/core/database/src/metadata/relations.ts index 8260037ef7..40805b7dbc 100644 --- a/packages/core/database/src/metadata/relations.ts +++ b/packages/core/database/src/metadata/relations.ts @@ -1,6 +1,7 @@ import _ from 'lodash/fp'; -import type { Meta, RelationalAttribute, Relation, Metadata, MorphJoinTable } from './types'; +import type { Meta, Metadata } from './metadata'; +import type { RelationalAttribute, Relation, MorphJoinTable } from '../types'; interface JoinColumnOptions { attribute: (Relation.OneToOne | Relation.ManyToOne) & Relation.Owner; @@ -245,6 +246,7 @@ const createMorphToMany = ( const typeColumnName = `${morphColumnName}_type`; metadata.add({ + singularName: joinTableName, uid: joinTableName, tableName: joinTableName, attributes: { @@ -299,6 +301,7 @@ const createMorphToMany = ( onDelete: 'CASCADE', }, ], + lifecycles: {}, columnToAttribute: {}, }); @@ -429,6 +432,7 @@ const createJoinTable = ( } const metadataSchema: Meta = { + singularName: joinTableName, uid: joinTableName, tableName: joinTableName, attributes: { @@ -480,6 +484,7 @@ const createJoinTable = ( onDelete: 'CASCADE', }, ], + lifecycles: {}, columnToAttribute: {}, }; diff --git a/packages/core/database/src/metadata/types.ts b/packages/core/database/src/metadata/types.ts deleted file mode 100644 index f0fe38f2e4..0000000000 --- a/packages/core/database/src/metadata/types.ts +++ /dev/null @@ -1,269 +0,0 @@ -import type { Action, SubscriberFn } from '../lifecycles'; -import type { ForeignKey, Index } from '../schema/types'; - -export interface ColumnInfo { - unsigned?: boolean; - defaultTo?: unknown; -} - -export type Attribute = ScalarAttribute | RelationalAttribute; - -export type RelationalAttribute = - | Relation.OneToOne - | Relation.OneToMany - | Relation.ManyToOne - | Relation.ManyToMany - | Relation.MorphMany - | Relation.MorphOne - | Relation.MorphToOne - | Relation.MorphToMany; - -export interface BasAttribute { - type: string; - columnName?: string; - default?: any; - column?: ColumnInfo; - required?: boolean; - unique?: boolean; - component?: string; - repeatable?: boolean; - columnType?: { - type: string; - args: unknown[]; - }; - searchable?: boolean; -} - -export interface ScalarAttribute extends BasAttribute { - type: - | 'increments' - | 'password' - | 'email' - | 'string' - | 'enumeration' - | 'uid' - | 'richtext' - | 'text' - | 'json' - | 'integer' - | 'biginteger' - | 'float' - | 'decimal' - | 'boolean' - | 'date' - | 'time' - | 'datetime' - | 'timestamp'; -} - -export interface JoinColumn { - name: string; - referencedColumn: string; - referencedTable?: string; -} - -export interface BaseJoinTable { - name: string; - joinColumn: JoinColumn; - orderBy?: Record; - on?: Record; - pivotColumns: string[]; - inverseJoinColumn: { - name: string; - referencedColumn: string; - }; -} - -export interface JoinTable extends BaseJoinTable { - orderColumnName?: string; - inverseOrderColumnName?: string; -} - -export interface OrderedJoinTable extends BaseJoinTable { - orderColumnName: string; - inverseOrderColumnName: string; -} - -// eslint-disable-next-line @typescript-eslint/no-namespace -export namespace Relation { - export type Owner = { - inversedBy: string; - }; - - export type WithTarget = { - target: string; - }; - - export type Bidirectional = OneToOne | OneToMany | ManyToOne | ManyToMany; - - type BaseBidirectional = { - type: 'relation'; - relation: 'oneToOne' | 'oneToMany' | 'manyToOne' | 'manyToMany'; - target: string; - inversedBy?: string; - mappedBy?: string; - joinTable: BidirectionalAttributeJoinTable; - }; - - export type OneToOne = BaseBidirectional & { - relation: 'oneToOne'; - useJoinTable?: boolean; - joinTable?: JoinTable; - joinColumn?: JoinColumn; - owner?: boolean; - }; - - export type OneToMany = BaseBidirectional & { - relation: 'oneToMany'; - joinTable: OrderedJoinTable; - joinColumn?: JoinColumn; - owner?: boolean; - }; - - export type ManyToOne = BaseBidirectional & { - relation: 'manyToOne'; - useJoinTable?: boolean; - joinTable?: JoinTable; - joinColumn?: JoinColumn; - owner?: boolean; - }; - - export type ManyToMany = BaseBidirectional & { - relation: 'manyToMany'; - joinTable: OrderedJoinTable; - }; - - export type Morph = MorphMany | MorphOne | MorphToOne | MorphToMany; - - export type MorphMany = { - type: 'relation'; - relation: 'morphMany'; - target: string; - morphBy: string; - joinTable: MorphJoinTable; - }; - - export type MorphOne = { - type: 'relation'; - relation: 'morphOne'; - target: string; - morphBy: string; - }; - - export type MorphToOne = { - type: 'relation'; - relation: 'morphToOne'; - owner?: boolean; - morphColumn: MorphColumn; - }; - - export type MorphToMany = { - type: 'relation'; - relation: 'morphToMany'; - joinTable: MorphJoinTable; - }; -} - -export interface BidirectionalAttributeJoinTable extends JoinTable { - orderColumnName: string; - inverseOrderColumnName: string; -} - -export interface MorphColumn { - typeField?: string; - typeColumn: { - name: string; - }; - idColumn: { - name: string; - referencedColumn: string; - }; -} - -export interface MorphJoinTable { - name: string; - joinColumn: JoinColumn; - orderBy?: Record; - on?: Record; - pivotColumns: string[]; - morphColumn: MorphColumn; -} - -export interface BaseRelationalAttribute { - type: 'relation'; - target: string; - useJoinTable?: boolean; - joinTable?: JoinTable | MorphJoinTable; - morphBy?: string; - inversedBy?: string; - owner?: boolean; - morphColumn?: MorphColumn; - joinColumn?: JoinColumn; - // TODO: remove this - component?: string; -} - -export interface MorphRelationalAttribute extends BaseRelationalAttribute { - relation: 'morphMany' | 'morphOne' | 'morphToOne' | 'morphToMany'; - morphColumn: MorphColumn; - morphBy: string; - joinTable: MorphJoinTable; - target: string; -} - -export interface Meta { - singularName?: string; - uid: string; - tableName: string; - attributes: Record; - indexes: Index[]; - foreignKeys?: ForeignKey[]; - lifecycles?: Record; - columnToAttribute: Record; - componentLink?: Meta; -} - -export interface ComponentLinkMeta extends Meta { - componentLink: Meta; -} - -export interface Model { - uid: string; - tableName: string; - singularName: string; - attributes: Record; - lifecycles?: Record; - indexes: Index[]; - componentLink?: Meta; - columnToAttribute?: Record; - foreignKeys?: Record[]; -} - -export class Metadata extends Map { - get(key: string): Meta { - if (!super.has(key)) { - throw new Error(`Metadata for "${key}" not found`); - } - - return super.get(key) as Meta; - } - - add(meta: Meta) { - return this.set(meta.uid, meta); - } - - /** - * Validate the DB metadata, throwing an error if a duplicate DB table name is detected - */ - validate() { - const seenTables = new Map(); - for (const meta of this.values()) { - if (seenTables.get(meta.tableName)) { - throw new Error( - `DB table "${meta.tableName}" already exists. Change the collectionName of the related content type.` - ); - } - seenTables.set(meta.tableName, true); - } - } -} diff --git a/packages/core/database/src/query/helpers/order-by.ts b/packages/core/database/src/query/helpers/order-by.ts index b670d76522..96812092f4 100644 --- a/packages/core/database/src/query/helpers/order-by.ts +++ b/packages/core/database/src/query/helpers/order-by.ts @@ -1,6 +1,6 @@ import _ from 'lodash/fp'; -import * as types from '../../types'; +import * as types from '../../utils/types'; import { createJoin } from './join'; import { toColumnName } from './transform'; diff --git a/packages/core/database/src/query/helpers/populate/apply.ts b/packages/core/database/src/query/helpers/populate/apply.ts index 3f58637ace..92c8945124 100644 --- a/packages/core/database/src/query/helpers/populate/apply.ts +++ b/packages/core/database/src/query/helpers/populate/apply.ts @@ -1,10 +1,10 @@ import _ from 'lodash/fp'; -import { Rec, fromRow } from '../transform'; +import { fromRow } from '../transform'; import type { QueryBuilder } from '../../query-builder'; import type { Database } from '../../..'; -import type { Meta, RelationalAttribute, Relation } from '../../../metadata/types'; -import { ID } from '../../../typings'; +import type { Meta } from '../../../metadata'; +import { ID, RelationalAttribute, Relation } from '../../../types'; type Context = { db: Database; diff --git a/packages/core/database/src/query/helpers/populate/process.ts b/packages/core/database/src/query/helpers/populate/process.ts index 4c3a79c9d8..8f5274befc 100644 --- a/packages/core/database/src/query/helpers/populate/process.ts +++ b/packages/core/database/src/query/helpers/populate/process.ts @@ -1,7 +1,7 @@ import _ from 'lodash/fp'; -import * as types from '../../../types'; -import type { Meta } from '../../../metadata/types'; +import * as types from '../../../utils/types'; +import type { Meta } from '../../../metadata'; import type { QueryBuilder } from '../../query-builder'; import type { Database } from '../../..'; diff --git a/packages/core/database/src/query/helpers/search.ts b/packages/core/database/src/query/helpers/search.ts index 66f07a41f8..b3175292d3 100644 --- a/packages/core/database/src/query/helpers/search.ts +++ b/packages/core/database/src/query/helpers/search.ts @@ -1,7 +1,7 @@ import _ from 'lodash/fp'; import type { Knex } from 'knex'; -import * as types from '../../types'; +import * as types from '../../utils/types'; import { toColumnName } from './transform'; import type { Ctx } from '../types'; diff --git a/packages/core/database/src/query/helpers/streams/readable.ts b/packages/core/database/src/query/helpers/streams/readable.ts index 187d45e614..e392173534 100644 --- a/packages/core/database/src/query/helpers/streams/readable.ts +++ b/packages/core/database/src/query/helpers/streams/readable.ts @@ -6,7 +6,7 @@ import type { Database } from '../../..'; import { applyPopulate } from '../populate'; import { fromRow } from '../transform'; -import { Meta } from '../../../metadata/types'; +import { Meta } from '../../../metadata'; const knexQueryDone = Symbol('knexQueryDone'); const knexPerformingQuery = Symbol('knexPerformingQuery'); diff --git a/packages/core/database/src/query/helpers/transform.ts b/packages/core/database/src/query/helpers/transform.ts index 9bac812108..4244d17852 100644 --- a/packages/core/database/src/query/helpers/transform.ts +++ b/packages/core/database/src/query/helpers/transform.ts @@ -1,9 +1,9 @@ import _ from 'lodash/fp'; -import * as types from '../../types'; +import * as types from '../../utils/types'; import { createField } from '../../fields'; -import type { Meta } from '../../metadata/types'; +import type { Meta } from '../../metadata'; type Row = Record | null; export type Rec = Record | null; diff --git a/packages/core/database/src/query/helpers/where.ts b/packages/core/database/src/query/helpers/where.ts index 766cdf45c4..c88e9dfb0b 100644 --- a/packages/core/database/src/query/helpers/where.ts +++ b/packages/core/database/src/query/helpers/where.ts @@ -3,14 +3,14 @@ import { isArray, castArray, keys, isPlainObject } from 'lodash/fp'; import type { Knex } from 'knex'; import { isOperatorOfType } from '@strapi/utils'; -import * as types from '../../types'; +import * as types from '../../utils/types'; import { createField } from '../../fields'; import { createJoin } from './join'; import { toColumnName } from './transform'; import { isKnexQuery } from '../../utils/knex'; -import type { Attribute } from '../../metadata/types'; import type { Ctx } from '../types'; +import type { Attribute } from '../../types'; const isObj = (value: unknown): value is Record => isPlainObject(value); diff --git a/packages/core/database/src/schema/schema.ts b/packages/core/database/src/schema/schema.ts index 59c0b26f19..898aabb0b7 100644 --- a/packages/core/database/src/schema/schema.ts +++ b/packages/core/database/src/schema/schema.ts @@ -1,7 +1,8 @@ -import * as types from '../types'; +import * as types from '../utils/types'; -import type { Metadata, Meta, Attribute } from '../metadata/types'; +import type { Metadata, Meta } from '../metadata'; import type { Column, Schema, Table } from './types'; +import type { Attribute } from '../types'; const createColumn = (name: string, attribute: Attribute): Column => { const { type, args = [], ...opts } = getColumnType(attribute); diff --git a/packages/core/database/src/global.d.ts b/packages/core/database/src/types/global.d.ts similarity index 100% rename from packages/core/database/src/global.d.ts rename to packages/core/database/src/types/global.d.ts diff --git a/packages/core/database/src/types/index.ts b/packages/core/database/src/types/index.ts index 1f8ebe1b08..3c0e0eb75c 100644 --- a/packages/core/database/src/types/index.ts +++ b/packages/core/database/src/types/index.ts @@ -1,36 +1,223 @@ -import type { Attribute, ScalarAttribute, RelationalAttribute } from '../metadata/types'; +import type { Action, SubscriberFn } from '../lifecycles'; +import type { ForeignKey, Index } from '../schema/types'; -const SCALAR_TYPES = [ - 'increments', - 'password', - 'email', - 'string', - 'uid', - 'richtext', - 'text', - 'json', - 'enumeration', - 'integer', - 'biginteger', - 'float', - 'decimal', - 'date', - 'time', - 'datetime', - 'timestamp', - 'boolean', -]; +export type ID = string | number; +export interface ColumnInfo { + unsigned?: boolean; + defaultTo?: unknown; +} -const STRING_TYPES = ['string', 'text', 'uid', 'email', 'enumeration', 'richtext']; -const NUMBER_TYPES = ['biginteger', 'integer', 'decimal', 'float']; +export type Attribute = ScalarAttribute | RelationalAttribute; -export const isString = (type: string) => STRING_TYPES.includes(type); -export const isNumber = (type: string) => NUMBER_TYPES.includes(type); -export const isScalar = (type: string) => SCALAR_TYPES.includes(type); -export const isComponent = (type: string) => type === 'component'; -export const isDynamicZone = (type: string) => type === 'dynamiczone'; -export const isRelation = (type: string) => type === 'relation'; -export const isScalarAttribute = (attribute: Attribute): attribute is ScalarAttribute => - isScalar(attribute.type); -export const isRelationalAttribute = (attribute: Attribute): attribute is RelationalAttribute => - isRelation(attribute.type); +export type RelationalAttribute = + | Relation.OneToOne + | Relation.OneToMany + | Relation.ManyToOne + | Relation.ManyToMany + | Relation.MorphMany + | Relation.MorphOne + | Relation.MorphToOne + | Relation.MorphToMany; + +export interface BasAttribute { + type: string; + columnName?: string; + default?: any; + column?: ColumnInfo; + required?: boolean; + unique?: boolean; + component?: string; + repeatable?: boolean; + columnType?: { + type: string; + args: unknown[]; + }; + searchable?: boolean; +} + +export interface ScalarAttribute extends BasAttribute { + type: + | 'increments' + | 'password' + | 'email' + | 'string' + | 'enumeration' + | 'uid' + | 'richtext' + | 'text' + | 'json' + | 'integer' + | 'biginteger' + | 'float' + | 'decimal' + | 'boolean' + | 'date' + | 'time' + | 'datetime' + | 'timestamp'; +} + +export interface JoinColumn { + name: string; + referencedColumn: string; + referencedTable?: string; +} + +export interface BaseJoinTable { + name: string; + joinColumn: JoinColumn; + orderBy?: Record; + on?: Record; + pivotColumns: string[]; + inverseJoinColumn: { + name: string; + referencedColumn: string; + }; +} + +export interface JoinTable extends BaseJoinTable { + orderColumnName?: string; + inverseOrderColumnName?: string; +} + +export interface OrderedJoinTable extends BaseJoinTable { + orderColumnName: string; + inverseOrderColumnName: string; +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Relation { + export type Owner = { + inversedBy: string; + }; + + export type WithTarget = { + target: string; + }; + + export type Bidirectional = OneToOne | OneToMany | ManyToOne | ManyToMany; + + type BaseBidirectional = { + type: 'relation'; + relation: 'oneToOne' | 'oneToMany' | 'manyToOne' | 'manyToMany'; + target: string; + inversedBy?: string; + mappedBy?: string; + joinTable: BidirectionalAttributeJoinTable; + }; + + export type OneToOne = BaseBidirectional & { + relation: 'oneToOne'; + useJoinTable?: boolean; + joinTable?: JoinTable; + joinColumn?: JoinColumn; + owner?: boolean; + }; + + export type OneToMany = BaseBidirectional & { + relation: 'oneToMany'; + joinTable: OrderedJoinTable; + joinColumn?: JoinColumn; + owner?: boolean; + }; + + export type ManyToOne = BaseBidirectional & { + relation: 'manyToOne'; + useJoinTable?: boolean; + joinTable?: JoinTable; + joinColumn?: JoinColumn; + owner?: boolean; + }; + + export type ManyToMany = BaseBidirectional & { + relation: 'manyToMany'; + joinTable: OrderedJoinTable; + }; + + export type Morph = MorphMany | MorphOne | MorphToOne | MorphToMany; + + export type MorphMany = { + type: 'relation'; + relation: 'morphMany'; + target: string; + morphBy: string; + joinTable: MorphJoinTable; + }; + + export type MorphOne = { + type: 'relation'; + relation: 'morphOne'; + target: string; + morphBy: string; + }; + + export type MorphToOne = { + type: 'relation'; + relation: 'morphToOne'; + owner?: boolean; + morphColumn: MorphColumn; + }; + + export type MorphToMany = { + type: 'relation'; + relation: 'morphToMany'; + joinTable: MorphJoinTable; + }; +} + +export interface BidirectionalAttributeJoinTable extends JoinTable { + orderColumnName: string; + inverseOrderColumnName: string; +} + +export interface MorphColumn { + typeField?: string; + typeColumn: { + name: string; + }; + idColumn: { + name: string; + referencedColumn: string; + }; +} + +export interface MorphJoinTable { + name: string; + joinColumn: JoinColumn; + orderBy?: Record; + on?: Record; + pivotColumns: string[]; + morphColumn: MorphColumn; +} + +export interface BaseRelationalAttribute { + type: 'relation'; + target: string; + useJoinTable?: boolean; + joinTable?: JoinTable | MorphJoinTable; + morphBy?: string; + inversedBy?: string; + owner?: boolean; + morphColumn?: MorphColumn; + joinColumn?: JoinColumn; + // TODO: remove this + component?: string; +} + +export interface MorphRelationalAttribute extends BaseRelationalAttribute { + relation: 'morphMany' | 'morphOne' | 'morphToOne' | 'morphToMany'; + morphColumn: MorphColumn; + morphBy: string; + joinTable: MorphJoinTable; + target: string; +} + +export interface Model { + uid: string; + tableName: string; + singularName: string; + attributes: Record; + indexes?: Index[]; + foreignKeys?: ForeignKey[]; + lifecycles?: Partial>; +} diff --git a/packages/core/database/src/typings.ts b/packages/core/database/src/typings.ts deleted file mode 100644 index 5d184034ac..0000000000 --- a/packages/core/database/src/typings.ts +++ /dev/null @@ -1 +0,0 @@ -export type ID = string | number; diff --git a/packages/core/database/src/utils/types.ts b/packages/core/database/src/utils/types.ts new file mode 100644 index 0000000000..162061897b --- /dev/null +++ b/packages/core/database/src/utils/types.ts @@ -0,0 +1,36 @@ +import type { Attribute, ScalarAttribute, RelationalAttribute } from '../types'; + +const SCALAR_TYPES = [ + 'increments', + 'password', + 'email', + 'string', + 'uid', + 'richtext', + 'text', + 'json', + 'enumeration', + 'integer', + 'biginteger', + 'float', + 'decimal', + 'date', + 'time', + 'datetime', + 'timestamp', + 'boolean', +]; + +const STRING_TYPES = ['string', 'text', 'uid', 'email', 'enumeration', 'richtext']; +const NUMBER_TYPES = ['biginteger', 'integer', 'decimal', 'float']; + +export const isString = (type: string) => STRING_TYPES.includes(type); +export const isNumber = (type: string) => NUMBER_TYPES.includes(type); +export const isScalar = (type: string) => SCALAR_TYPES.includes(type); +export const isComponent = (type: string) => type === 'component'; +export const isDynamicZone = (type: string) => type === 'dynamiczone'; +export const isRelation = (type: string) => type === 'relation'; +export const isScalarAttribute = (attribute: Attribute): attribute is ScalarAttribute => + isScalar(attribute.type); +export const isRelationalAttribute = (attribute: Attribute): attribute is RelationalAttribute => + isRelation(attribute.type); diff --git a/packages/core/database/src/validations/relations/bidirectional.ts b/packages/core/database/src/validations/relations/bidirectional.ts index 9b37fd6bb8..714efff3a2 100644 --- a/packages/core/database/src/validations/relations/bidirectional.ts +++ b/packages/core/database/src/validations/relations/bidirectional.ts @@ -1,7 +1,7 @@ -import { getJoinTableName } from '../../metadata/relations'; +import { getJoinTableName } from '../../metadata'; import type { Database } from '../..'; -import type { Relation } from '../../metadata/types'; +import type { Relation } from '../../types'; type Link = { relation: Relation.Bidirectional & { inversedBy: string };