mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-11 22:50:06 +00:00
chore: Bring typeORM fork to monorepo (#32898)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"generated": "2026-06-04T06:55:45.363Z",
|
||||
"totalViolations": 148,
|
||||
"totalViolations": 159,
|
||||
"violations": {
|
||||
"packages/@n8n/ai-workflow-builder.ee/package.json": [
|
||||
{
|
||||
@@ -1058,6 +1058,74 @@
|
||||
"message": "Override \"langsmith\" duplicates a catalog entry for langsmith — set the override to \"catalog:\" or remove it",
|
||||
"hash": "170e854b445c"
|
||||
}
|
||||
],
|
||||
"packages/@n8n/typeorm/package.json": [
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 118,
|
||||
"message": "reflect-metadata@^0.2.2 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "558f12c2dd9c"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 122,
|
||||
"message": "uuid@^9.0.0 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "0304587b7acc"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 67,
|
||||
"message": "@types/node@^18.13.0 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "0df18a02f2d0"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 72,
|
||||
"message": "@types/uuid@^9.0.0 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "0bcbecb362c9"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 76,
|
||||
"message": "eslint@^8.44.0 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "0036144a407d"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 78,
|
||||
"message": "pg@^8.17.0 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "6cac348bdcc8"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 80,
|
||||
"message": "prettier@^2.8.3 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "869ee9ce64bc"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 81,
|
||||
"message": "rimraf@^4.1.2 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "48f117898c57"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 84,
|
||||
"message": "source-map-support@^0.5.21 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "c27bcf7b8b06"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 87,
|
||||
"message": "typescript@^5.3.3 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "050d5b2d74e1"
|
||||
},
|
||||
{
|
||||
"rule": "catalog-violations",
|
||||
"line": 90,
|
||||
"message": "pg@^8.17.0 should use \"catalog:\" (exists in pnpm-workspace.yaml)",
|
||||
"hash": "6cac348bdcc8"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@n8n/db": "workspace:*",
|
||||
"@n8n/di": "workspace:*",
|
||||
"@n8n/permissions": "workspace:*",
|
||||
"@n8n/typeorm": "catalog:",
|
||||
"@n8n/typeorm": "workspace:*",
|
||||
"n8n-workflow": "workspace:*",
|
||||
"reflect-metadata": "catalog:",
|
||||
"uuid": "catalog:",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"@n8n/di": "workspace:*",
|
||||
"@n8n/permissions": "workspace:*",
|
||||
"@n8n/utils": "workspace:*",
|
||||
"@n8n/typeorm": "catalog:",
|
||||
"@n8n/typeorm": "workspace:*",
|
||||
"class-validator": "0.14.0",
|
||||
"flatted": "catalog:",
|
||||
"lodash": "catalog:",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
.nyc_output/
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.idea/
|
||||
build/
|
||||
dist/
|
||||
coverage/
|
||||
node_modules/
|
||||
/ormconfig.json
|
||||
ormlogs.log
|
||||
npm-debug.log
|
||||
/test/github-issues/799/tmp/*
|
||||
# test projects generated by test/github-issues/issue-8975.ts
|
||||
/*TestProject
|
||||
# ignore yarn2 artifacts but allow yarn.lock (forces yarn1 compat which is node_modules)
|
||||
.yarn/
|
||||
.yarn*
|
||||
*.iml
|
||||
temp/**/*.db*
|
||||
temp/test
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2015-2024 TypeORM. http://typeorm.github.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"name": "@n8n/typeorm",
|
||||
"version": "0.3.20-18",
|
||||
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports PostgreSQL and SQLite databases.",
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
"author": {
|
||||
"name": "Umed Khudoiberdiev",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"require": "./dist/index.js",
|
||||
"import": "./dist/index.js"
|
||||
},
|
||||
"./*.js": "./dist/*.js",
|
||||
"./*": {
|
||||
"types": "./dist/*.d.ts",
|
||||
"require": "./dist/*.js",
|
||||
"import": "./dist/*.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"dist/*": [
|
||||
"./dist/*"
|
||||
],
|
||||
"*": [
|
||||
"./dist/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/typeorm/typeorm.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/typeorm/typeorm/issues"
|
||||
},
|
||||
"homepage": "https://typeorm.io",
|
||||
"tags": [
|
||||
"orm",
|
||||
"typescript",
|
||||
"typescript-orm",
|
||||
"postgresql",
|
||||
"postgresql-orm",
|
||||
"sqlite",
|
||||
"sqlite-orm"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^16.1.1",
|
||||
"@types/app-root-path": "^1.2.4",
|
||||
"@types/chai": "^4.3.4",
|
||||
"@types/chai-as-promised": "^7.1.5",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^18.13.0",
|
||||
"@types/pg": "^8.15.6",
|
||||
"@types/sha.js": "^2.4.0",
|
||||
"@types/sinon": "^10.0.13",
|
||||
"@types/source-map-support": "^0.5.6",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.35.0",
|
||||
"chai": "^4.3.7",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"eslint": "^8.44.0",
|
||||
"mocha": "^10.2.0",
|
||||
"pg": "^8.17.0",
|
||||
"pg-query-stream": "^4.10.3",
|
||||
"prettier": "^2.8.3",
|
||||
"rimraf": "^4.1.2",
|
||||
"sinon": "^15.0.1",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"sqlite3": "5.1.7",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg": "^8.17.0",
|
||||
"pg-native": "^3.5.2",
|
||||
"pg-query-stream": "^4.10.3",
|
||||
"sqlite3": "5.1.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg": {
|
||||
"optional": true
|
||||
},
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
},
|
||||
"pg-query-stream": {
|
||||
"optional": true
|
||||
},
|
||||
"sqlite3": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"app-root-path": "^3.1.0",
|
||||
"async-mutex": "^0.5.0",
|
||||
"chalk": "^4.1.2",
|
||||
"dayjs": "^1.11.9",
|
||||
"debug": "^4.3.4",
|
||||
"dotenv": "catalog:",
|
||||
"glob": "^10.0.0",
|
||||
"mkdirp": "^2.1.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"sha.js": "^2.4.12",
|
||||
"tarn": "3.0.2",
|
||||
"tslib": "^2.5.0",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tsc && mocha --file ./build/compiled/test/utils/test-setup.js --recursive --timeout 90000 ./build/compiled/test",
|
||||
"test:unit": "echo \"No unit tests for @n8n/typeorm (vendored)\" && exit 0",
|
||||
"test-fast": "mocha --file ./build/compiled/test/utils/test-setup.js --bail --recursive --timeout 90000 ./build/compiled/test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "pnpm compile",
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"compile": "tsc -p tsconfig.build.json",
|
||||
"watch": "tsc -w -p tsconfig.build.json"
|
||||
},
|
||||
"funding": "https://opencollective.com/typeorm",
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typeorm",
|
||||
"logo": "https://opencollective.com/opencollective/logo.txt"
|
||||
},
|
||||
"nyc": {
|
||||
"all": true,
|
||||
"cache": false,
|
||||
"exclude": [
|
||||
"**/*.d.ts"
|
||||
],
|
||||
"extension": [
|
||||
".ts"
|
||||
],
|
||||
"include": [
|
||||
"build/compiled/src/**",
|
||||
"src/**"
|
||||
],
|
||||
"reporter": "json"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,19 @@
|
||||
import { Column, Entity } from '../../../src/index';
|
||||
import { PrimaryColumn } from '../../../src/decorator/columns/PrimaryColumn';
|
||||
import { Generated } from '../../../src/decorator/Generated';
|
||||
|
||||
@Entity('sample01_post')
|
||||
export class Post {
|
||||
@PrimaryColumn()
|
||||
@Generated()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
likesCount: number;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { PostDetails } from './PostDetails';
|
||||
import { PostCategory } from './PostCategory';
|
||||
import { PostAuthor } from './PostAuthor';
|
||||
import { PostInformation } from './PostInformation';
|
||||
import { PostImage } from './PostImage';
|
||||
import { PostMetadata } from './PostMetadata';
|
||||
import { JoinColumn } from '../../../src/decorator/relations/JoinColumn';
|
||||
|
||||
@Entity('sample2_post')
|
||||
export class Post {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
// post has relation with category, however inverse relation is not set (category does not have relation with post set)
|
||||
@OneToOne((type) => PostCategory, {
|
||||
cascade: true,
|
||||
})
|
||||
@JoinColumn()
|
||||
category: PostCategory;
|
||||
|
||||
// post has relation with details. cascade inserts here means if new PostDetails instance will be set to this
|
||||
// relation it will be inserted automatically to the db when you save this Post entity
|
||||
@OneToOne(
|
||||
(type) => PostDetails,
|
||||
(details) => details.post,
|
||||
{
|
||||
cascade: ['insert'],
|
||||
},
|
||||
)
|
||||
@JoinColumn()
|
||||
details?: PostDetails;
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@OneToOne(
|
||||
(type) => PostImage,
|
||||
(image) => image.post,
|
||||
{
|
||||
cascade: ['update'],
|
||||
},
|
||||
)
|
||||
@JoinColumn()
|
||||
image: PostImage;
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@OneToOne(
|
||||
(type) => PostMetadata,
|
||||
(metadata) => metadata.post,
|
||||
)
|
||||
@JoinColumn()
|
||||
metadata: PostMetadata | null;
|
||||
|
||||
// post has relation with details. full cascades here
|
||||
@OneToOne(
|
||||
(type) => PostInformation,
|
||||
(information) => information.post,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
@JoinColumn()
|
||||
information: PostInformation;
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@OneToOne(
|
||||
(type) => PostAuthor,
|
||||
(author) => author.post,
|
||||
)
|
||||
@JoinColumn()
|
||||
author: PostAuthor;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample2_post_author')
|
||||
export class PostAuthor {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToOne(
|
||||
(type) => Post,
|
||||
(post) => post.author,
|
||||
)
|
||||
post: Post;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
|
||||
@Entity('sample2_post_category')
|
||||
export class PostCategory {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample2_post_details')
|
||||
export class PostDetails {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
authorName: string;
|
||||
|
||||
@Column()
|
||||
comment: string;
|
||||
|
||||
@Column()
|
||||
metadata: string;
|
||||
|
||||
@OneToOne(
|
||||
(type) => Post,
|
||||
(post) => post.details,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
post: Post;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample2_post_image')
|
||||
export class PostImage {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@OneToOne(
|
||||
(type) => Post,
|
||||
(post) => post.image,
|
||||
)
|
||||
post: Post;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample2_post_information')
|
||||
export class PostInformation {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@OneToOne(
|
||||
(type) => Post,
|
||||
(post) => post.information,
|
||||
{
|
||||
cascade: ['update'],
|
||||
},
|
||||
)
|
||||
post: Post;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample2_post_metadata')
|
||||
export class PostMetadata {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@OneToOne(
|
||||
(type) => Post,
|
||||
(post) => post.metadata,
|
||||
)
|
||||
post: Post;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { PostDetails } from './PostDetails';
|
||||
import { PostCategory } from './PostCategory';
|
||||
import { PostAuthor } from './PostAuthor';
|
||||
import { PostInformation } from './PostInformation';
|
||||
import { PostImage } from './PostImage';
|
||||
import { PostMetadata } from './PostMetadata';
|
||||
|
||||
@Entity('sample3_post')
|
||||
export class Post {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
// post has relation with category, however inverse relation is not set (category does not have relation with post set)
|
||||
@ManyToOne((type) => PostCategory, {
|
||||
cascade: true,
|
||||
})
|
||||
category: PostCategory;
|
||||
|
||||
// post has relation with details. cascade inserts here means if new PostDetails instance will be set to this
|
||||
// relation it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToOne(
|
||||
(type) => PostDetails,
|
||||
(details) => details.posts,
|
||||
{
|
||||
cascade: ['insert'],
|
||||
},
|
||||
)
|
||||
details?: PostDetails;
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToOne(
|
||||
(type) => PostImage,
|
||||
(image) => image.posts,
|
||||
{
|
||||
cascade: ['update'],
|
||||
},
|
||||
)
|
||||
image: PostImage;
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToOne(
|
||||
(type) => PostMetadata,
|
||||
(metadata) => metadata.posts,
|
||||
)
|
||||
metadata: PostMetadata | null;
|
||||
|
||||
// post has relation with details. full cascades here
|
||||
@ManyToOne(
|
||||
(type) => PostInformation,
|
||||
(information) => information.posts,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
information: PostInformation;
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@ManyToOne(
|
||||
(type) => PostAuthor,
|
||||
(author) => author.posts,
|
||||
)
|
||||
author: PostAuthor;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample3_post_author')
|
||||
export class PostAuthor {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToMany(
|
||||
(type) => Post,
|
||||
(post) => post.author,
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
|
||||
@Entity('sample3_post_category')
|
||||
export class PostCategory {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample3_post_details')
|
||||
export class PostDetails {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({
|
||||
type: String,
|
||||
nullable: true,
|
||||
})
|
||||
authorName: string | null;
|
||||
|
||||
@Column({
|
||||
type: String,
|
||||
nullable: true,
|
||||
})
|
||||
comment: string | null;
|
||||
|
||||
@Column({
|
||||
type: String,
|
||||
nullable: true,
|
||||
})
|
||||
metadata: string | null;
|
||||
|
||||
@OneToMany(
|
||||
(type) => Post,
|
||||
(post) => post.details,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample3_post_image')
|
||||
export class PostImage {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@OneToMany(
|
||||
(type) => Post,
|
||||
(post) => post.image,
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample3_post_information')
|
||||
export class PostInformation {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@OneToMany(
|
||||
(type) => Post,
|
||||
(post) => post.information,
|
||||
{
|
||||
cascade: ['update'],
|
||||
},
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample3_post_metadata')
|
||||
export class PostMetadata {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@OneToMany(
|
||||
(type) => Post,
|
||||
(post) => post.metadata,
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { PostDetails } from './PostDetails';
|
||||
import { PostCategory } from './PostCategory';
|
||||
import { PostAuthor } from './PostAuthor';
|
||||
import { PostInformation } from './PostInformation';
|
||||
import { PostImage } from './PostImage';
|
||||
import { PostMetadata } from './PostMetadata';
|
||||
import { JoinTable } from '../../../src/decorator/relations/JoinTable';
|
||||
|
||||
@Entity('sample4_post')
|
||||
export class Post {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
// Post has relation with PostCategory, however inverse relation is not set
|
||||
// (category does not have relation with post set)
|
||||
@ManyToMany((type) => PostCategory, {
|
||||
cascade: true,
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[];
|
||||
|
||||
// Post has relation with PostDetails. Cascade insert here means if there is a new PostDetails instance set
|
||||
// on this relation, it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany(
|
||||
(type) => PostDetails,
|
||||
(details) => details.posts,
|
||||
{
|
||||
cascade: ['insert'],
|
||||
},
|
||||
)
|
||||
@JoinTable()
|
||||
details: PostDetails[];
|
||||
|
||||
// Post has relation with PostImage. Cascade update here means if there are changes to an existing PostImage, it
|
||||
// will be updated automatically to the db when you save this Post entity
|
||||
@ManyToMany(
|
||||
(type) => PostImage,
|
||||
(image) => image.posts,
|
||||
{
|
||||
cascade: ['update'],
|
||||
},
|
||||
)
|
||||
@JoinTable()
|
||||
images: PostImage[];
|
||||
|
||||
// Post has relation with PostMetadata. No cascades here means that when saving a Post entity, there will be
|
||||
// no creating/updating/destroying PostMetadata.
|
||||
@ManyToMany(
|
||||
(type) => PostMetadata,
|
||||
(metadata) => metadata.posts,
|
||||
)
|
||||
@JoinTable()
|
||||
metadatas: PostMetadata[];
|
||||
|
||||
// Post has relation with PostInformation. Full cascades here
|
||||
@ManyToMany(
|
||||
(type) => PostInformation,
|
||||
(information) => information.posts,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
@JoinTable()
|
||||
informations: PostInformation[];
|
||||
|
||||
// Post has relation with author. No cascades here means that when saving a Post entity, there will be
|
||||
// no creating/updating/destroying PostAuthor.
|
||||
@ManyToMany(
|
||||
(type) => PostAuthor,
|
||||
(author) => author.posts,
|
||||
)
|
||||
@JoinTable()
|
||||
authors: PostAuthor[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample4_post_author')
|
||||
export class PostAuthor {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(
|
||||
(type) => Post,
|
||||
(post) => post.authors,
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
|
||||
@Entity('sample4_post_category')
|
||||
export class PostCategory {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample4_post_details')
|
||||
export class PostDetails {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({
|
||||
type: String,
|
||||
nullable: true,
|
||||
})
|
||||
authorName: string | null;
|
||||
|
||||
@Column({
|
||||
type: String,
|
||||
nullable: true,
|
||||
})
|
||||
comment: string | null;
|
||||
|
||||
@Column({
|
||||
type: String,
|
||||
nullable: true,
|
||||
})
|
||||
metadata: string | null;
|
||||
|
||||
@ManyToMany(
|
||||
(type) => Post,
|
||||
(post) => post.details,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample4_post_image')
|
||||
export class PostImage {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@ManyToMany(
|
||||
(type) => Post,
|
||||
(post) => post.images,
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample4_post_information')
|
||||
export class PostInformation {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@ManyToMany(
|
||||
(type) => Post,
|
||||
(post) => post.informations,
|
||||
{
|
||||
cascade: ['update'],
|
||||
},
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from '../../../src/index';
|
||||
import { Post } from './Post';
|
||||
|
||||
@Entity('sample4_post_metadata')
|
||||
export class PostMetadata {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@ManyToMany(
|
||||
(type) => Post,
|
||||
(post) => post.metadatas,
|
||||
)
|
||||
posts: Post[];
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
import { ObjectLiteral } from '../common/ObjectLiteral';
|
||||
import { DataSource } from '../data-source/DataSource';
|
||||
import { QueryRunner } from '../query-runner/QueryRunner';
|
||||
import { Table } from '../schema-builder/table/Table';
|
||||
import { QueryResultCache } from './QueryResultCache';
|
||||
import { QueryResultCacheOptions } from './QueryResultCacheOptions';
|
||||
|
||||
/**
|
||||
* Caches query result into current database, into separate table called "query-result-cache".
|
||||
*/
|
||||
export class DbQueryResultCache implements QueryResultCache {
|
||||
// -------------------------------------------------------------------------
|
||||
// Private properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private queryResultCacheTable: string;
|
||||
|
||||
private queryResultCacheDatabase?: string;
|
||||
|
||||
private queryResultCacheSchema?: string;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected connection: DataSource) {
|
||||
const { schema } = this.connection.driver.options as any;
|
||||
const database = this.connection.driver.database;
|
||||
const cacheOptions =
|
||||
typeof this.connection.options.cache === 'object' ? this.connection.options.cache : {};
|
||||
const cacheTableName = cacheOptions.tableName || 'query-result-cache';
|
||||
|
||||
this.queryResultCacheDatabase = database;
|
||||
this.queryResultCacheSchema = schema;
|
||||
this.queryResultCacheTable = this.connection.driver.buildTableName(
|
||||
cacheTableName,
|
||||
schema,
|
||||
database,
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a connection with given cache provider.
|
||||
*/
|
||||
async connect(): Promise<void> {}
|
||||
|
||||
/**
|
||||
* Disconnects with given cache provider.
|
||||
*/
|
||||
async disconnect(): Promise<void> {}
|
||||
|
||||
/**
|
||||
* Creates table for storing cache if it does not exist yet.
|
||||
*/
|
||||
async synchronize(queryRunner?: QueryRunner): Promise<void> {
|
||||
queryRunner = this.getQueryRunner(queryRunner);
|
||||
const driver = this.connection.driver;
|
||||
const tableExist = await queryRunner.hasTable(this.queryResultCacheTable); // todo: table name should be configurable
|
||||
if (tableExist) return;
|
||||
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
database: this.queryResultCacheDatabase,
|
||||
schema: this.queryResultCacheSchema,
|
||||
name: this.queryResultCacheTable,
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
isPrimary: true,
|
||||
isNullable: false,
|
||||
type: driver.normalizeType({
|
||||
type: driver.mappedDataTypes.cacheId,
|
||||
}),
|
||||
generationStrategy: 'increment',
|
||||
isGenerated: true,
|
||||
},
|
||||
{
|
||||
name: 'identifier',
|
||||
type: driver.normalizeType({
|
||||
type: driver.mappedDataTypes.cacheIdentifier,
|
||||
}),
|
||||
isNullable: true,
|
||||
},
|
||||
{
|
||||
name: 'time',
|
||||
type: driver.normalizeType({
|
||||
type: driver.mappedDataTypes.cacheTime,
|
||||
}),
|
||||
isPrimary: false,
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
type: driver.normalizeType({
|
||||
type: driver.mappedDataTypes.cacheDuration,
|
||||
}),
|
||||
isPrimary: false,
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'query',
|
||||
type: driver.normalizeType({
|
||||
type: driver.mappedDataTypes.cacheQuery,
|
||||
}),
|
||||
isPrimary: false,
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'result',
|
||||
type: driver.normalizeType({
|
||||
type: driver.mappedDataTypes.cacheResult,
|
||||
}),
|
||||
isNullable: false,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data from cache.
|
||||
* Returns cache result if found.
|
||||
* Returns undefined if result is not cached.
|
||||
*/
|
||||
getFromCache(
|
||||
options: QueryResultCacheOptions,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<QueryResultCacheOptions | undefined> {
|
||||
queryRunner = this.getQueryRunner(queryRunner);
|
||||
const qb = this.connection
|
||||
.createQueryBuilder(queryRunner)
|
||||
.select()
|
||||
.from(this.queryResultCacheTable, 'cache');
|
||||
|
||||
if (options.identifier) {
|
||||
return qb
|
||||
.where(`${qb.escape('cache')}.${qb.escape('identifier')} = :identifier`)
|
||||
.setParameters({
|
||||
identifier: options.identifier,
|
||||
})
|
||||
.cache(false) // disable cache to avoid infinite loops when cache is alwaysEnable
|
||||
.getRawOne();
|
||||
} else if (options.query) {
|
||||
return qb
|
||||
.where(`${qb.escape('cache')}.${qb.escape('query')} = :query`)
|
||||
.setParameters({
|
||||
query: options.query,
|
||||
})
|
||||
.cache(false) // disable cache to avoid infinite loops when cache is alwaysEnable
|
||||
.getRawOne();
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if cache is expired or not.
|
||||
*/
|
||||
isExpired(savedCache: QueryResultCacheOptions): boolean {
|
||||
const duration =
|
||||
typeof savedCache.duration === 'string' ? parseInt(savedCache.duration) : savedCache.duration;
|
||||
return (
|
||||
(typeof savedCache.time === 'string' ? parseInt(savedCache.time as any) : savedCache.time)! +
|
||||
duration <
|
||||
new Date().getTime()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores given query result in the cache.
|
||||
*/
|
||||
async storeInCache(
|
||||
options: QueryResultCacheOptions,
|
||||
savedCache: QueryResultCacheOptions | undefined,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<void> {
|
||||
const shouldCreateQueryRunner =
|
||||
queryRunner === undefined || queryRunner?.getReplicationMode() === 'slave';
|
||||
|
||||
if (queryRunner === undefined || shouldCreateQueryRunner) {
|
||||
queryRunner = this.connection.createQueryRunner('master');
|
||||
}
|
||||
|
||||
let insertedValues: ObjectLiteral = options;
|
||||
|
||||
if (savedCache && savedCache.identifier) {
|
||||
// if exist then update
|
||||
const qb = queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.update(this.queryResultCacheTable)
|
||||
.set(insertedValues);
|
||||
|
||||
qb.where(`${qb.escape('identifier')} = :condition`, {
|
||||
condition: insertedValues.identifier,
|
||||
});
|
||||
await qb.execute();
|
||||
} else if (savedCache && savedCache.query) {
|
||||
// if exist then update
|
||||
const qb = queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.update(this.queryResultCacheTable)
|
||||
.set(insertedValues);
|
||||
|
||||
qb.where(`${qb.escape('query')} = :condition`, {
|
||||
condition: insertedValues.query,
|
||||
});
|
||||
|
||||
await qb.execute();
|
||||
} else {
|
||||
// otherwise insert
|
||||
await queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(this.queryResultCacheTable)
|
||||
.values(insertedValues)
|
||||
.execute();
|
||||
}
|
||||
|
||||
if (shouldCreateQueryRunner) {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears everything stored in the cache.
|
||||
*/
|
||||
async clear(queryRunner: QueryRunner): Promise<void> {
|
||||
return this.getQueryRunner(queryRunner).clearTable(this.queryResultCacheTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all cached results by given identifiers from cache.
|
||||
*/
|
||||
async remove(identifiers: string[], queryRunner?: QueryRunner): Promise<void> {
|
||||
let _queryRunner: QueryRunner = queryRunner || this.getQueryRunner();
|
||||
await Promise.all(
|
||||
identifiers.map((identifier) => {
|
||||
const qb = _queryRunner.manager.createQueryBuilder();
|
||||
return qb
|
||||
.delete()
|
||||
.from(this.queryResultCacheTable)
|
||||
.where(`${qb.escape('identifier')} = :identifier`, {
|
||||
identifier,
|
||||
})
|
||||
.execute();
|
||||
}),
|
||||
);
|
||||
|
||||
if (!queryRunner) {
|
||||
await _queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets a query runner to work with.
|
||||
*/
|
||||
protected getQueryRunner(queryRunner?: QueryRunner): QueryRunner {
|
||||
if (queryRunner) return queryRunner;
|
||||
|
||||
return this.connection.createQueryRunner();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { QueryResultCacheOptions } from './QueryResultCacheOptions';
|
||||
import { QueryRunner } from '../query-runner/QueryRunner';
|
||||
|
||||
/**
|
||||
* Implementations of this interface provide different strategies to cache query builder results.
|
||||
*/
|
||||
export interface QueryResultCache {
|
||||
/**
|
||||
* Creates a connection with given cache provider.
|
||||
*/
|
||||
connect(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Closes a connection with given cache provider.
|
||||
*/
|
||||
disconnect(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Performs operations needs to be created during schema synchronization.
|
||||
*/
|
||||
synchronize(queryRunner?: QueryRunner): Promise<void>;
|
||||
|
||||
/**
|
||||
* Caches given query result.
|
||||
*/
|
||||
getFromCache(
|
||||
options: QueryResultCacheOptions,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<QueryResultCacheOptions | undefined>;
|
||||
|
||||
/**
|
||||
* Stores given query result in the cache.
|
||||
*/
|
||||
storeInCache(
|
||||
options: QueryResultCacheOptions,
|
||||
savedCache: QueryResultCacheOptions | undefined,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Checks if cache is expired or not.
|
||||
*/
|
||||
isExpired(savedCache: QueryResultCacheOptions): boolean;
|
||||
|
||||
/**
|
||||
* Clears everything stored in the cache.
|
||||
*/
|
||||
clear(queryRunner?: QueryRunner): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes all cached results by given identifiers from cache.
|
||||
*/
|
||||
remove(identifiers: string[], queryRunner?: QueryRunner): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { DbQueryResultCache } from './DbQueryResultCache';
|
||||
import { QueryResultCache } from './QueryResultCache';
|
||||
import { DataSource } from '../data-source/DataSource';
|
||||
import { TypeORMError } from '../error/TypeORMError';
|
||||
|
||||
/**
|
||||
* Caches query result into the database.
|
||||
*/
|
||||
export class QueryResultCacheFactory {
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected connection: DataSource) {}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new query result cache based on connection options.
|
||||
*/
|
||||
create(): QueryResultCache {
|
||||
if (!this.connection.options.cache)
|
||||
throw new TypeORMError(
|
||||
`To use cache you need to enable it in connection options by setting cache: true or providing some caching options. Example: { host: ..., username: ..., cache: true }`,
|
||||
);
|
||||
|
||||
const cache: any = this.connection.options.cache;
|
||||
|
||||
if (cache.provider && typeof cache.provider === 'function') {
|
||||
return cache.provider(this.connection);
|
||||
}
|
||||
|
||||
return new DbQueryResultCache(this.connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Options passed to QueryResultCache class.
|
||||
*/
|
||||
export interface QueryResultCacheOptions {
|
||||
/**
|
||||
* Cache identifier set by user.
|
||||
* Can be empty.
|
||||
*/
|
||||
identifier?: string;
|
||||
|
||||
/**
|
||||
* Time, when cache was created.
|
||||
*/
|
||||
time?: number;
|
||||
|
||||
/**
|
||||
* Duration in milliseconds during which results will be returned from cache.
|
||||
*/
|
||||
duration: number;
|
||||
|
||||
/**
|
||||
* Cached query.
|
||||
*/
|
||||
query?: string;
|
||||
|
||||
/**
|
||||
* Query result that will be cached.
|
||||
*/
|
||||
result?: any;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Same as Partial<T> but goes deeper and makes Partial<T> all its properties and sub-properties.
|
||||
*/
|
||||
export type DeepPartial<T> =
|
||||
| T
|
||||
| (T extends Array<infer U>
|
||||
? DeepPartial<U>[]
|
||||
: T extends Map<infer K, infer V>
|
||||
? Map<DeepPartial<K>, DeepPartial<V>>
|
||||
: T extends Set<infer M>
|
||||
? Set<DeepPartial<M>>
|
||||
: T extends object
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: T);
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ObjectType } from './ObjectType';
|
||||
import { EntitySchema } from '..';
|
||||
|
||||
/**
|
||||
* Entity target.
|
||||
*/
|
||||
export type EntityTarget<Entity> =
|
||||
| ObjectType<Entity>
|
||||
| EntitySchema<Entity>
|
||||
| string
|
||||
| { type: Entity; name: string };
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* List of T-s passed as an array or object map.
|
||||
*
|
||||
* Example usage: entities as an array of imported using import * as syntax.
|
||||
*/
|
||||
export type MixedList<T> = T[] | { [key: string]: T };
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Interface of the simple literal object with any string keys.
|
||||
*/
|
||||
export interface ObjectLiteral {
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Represents some Type of the Object.
|
||||
*/
|
||||
export type ObjectType<T> = { new (): T } | Function;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Pick only the keys that match the Type `U`
|
||||
*/
|
||||
export type PickKeysByType<T, U> = string &
|
||||
keyof {
|
||||
[P in keyof T as T[P] extends U ? P : never]: T[P];
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Wrapper type for relation type definitions in entities.
|
||||
* Used to circumvent ESM modules circular dependency issue caused by reflection metadata saving the type of the property.
|
||||
*
|
||||
* Usage example:
|
||||
* @Entity()
|
||||
* export default class User {
|
||||
*
|
||||
* @OneToOne(() => Profile, profile => profile.user)
|
||||
* profile: Relation<Profile>;
|
||||
*
|
||||
* }
|
||||
*/
|
||||
export type Relation<T> = T;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { DataSource } from '../data-source/DataSource';
|
||||
|
||||
/**
|
||||
* Connection is a single database ORM connection to a specific database.
|
||||
* Its not required to be a database connection, depend on database type it can create connection pool.
|
||||
* You can have multiple connections to multiple databases in your application.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export class Connection extends DataSource {}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { DataSource } from '../data-source/DataSource';
|
||||
import { ConnectionNotFoundError } from '../error/ConnectionNotFoundError';
|
||||
import { DataSourceOptions } from '../data-source/DataSourceOptions';
|
||||
import { AlreadyHasActiveConnectionError } from '../error/AlreadyHasActiveConnectionError';
|
||||
|
||||
/**
|
||||
* ConnectionManager is used to store and manage multiple orm connections.
|
||||
* It also provides useful factory methods to simplify connection creation.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export class ConnectionManager {
|
||||
/**
|
||||
* List of connections registered in this connection manager.
|
||||
*/
|
||||
get connections(): DataSource[] {
|
||||
return Array.from(this.connectionMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal lookup to quickly get from a connection name to the Connection object.
|
||||
*/
|
||||
private readonly connectionMap: Map<string, DataSource> = new Map();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks if connection with the given name exist in the manager.
|
||||
*/
|
||||
has(name: string): boolean {
|
||||
return this.connectionMap.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets registered connection with the given name.
|
||||
* If connection name is not given then it will get a default connection.
|
||||
* Throws error if connection with the given name was not found.
|
||||
*/
|
||||
get(name: string = 'default'): DataSource {
|
||||
const connection = this.connectionMap.get(name);
|
||||
if (!connection) throw new ConnectionNotFoundError(name);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection based on the given connection options and registers it in the manager.
|
||||
* Connection won't be established, you'll need to manually call connect method to establish connection.
|
||||
*/
|
||||
create(options: DataSourceOptions): DataSource {
|
||||
// check if such connection is already registered
|
||||
const existConnection = this.connectionMap.get(options.name || 'default');
|
||||
if (existConnection) {
|
||||
// if connection is registered and its not closed then throw an error
|
||||
if (existConnection.isInitialized)
|
||||
throw new AlreadyHasActiveConnectionError(options.name || 'default');
|
||||
}
|
||||
|
||||
// create a new connection
|
||||
const connection = new DataSource(options);
|
||||
this.connectionMap.set(connection.name, connection);
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { importClassesFromDirectories } from '../util/DirectoryExportedClassesLoader';
|
||||
import { OrmUtils } from '../util/OrmUtils';
|
||||
import { getFromContainer } from '../container';
|
||||
import { MigrationInterface } from '../migration/MigrationInterface';
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { EntityMetadataBuilder } from '../metadata-builder/EntityMetadataBuilder';
|
||||
import { EntitySchemaTransformer } from '../entity-schema/EntitySchemaTransformer';
|
||||
import { DataSource } from '../data-source/DataSource';
|
||||
import { EntitySchema } from '../entity-schema/EntitySchema';
|
||||
import { EntityMetadata } from '../metadata/EntityMetadata';
|
||||
import { EntitySubscriberInterface } from '../subscriber/EntitySubscriberInterface';
|
||||
import { InstanceChecker } from '../util/InstanceChecker';
|
||||
|
||||
/**
|
||||
* Builds migration instances, subscriber instances and entity metadatas for the given classes.
|
||||
*/
|
||||
export class ConnectionMetadataBuilder {
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected connection: DataSource) {}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Builds migration instances for the given classes or directories.
|
||||
*/
|
||||
async buildMigrations(migrations: (Function | string)[]): Promise<MigrationInterface[]> {
|
||||
const [migrationClasses, migrationDirectories] = OrmUtils.splitClassesAndStrings(migrations);
|
||||
const allMigrationClasses = [
|
||||
...migrationClasses,
|
||||
...(await importClassesFromDirectories(this.connection.logger, migrationDirectories)),
|
||||
];
|
||||
return allMigrationClasses.map((migrationClass) =>
|
||||
getFromContainer<MigrationInterface>(migrationClass),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds subscriber instances for the given classes or directories.
|
||||
*/
|
||||
async buildSubscribers(
|
||||
subscribers: (Function | string)[],
|
||||
): Promise<EntitySubscriberInterface<any>[]> {
|
||||
const [subscriberClasses, subscriberDirectories] = OrmUtils.splitClassesAndStrings(
|
||||
subscribers || [],
|
||||
);
|
||||
const allSubscriberClasses = [
|
||||
...subscriberClasses,
|
||||
...(await importClassesFromDirectories(this.connection.logger, subscriberDirectories)),
|
||||
];
|
||||
return getMetadataArgsStorage()
|
||||
.filterSubscribers(allSubscriberClasses)
|
||||
.map((metadata) => getFromContainer<EntitySubscriberInterface<any>>(metadata.target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity metadatas for the given classes or directories.
|
||||
*/
|
||||
async buildEntityMetadatas(
|
||||
entities: (Function | EntitySchema<any> | string)[],
|
||||
): Promise<EntityMetadata[]> {
|
||||
// todo: instead we need to merge multiple metadata args storages
|
||||
|
||||
const [entityClassesOrSchemas, entityDirectories] = OrmUtils.splitClassesAndStrings(
|
||||
entities || [],
|
||||
);
|
||||
const entityClasses: Function[] = entityClassesOrSchemas.filter(
|
||||
(entityClass) => !InstanceChecker.isEntitySchema(entityClass),
|
||||
) as any;
|
||||
const entitySchemas: EntitySchema<any>[] = entityClassesOrSchemas.filter((entityClass) =>
|
||||
InstanceChecker.isEntitySchema(entityClass),
|
||||
) as any;
|
||||
|
||||
const allEntityClasses = [
|
||||
...entityClasses,
|
||||
...(await importClassesFromDirectories(this.connection.logger, entityDirectories)),
|
||||
];
|
||||
allEntityClasses.forEach((entityClass) => {
|
||||
// if we have entity schemas loaded from directories
|
||||
if (InstanceChecker.isEntitySchema(entityClass)) {
|
||||
entitySchemas.push(entityClass);
|
||||
}
|
||||
});
|
||||
const decoratorEntityMetadatas = new EntityMetadataBuilder(
|
||||
this.connection,
|
||||
getMetadataArgsStorage(),
|
||||
).build(allEntityClasses);
|
||||
|
||||
const metadataArgsStorageFromSchema = new EntitySchemaTransformer().transform(entitySchemas);
|
||||
const schemaEntityMetadatas = new EntityMetadataBuilder(
|
||||
this.connection,
|
||||
metadataArgsStorageFromSchema,
|
||||
).build();
|
||||
|
||||
return [...decoratorEntityMetadatas, ...schemaEntityMetadatas];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { DataSourceOptions } from '../data-source/DataSourceOptions';
|
||||
|
||||
/**
|
||||
* ConnectionOptions is an interface with settings and options for specific connection.
|
||||
* Options contain database and other connection-related settings.
|
||||
* Consumer must provide connection options for each of your connections.
|
||||
*
|
||||
* @deprecated use DataSourceOptions instead
|
||||
*/
|
||||
export type ConnectionOptions = DataSourceOptions;
|
||||
@@ -0,0 +1,233 @@
|
||||
import appRootPath from 'app-root-path';
|
||||
import path from 'path';
|
||||
import { DataSourceOptions } from '../data-source/DataSourceOptions';
|
||||
import { PlatformTools } from '../platform/PlatformTools';
|
||||
import { ConnectionOptionsEnvReader } from './options-reader/ConnectionOptionsEnvReader';
|
||||
import { TypeORMError } from '../error';
|
||||
import { isAbsolute } from '../util/PathUtils';
|
||||
import { importOrRequireFile } from '../util/ImportUtils';
|
||||
|
||||
/**
|
||||
* Reads connection options from the ormconfig.
|
||||
*/
|
||||
export class ConnectionOptionsReader {
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(
|
||||
protected options?: {
|
||||
/**
|
||||
* Directory where ormconfig should be read from.
|
||||
* By default its your application root (where your app package.json is located).
|
||||
*/
|
||||
root?: string;
|
||||
|
||||
/**
|
||||
* Filename of the ormconfig configuration. By default its equal to "ormconfig".
|
||||
*/
|
||||
configName?: string;
|
||||
},
|
||||
) {}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns all connection options read from the ormconfig.
|
||||
*/
|
||||
async all(): Promise<DataSourceOptions[]> {
|
||||
const options = await this.load();
|
||||
if (!options)
|
||||
throw new TypeORMError(`No connection options were found in any orm configuration files.`);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a connection with a given name read from ormconfig.
|
||||
* If connection with such name would not be found then it throw error.
|
||||
*/
|
||||
async get(name: string): Promise<DataSourceOptions> {
|
||||
const allOptions = await this.all();
|
||||
const targetOptions = allOptions.find(
|
||||
(options) => options.name === name || (name === 'default' && !options.name),
|
||||
);
|
||||
if (!targetOptions)
|
||||
throw new TypeORMError(
|
||||
`Cannot find connection ${name} because its not defined in any orm configuration files.`,
|
||||
);
|
||||
|
||||
return targetOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a TypeORM configuration file.
|
||||
*/
|
||||
async has(name: string): Promise<boolean> {
|
||||
const allOptions = await this.load();
|
||||
if (!allOptions) return false;
|
||||
|
||||
const targetOptions = allOptions.find(
|
||||
(options) => options.name === name || (name === 'default' && !options.name),
|
||||
);
|
||||
return !!targetOptions;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads all connection options from a configuration file.
|
||||
*
|
||||
* todo: get in count NODE_ENV somehow
|
||||
*/
|
||||
protected async load(): Promise<DataSourceOptions[] | undefined> {
|
||||
let connectionOptions: DataSourceOptions | DataSourceOptions[] | undefined = undefined;
|
||||
|
||||
const fileFormats = ['env', 'js', 'mjs', 'cjs', 'ts', 'mts', 'cts', 'json'];
|
||||
|
||||
// Detect if baseFilePath contains file extension
|
||||
const possibleExtension = this.baseFilePath.substr(this.baseFilePath.lastIndexOf('.'));
|
||||
const fileExtension = fileFormats.find((extension) => `.${extension}` === possibleExtension);
|
||||
|
||||
// try to find any of following configuration formats
|
||||
const foundFileFormat =
|
||||
fileExtension ||
|
||||
fileFormats.find((format) => {
|
||||
return PlatformTools.fileExist(this.baseFilePath + '.' + format);
|
||||
});
|
||||
|
||||
// Determine config file name
|
||||
const configFile = fileExtension
|
||||
? this.baseFilePath
|
||||
: this.baseFilePath + '.' + foundFileFormat;
|
||||
|
||||
// if .env file found then load all its variables into process.env using dotenv package
|
||||
if (foundFileFormat === 'env') {
|
||||
PlatformTools.dotenv(configFile);
|
||||
} else if (PlatformTools.fileExist(this.baseDirectory + '/.env')) {
|
||||
PlatformTools.dotenv(this.baseDirectory + '/.env');
|
||||
}
|
||||
|
||||
// try to find connection options from any of available sources of configuration
|
||||
if (
|
||||
PlatformTools.getEnvVariable('TYPEORM_CONNECTION') ||
|
||||
PlatformTools.getEnvVariable('TYPEORM_URL')
|
||||
) {
|
||||
connectionOptions = await new ConnectionOptionsEnvReader().read();
|
||||
} else if (
|
||||
foundFileFormat === 'js' ||
|
||||
foundFileFormat === 'mjs' ||
|
||||
foundFileFormat === 'cjs' ||
|
||||
foundFileFormat === 'ts' ||
|
||||
foundFileFormat === 'mts' ||
|
||||
foundFileFormat === 'cts'
|
||||
) {
|
||||
const [importOrRequireResult, moduleSystem] = await importOrRequireFile(configFile);
|
||||
const configModule = await importOrRequireResult;
|
||||
|
||||
if (
|
||||
moduleSystem === 'esm' ||
|
||||
(configModule && '__esModule' in configModule && 'default' in configModule)
|
||||
) {
|
||||
connectionOptions = configModule.default;
|
||||
} else {
|
||||
connectionOptions = configModule;
|
||||
}
|
||||
} else if (foundFileFormat === 'json') {
|
||||
connectionOptions = require(configFile);
|
||||
}
|
||||
|
||||
// normalize and return connection options
|
||||
if (connectionOptions) {
|
||||
return this.normalizeConnectionOptions(connectionOptions);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize connection options.
|
||||
*/
|
||||
protected normalizeConnectionOptions(
|
||||
connectionOptions: DataSourceOptions | DataSourceOptions[],
|
||||
): DataSourceOptions[] {
|
||||
if (!Array.isArray(connectionOptions)) connectionOptions = [connectionOptions];
|
||||
|
||||
connectionOptions.forEach((options) => {
|
||||
options.baseDirectory = this.baseDirectory;
|
||||
if (options.entities) {
|
||||
const entities = (options.entities as any[]).map((entity) => {
|
||||
if (typeof entity === 'string' && entity.substr(0, 1) !== '/')
|
||||
return this.baseDirectory + '/' + entity;
|
||||
|
||||
return entity;
|
||||
});
|
||||
Object.assign(connectionOptions, { entities: entities });
|
||||
}
|
||||
if (options.subscribers) {
|
||||
const subscribers = (options.subscribers as any[]).map((subscriber) => {
|
||||
if (typeof subscriber === 'string' && subscriber.substr(0, 1) !== '/')
|
||||
return this.baseDirectory + '/' + subscriber;
|
||||
|
||||
return subscriber;
|
||||
});
|
||||
Object.assign(connectionOptions, { subscribers: subscribers });
|
||||
}
|
||||
if (options.migrations) {
|
||||
const migrations = (options.migrations as any[]).map((migration) => {
|
||||
if (typeof migration === 'string' && migration.substr(0, 1) !== '/')
|
||||
return this.baseDirectory + '/' + migration;
|
||||
|
||||
return migration;
|
||||
});
|
||||
Object.assign(connectionOptions, { migrations: migrations });
|
||||
}
|
||||
|
||||
// make database path file in sqlite relative to package.json
|
||||
if (options.type === 'sqlite' || options.type === 'sqlite-pooled') {
|
||||
if (
|
||||
typeof options.database === 'string' &&
|
||||
!isAbsolute(options.database) &&
|
||||
options.database.substr(0, 1) !== '/' && // unix absolute
|
||||
options.database.substr(1, 2) !== ':\\' && // windows absolute
|
||||
options.database !== ':memory:'
|
||||
) {
|
||||
Object.assign(options, {
|
||||
database: this.baseDirectory + '/' + options.database,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return connectionOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets directory where configuration file should be located and configuration file name.
|
||||
*/
|
||||
protected get baseFilePath(): string {
|
||||
return path.resolve(this.baseDirectory, this.baseConfigName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets directory where configuration file should be located.
|
||||
*/
|
||||
protected get baseDirectory(): string {
|
||||
if (this.options && this.options.root) return this.options.root;
|
||||
|
||||
return appRootPath.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets configuration file name.
|
||||
*/
|
||||
protected get baseConfigName(): string {
|
||||
if (this.options && this.options.configName) return this.options.configName;
|
||||
|
||||
return 'ormconfig';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { DataSourceOptions } from '../../data-source/DataSourceOptions';
|
||||
import { PlatformTools } from '../../platform/PlatformTools';
|
||||
import { OrmUtils } from '../../util/OrmUtils';
|
||||
|
||||
/**
|
||||
* Reads connection options from environment variables.
|
||||
* Environment variables can have only a single connection.
|
||||
* Its strongly required to define TYPEORM_CONNECTION env variable.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export class ConnectionOptionsEnvReader {
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reads connection options from environment variables.
|
||||
*/
|
||||
async read(): Promise<DataSourceOptions[]> {
|
||||
return [
|
||||
{
|
||||
type:
|
||||
PlatformTools.getEnvVariable('TYPEORM_CONNECTION') ||
|
||||
(PlatformTools.getEnvVariable('TYPEORM_URL')
|
||||
? PlatformTools.getEnvVariable('TYPEORM_URL').split('://')[0]
|
||||
: undefined),
|
||||
url: PlatformTools.getEnvVariable('TYPEORM_URL'),
|
||||
host: PlatformTools.getEnvVariable('TYPEORM_HOST'),
|
||||
port: this.stringToNumber(PlatformTools.getEnvVariable('TYPEORM_PORT')),
|
||||
username: PlatformTools.getEnvVariable('TYPEORM_USERNAME'),
|
||||
password: PlatformTools.getEnvVariable('TYPEORM_PASSWORD'),
|
||||
database: PlatformTools.getEnvVariable('TYPEORM_DATABASE'),
|
||||
schema: PlatformTools.getEnvVariable('TYPEORM_SCHEMA'),
|
||||
extra: PlatformTools.getEnvVariable('TYPEORM_DRIVER_EXTRA')
|
||||
? JSON.parse(PlatformTools.getEnvVariable('TYPEORM_DRIVER_EXTRA'))
|
||||
: undefined,
|
||||
synchronize: OrmUtils.toBoolean(PlatformTools.getEnvVariable('TYPEORM_SYNCHRONIZE')),
|
||||
dropSchema: OrmUtils.toBoolean(PlatformTools.getEnvVariable('TYPEORM_DROP_SCHEMA')),
|
||||
migrationsRun: OrmUtils.toBoolean(PlatformTools.getEnvVariable('TYPEORM_MIGRATIONS_RUN')),
|
||||
entities: this.stringToArray(PlatformTools.getEnvVariable('TYPEORM_ENTITIES')),
|
||||
migrations: this.stringToArray(PlatformTools.getEnvVariable('TYPEORM_MIGRATIONS')),
|
||||
migrationsTableName: PlatformTools.getEnvVariable('TYPEORM_MIGRATIONS_TABLE_NAME'),
|
||||
metadataTableName: PlatformTools.getEnvVariable('TYPEORM_METADATA_TABLE_NAME'),
|
||||
subscribers: this.stringToArray(PlatformTools.getEnvVariable('TYPEORM_SUBSCRIBERS')),
|
||||
logging: this.transformLogging(PlatformTools.getEnvVariable('TYPEORM_LOGGING')),
|
||||
logger: PlatformTools.getEnvVariable('TYPEORM_LOGGER'),
|
||||
entityPrefix: PlatformTools.getEnvVariable('TYPEORM_ENTITY_PREFIX'),
|
||||
maxQueryExecutionTime: PlatformTools.getEnvVariable('TYPEORM_MAX_QUERY_EXECUTION_TIME'),
|
||||
cache: this.transformCaching(),
|
||||
uuidExtension: PlatformTools.getEnvVariable('TYPEORM_UUID_EXTENSION'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Transforms logging string into real logging value connection requires.
|
||||
*/
|
||||
protected transformLogging(logging: string): any {
|
||||
if (logging === 'true' || logging === 'TRUE' || logging === '1') return true;
|
||||
if (logging === 'all') return 'all';
|
||||
|
||||
return this.stringToArray(logging);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms caching option into real caching value option requires.
|
||||
*/
|
||||
protected transformCaching(): boolean | object | undefined {
|
||||
const caching = PlatformTools.getEnvVariable('TYPEORM_CACHE');
|
||||
if (caching === 'true' || caching === 'TRUE' || caching === '1') return true;
|
||||
if (caching === 'false' || caching === 'FALSE' || caching === '0') return false;
|
||||
if (caching === 'database')
|
||||
return {
|
||||
type: caching,
|
||||
options: PlatformTools.getEnvVariable('TYPEORM_CACHE_OPTIONS')
|
||||
? JSON.parse(PlatformTools.getEnvVariable('TYPEORM_CACHE_OPTIONS'))
|
||||
: undefined,
|
||||
alwaysEnabled: PlatformTools.getEnvVariable('TYPEORM_CACHE_ALWAYS_ENABLED'),
|
||||
duration: parseInt(PlatformTools.getEnvVariable('TYPEORM_CACHE_DURATION')),
|
||||
};
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string which contains multiple elements split by comma into a string array of strings.
|
||||
*/
|
||||
protected stringToArray(variable?: string) {
|
||||
if (!variable) return [];
|
||||
return variable.split(',').map((str) => str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string which contains a number into a javascript number
|
||||
*/
|
||||
private stringToNumber(value: any): number | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return parseInt(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Container options.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export interface UseContainerOptions {
|
||||
/**
|
||||
* If set to true, then default container will be used in the case if given container haven't returned anything.
|
||||
*/
|
||||
fallback?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true, then default container will be used in the case if given container thrown an exception.
|
||||
*/
|
||||
fallbackOnErrors?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type ContainedType<T> = { new (...args: any[]): T } | Function;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export interface ContainerInterface {
|
||||
get<T>(someClass: ContainedType<T>): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container to be used by this library for inversion control. If container was not implicitly set then by default
|
||||
* container simply creates a new instance of the given class.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
const defaultContainer: ContainerInterface = new (class implements ContainerInterface {
|
||||
private instances: { type: Function; object: any }[] = [];
|
||||
|
||||
get<T>(someClass: ContainedType<T>): T {
|
||||
let instance = this.instances.find((i) => i.type === someClass);
|
||||
if (!instance) {
|
||||
instance = {
|
||||
type: someClass,
|
||||
object: new (someClass as new () => T)(),
|
||||
};
|
||||
this.instances.push(instance);
|
||||
}
|
||||
|
||||
return instance.object;
|
||||
}
|
||||
})();
|
||||
|
||||
let userContainer: ContainerInterface;
|
||||
let userContainerOptions: UseContainerOptions | undefined;
|
||||
|
||||
/**
|
||||
* Sets container to be used by this library.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export function useContainer(iocContainer: ContainerInterface, options?: UseContainerOptions) {
|
||||
userContainer = iocContainer;
|
||||
userContainerOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the IOC container used by this library.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export function getFromContainer<T>(someClass: ContainedType<T>): T {
|
||||
if (userContainer) {
|
||||
try {
|
||||
const instance = userContainer.get(someClass);
|
||||
if (instance) return instance;
|
||||
|
||||
if (!userContainerOptions || !userContainerOptions.fallback) return instance;
|
||||
} catch (error) {
|
||||
if (!userContainerOptions || !userContainerOptions.fallbackOnErrors) throw error;
|
||||
}
|
||||
}
|
||||
return defaultContainer.get<T>(someClass);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
import { EntitySchema } from '../entity-schema/EntitySchema';
|
||||
import { LoggerOptions } from '../logger/LoggerOptions';
|
||||
import { NamingStrategyInterface } from '../naming-strategy/NamingStrategyInterface';
|
||||
import { DatabaseType } from '../driver/types/DatabaseType';
|
||||
import { Logger } from '../logger/Logger';
|
||||
import { DataSource } from '../data-source/DataSource';
|
||||
import { QueryResultCache } from '../cache/QueryResultCache';
|
||||
import { MixedList } from '../common/MixedList';
|
||||
|
||||
/**
|
||||
* BaseDataSourceOptions is set of DataSourceOptions shared by all database types.
|
||||
*/
|
||||
export interface BaseDataSourceOptions {
|
||||
/**
|
||||
* Database type. This value is required.
|
||||
*/
|
||||
readonly type: DatabaseType;
|
||||
|
||||
/**
|
||||
* Connection name. If connection name is not given then it will be called "default".
|
||||
* Different connections must have different names.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Entities to be loaded for this connection.
|
||||
* Accepts both entity classes and directories where from entities need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
readonly entities?: MixedList<Function | string | EntitySchema>;
|
||||
|
||||
/**
|
||||
* Subscribers to be loaded for this connection.
|
||||
* Accepts both subscriber classes and directories where from subscribers need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
readonly subscribers?: MixedList<Function | string>;
|
||||
|
||||
/**
|
||||
* Migrations to be loaded for this connection.
|
||||
* Accepts both migration classes and glob patterns representing migration files.
|
||||
*/
|
||||
readonly migrations?: MixedList<Function | string>;
|
||||
|
||||
/**
|
||||
* Migrations table name, in case of different name from "migrations".
|
||||
* Accepts single string name.
|
||||
*/
|
||||
readonly migrationsTableName?: string;
|
||||
|
||||
/**
|
||||
* Transaction mode for migrations to run in
|
||||
*/
|
||||
readonly migrationsTransactionMode?: 'all' | 'none' | 'each';
|
||||
|
||||
/**
|
||||
* Typeorm metadata table name, in case of different name from "typeorm_metadata".
|
||||
* Accepts single string name.
|
||||
*/
|
||||
readonly metadataTableName?: string;
|
||||
|
||||
/**
|
||||
* Naming strategy to be used to name tables and columns in the database.
|
||||
*/
|
||||
readonly namingStrategy?: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Logging options.
|
||||
*/
|
||||
readonly logging?: LoggerOptions;
|
||||
|
||||
/**
|
||||
* Logger instance used to log queries and events in the ORM.
|
||||
*/
|
||||
readonly logger?: 'advanced-console' | 'simple-console' | 'file' | 'debug' | Logger;
|
||||
|
||||
/**
|
||||
* Maximum number of milliseconds query should be executed before logger log a warning.
|
||||
*/
|
||||
readonly maxQueryExecutionTime?: number;
|
||||
|
||||
/**
|
||||
* Maximum number of clients the pool should contain.
|
||||
*/
|
||||
readonly poolSize?: number;
|
||||
|
||||
/**
|
||||
* Indicates if database schema should be auto created on every application launch.
|
||||
* Be careful with this option and don't use this in production - otherwise you can lose production data.
|
||||
* This option is useful during debug and development.
|
||||
* Alternative to it, you can use CLI and run schema:sync command.
|
||||
*
|
||||
* Note that for MongoDB database it does not create schema, because MongoDB is schemaless.
|
||||
* Instead, it syncs just by creating indices.
|
||||
*/
|
||||
readonly synchronize?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if migrations should be auto run on every application launch.
|
||||
* Alternative to it, you can use CLI and run migrations:run command.
|
||||
*/
|
||||
readonly migrationsRun?: boolean;
|
||||
|
||||
/**
|
||||
* Drops the schema each time connection is being established.
|
||||
* Be careful with this option and don't use this in production - otherwise you'll lose all production data.
|
||||
* This option is useful during debug and development.
|
||||
*/
|
||||
readonly dropSchema?: boolean;
|
||||
|
||||
/**
|
||||
* Prefix to use on all tables (collections) of this connection in the database.
|
||||
*/
|
||||
readonly entityPrefix?: string;
|
||||
|
||||
/**
|
||||
* When creating new Entity instances, skip all constructors when true.
|
||||
*/
|
||||
readonly entitySkipConstructor?: boolean;
|
||||
|
||||
/**
|
||||
* Extra connection options to be passed to the underlying driver.
|
||||
*
|
||||
* todo: deprecate this and move all database-specific types into hts own connection options object.
|
||||
*/
|
||||
readonly extra?: any;
|
||||
|
||||
/**
|
||||
* Specifies how relations must be loaded - using "joins" or separate queries.
|
||||
* If you are loading too much data with nested joins it's better to load relations
|
||||
* using separate queries.
|
||||
*
|
||||
* Default strategy is "join", but this default can be changed here.
|
||||
* Also, strategy can be set per-query in FindOptions and QueryBuilder.
|
||||
*/
|
||||
readonly relationLoadStrategy?: 'join' | 'query';
|
||||
|
||||
/**
|
||||
* Optionally applied "typename" to the model.
|
||||
* If set, then each hydrated model will have this property with the target model / entity name inside.
|
||||
*
|
||||
* (works like a discriminator property).
|
||||
*/
|
||||
readonly typename?: string;
|
||||
|
||||
/**
|
||||
* Holds reference to the baseDirectory where configuration file are expected.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
baseDirectory?: string;
|
||||
|
||||
/**
|
||||
* Allows to setup cache options.
|
||||
*/
|
||||
readonly cache?:
|
||||
| boolean
|
||||
| {
|
||||
/**
|
||||
* Type of caching.
|
||||
*
|
||||
* - "database" means cached values will be stored in the separate table in database. This is default value.
|
||||
*/
|
||||
readonly type?: 'database';
|
||||
|
||||
/**
|
||||
* Factory function for custom cache providers that implement QueryResultCache.
|
||||
*/
|
||||
readonly provider?: (connection: DataSource) => QueryResultCache;
|
||||
|
||||
/**
|
||||
* Configurable table name for "database" type cache.
|
||||
* Default value is "query-result-cache"
|
||||
*/
|
||||
readonly tableName?: string;
|
||||
|
||||
/**
|
||||
* If set to true then queries (using find methods and QueryBuilder's methods) will always be cached.
|
||||
*/
|
||||
readonly alwaysEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* Time in milliseconds in which cache will expire.
|
||||
* This can be setup per-query.
|
||||
* Default value is 1000 which is equivalent to 1 second.
|
||||
*/
|
||||
readonly duration?: number;
|
||||
|
||||
/**
|
||||
* Used to specify if cache errors should be ignored, and pass through the call to the Database.
|
||||
*/
|
||||
readonly ignoreErrors?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows automatic isolation of where clauses
|
||||
*/
|
||||
readonly isolateWhereStatements?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
import { Driver } from '../driver/Driver';
|
||||
import { registerQueryBuilders } from '../query-builder';
|
||||
import { Repository } from '../repository/Repository';
|
||||
import { EntitySubscriberInterface } from '../subscriber/EntitySubscriberInterface';
|
||||
import { EntityTarget } from '../common/EntityTarget';
|
||||
import { ObjectType } from '../common/ObjectType';
|
||||
import { EntityManager } from '../entity-manager/EntityManager';
|
||||
import { DefaultNamingStrategy } from '../naming-strategy/DefaultNamingStrategy';
|
||||
import {
|
||||
CannotConnectAlreadyConnectedError,
|
||||
CannotExecuteNotConnectedError,
|
||||
EntityMetadataNotFoundError,
|
||||
QueryRunnerProviderAlreadyReleasedError,
|
||||
TypeORMError,
|
||||
} from '../error';
|
||||
import { TreeRepository } from '../repository/TreeRepository';
|
||||
import { NamingStrategyInterface } from '../naming-strategy/NamingStrategyInterface';
|
||||
import { EntityMetadata } from '../metadata/EntityMetadata';
|
||||
import { Logger } from '../logger/Logger';
|
||||
import { MigrationInterface } from '../migration/MigrationInterface';
|
||||
import { MigrationExecutor } from '../migration/MigrationExecutor';
|
||||
import { Migration } from '../migration/Migration';
|
||||
import { EntityMetadataValidator } from '../metadata-builder/EntityMetadataValidator';
|
||||
import { DataSourceOptions } from './DataSourceOptions';
|
||||
import { EntityManagerFactory } from '../entity-manager/EntityManagerFactory';
|
||||
import { DriverFactory } from '../driver/DriverFactory';
|
||||
import { ConnectionMetadataBuilder } from '../connection/ConnectionMetadataBuilder';
|
||||
import { QueryRunner } from '../query-runner/QueryRunner';
|
||||
import { SelectQueryBuilder } from '../query-builder/SelectQueryBuilder';
|
||||
import { LoggerFactory } from '../logger/LoggerFactory';
|
||||
import { QueryResultCacheFactory } from '../cache/QueryResultCacheFactory';
|
||||
import { QueryResultCache } from '../cache/QueryResultCache';
|
||||
import { RelationLoader } from '../query-builder/RelationLoader';
|
||||
import { ObjectUtils } from '../util/ObjectUtils';
|
||||
import { IsolationLevel } from '../driver/types/IsolationLevel';
|
||||
import { ReplicationMode } from '../driver/types/ReplicationMode';
|
||||
import { RelationIdLoader } from '../query-builder/RelationIdLoader';
|
||||
import { DriverUtils } from '../driver/DriverUtils';
|
||||
import { InstanceChecker } from '../util/InstanceChecker';
|
||||
import { ObjectLiteral } from '../common/ObjectLiteral';
|
||||
|
||||
registerQueryBuilders();
|
||||
|
||||
/**
|
||||
* DataSource is a pre-defined connection configuration to a specific database.
|
||||
* You can have multiple data sources connected (with multiple connections in it),
|
||||
* connected to multiple databases in your application.
|
||||
*
|
||||
* Before, it was called `Connection`, but now `Connection` is deprecated
|
||||
* because `Connection` isn't the best name for what it's actually is.
|
||||
*/
|
||||
export class DataSource {
|
||||
readonly '@instanceof' = Symbol.for('DataSource');
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Readonly Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connection name.
|
||||
*
|
||||
* @deprecated we don't need names anymore since we are going to drop all related methods relying on this property.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Connection options.
|
||||
*/
|
||||
readonly options: DataSourceOptions;
|
||||
|
||||
/**
|
||||
* Indicates if DataSource is initialized or not.
|
||||
*/
|
||||
readonly isInitialized: boolean;
|
||||
|
||||
/**
|
||||
* Database driver used by this connection.
|
||||
*/
|
||||
driver: Driver;
|
||||
|
||||
/**
|
||||
* EntityManager of this connection.
|
||||
*/
|
||||
readonly manager: EntityManager;
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Name for the metadata table
|
||||
*/
|
||||
readonly metadataTableName: string;
|
||||
|
||||
/**
|
||||
* Logger used to log orm events.
|
||||
*/
|
||||
logger: Logger;
|
||||
|
||||
/**
|
||||
* Migration instances that are registered for this connection.
|
||||
*/
|
||||
readonly migrations: MigrationInterface[] = [];
|
||||
|
||||
/**
|
||||
* Entity subscriber instances that are registered for this connection.
|
||||
*/
|
||||
readonly subscribers: EntitySubscriberInterface<any>[] = [];
|
||||
|
||||
/**
|
||||
* All entity metadatas that are registered for this connection.
|
||||
*/
|
||||
readonly entityMetadatas: EntityMetadata[] = [];
|
||||
|
||||
/**
|
||||
* All entity metadatas that are registered for this connection.
|
||||
* This is a copy of #.entityMetadatas property -> used for more performant searches.
|
||||
*/
|
||||
readonly entityMetadatasMap = new Map<EntityTarget<any>, EntityMetadata>();
|
||||
|
||||
/**
|
||||
* Used to work with query result cache.
|
||||
*/
|
||||
queryResultCache?: QueryResultCache;
|
||||
|
||||
/**
|
||||
* Used to load relations and work with lazy relations.
|
||||
*/
|
||||
readonly relationLoader: RelationLoader;
|
||||
|
||||
readonly relationIdLoader: RelationIdLoader;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DataSourceOptions) {
|
||||
registerQueryBuilders();
|
||||
this.name = options.name || 'default';
|
||||
this.options = options;
|
||||
this.logger = new LoggerFactory().create(this.options.logger, this.options.logging);
|
||||
this.manager = this.createEntityManager();
|
||||
this.namingStrategy = options.namingStrategy || new DefaultNamingStrategy();
|
||||
this.metadataTableName = options.metadataTableName || 'typeorm_metadata';
|
||||
this.relationLoader = new RelationLoader(this);
|
||||
this.relationIdLoader = new RelationIdLoader(this);
|
||||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Accessors
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Indicates if DataSource is initialized or not.
|
||||
*
|
||||
* @deprecated use .isInitialized instead
|
||||
*/
|
||||
get isConnected() {
|
||||
return this.isInitialized;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
/**
|
||||
* Updates current connection options with provided options.
|
||||
*/
|
||||
setOptions(options: Partial<DataSourceOptions>): this {
|
||||
Object.assign(this.options, options);
|
||||
|
||||
if (options.logger || options.logging) {
|
||||
this.logger = new LoggerFactory().create(
|
||||
options.logger || this.options.logger,
|
||||
options.logging || this.options.logging,
|
||||
);
|
||||
}
|
||||
|
||||
if (options.namingStrategy) {
|
||||
this.namingStrategy = options.namingStrategy;
|
||||
}
|
||||
|
||||
if (options.cache) {
|
||||
this.queryResultCache = new QueryResultCacheFactory(this).create();
|
||||
}
|
||||
|
||||
// todo: we must update the database in the driver as well, if it was set by setOptions method
|
||||
// in the future we need to refactor the code and remove "database" from the driver, and instead
|
||||
// use database (and options) from a single place - data source.
|
||||
if (options.database) {
|
||||
this.driver.database = DriverUtils.buildDriverOptions(this.options).database;
|
||||
}
|
||||
|
||||
// todo: need to take a look if we need to update schema and other "poor" properties
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
* This method should be called once on application bootstrap.
|
||||
* This method not necessarily creates database connection (depend on database type),
|
||||
* but it also can setup a connection pool with database to use.
|
||||
*/
|
||||
async initialize(): Promise<this> {
|
||||
if (this.isInitialized) throw new CannotConnectAlreadyConnectedError(this.name);
|
||||
|
||||
this.driver = await DriverFactory.create(this);
|
||||
this.queryResultCache = this.options.cache
|
||||
? new QueryResultCacheFactory(this).create()
|
||||
: undefined;
|
||||
|
||||
// connect to the database via its driver
|
||||
await this.driver.connect();
|
||||
|
||||
// connect to the cache-specific database if cache is enabled
|
||||
if (this.queryResultCache) await this.queryResultCache.connect();
|
||||
|
||||
// set connected status for the current connection
|
||||
ObjectUtils.assign(this, { isInitialized: true });
|
||||
|
||||
try {
|
||||
// build all metadatas registered in the current connection
|
||||
await this.buildMetadatas();
|
||||
|
||||
await this.driver.afterConnect();
|
||||
|
||||
// if option is set - drop schema once connection is done
|
||||
if (this.options.dropSchema) await this.dropDatabase();
|
||||
|
||||
// if option is set - automatically synchronize a schema
|
||||
if (this.options.migrationsRun)
|
||||
await this.runMigrations({
|
||||
transaction: this.options.migrationsTransactionMode,
|
||||
});
|
||||
|
||||
// if option is set - automatically synchronize a schema
|
||||
if (this.options.synchronize) await this.synchronize();
|
||||
} catch (error) {
|
||||
// if for some reason build metadata fail (for example validation error during entity metadata check)
|
||||
// connection needs to be closed
|
||||
await this.destroy();
|
||||
throw error;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
* This method should be called once on application bootstrap.
|
||||
* This method not necessarily creates database connection (depend on database type),
|
||||
* but it also can setup a connection pool with database to use.
|
||||
*
|
||||
* @deprecated use .initialize method instead
|
||||
*/
|
||||
async connect(): Promise<this> {
|
||||
return this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes connection with the database.
|
||||
* Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
|
||||
*/
|
||||
async destroy(): Promise<void> {
|
||||
if (!this.isInitialized) throw new CannotExecuteNotConnectedError(this.name);
|
||||
|
||||
await this.driver.disconnect();
|
||||
|
||||
// disconnect from the cache-specific database if cache was enabled
|
||||
if (this.queryResultCache) await this.queryResultCache.disconnect();
|
||||
|
||||
ObjectUtils.assign(this, { isInitialized: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes connection with the database.
|
||||
* Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
|
||||
*
|
||||
* @deprecated use .destroy method instead
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
return this.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates database schema for all entities registered in this connection.
|
||||
* Can be used only after connection to the database is established.
|
||||
*
|
||||
* @param dropBeforeSync If set to true then it drops the database with all its tables and data
|
||||
*/
|
||||
async synchronize(dropBeforeSync: boolean = false): Promise<void> {
|
||||
if (!this.isInitialized) throw new CannotExecuteNotConnectedError(this.name);
|
||||
|
||||
if (dropBeforeSync) await this.dropDatabase();
|
||||
|
||||
const schemaBuilder = this.driver.createSchemaBuilder();
|
||||
await schemaBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the database and all its data.
|
||||
* Be careful with this method on production since this method will erase all your database tables and their data.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
// TODO rename
|
||||
async dropDatabase(): Promise<void> {
|
||||
const queryRunner = this.createQueryRunner();
|
||||
try {
|
||||
if (DriverUtils.isSQLiteFamily(this.driver)) {
|
||||
const databases: string[] = [];
|
||||
this.entityMetadatas.forEach((metadata) => {
|
||||
if (metadata.database && databases.indexOf(metadata.database) === -1)
|
||||
databases.push(metadata.database);
|
||||
});
|
||||
if (databases.length === 0 && this.driver.database) {
|
||||
databases.push(this.driver.database);
|
||||
}
|
||||
|
||||
if (databases.length === 0) {
|
||||
await queryRunner.clearDatabase();
|
||||
} else {
|
||||
for (const database of databases) {
|
||||
await queryRunner.clearDatabase(database);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await queryRunner.clearDatabase();
|
||||
}
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all pending migrations.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async runMigrations(options?: {
|
||||
transaction?: 'all' | 'none' | 'each';
|
||||
fake?: boolean;
|
||||
}): Promise<Migration[]> {
|
||||
if (!this.isInitialized) throw new CannotExecuteNotConnectedError(this.name);
|
||||
|
||||
const migrationExecutor = new MigrationExecutor(this);
|
||||
migrationExecutor.transaction =
|
||||
options?.transaction || this.options?.migrationsTransactionMode || 'all';
|
||||
migrationExecutor.fake = (options && options.fake) || false;
|
||||
|
||||
const successMigrations = await migrationExecutor.executePendingMigrations();
|
||||
return successMigrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts last executed migration.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async undoLastMigration(options?: {
|
||||
transaction?: 'all' | 'none' | 'each';
|
||||
fake?: boolean;
|
||||
}): Promise<void> {
|
||||
if (!this.isInitialized) throw new CannotExecuteNotConnectedError(this.name);
|
||||
|
||||
const migrationExecutor = new MigrationExecutor(this);
|
||||
migrationExecutor.transaction = (options && options.transaction) || 'all';
|
||||
migrationExecutor.fake = (options && options.fake) || false;
|
||||
|
||||
await migrationExecutor.undoLastMigration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all migrations and whether they have been run.
|
||||
* Returns true if there are pending migrations
|
||||
*/
|
||||
async showMigrations(): Promise<boolean> {
|
||||
if (!this.isInitialized) {
|
||||
throw new CannotExecuteNotConnectedError(this.name);
|
||||
}
|
||||
const migrationExecutor = new MigrationExecutor(this);
|
||||
return await migrationExecutor.showMigrations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity metadata exist for the given entity class, target name or table name.
|
||||
*/
|
||||
hasMetadata(target: EntityTarget<any>): boolean {
|
||||
return !!this.findMetadata(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity metadata for the given entity class or schema name.
|
||||
*/
|
||||
getMetadata(target: EntityTarget<any>): EntityMetadata {
|
||||
const metadata = this.findMetadata(target);
|
||||
if (!metadata) throw new EntityMetadataNotFoundError(target);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity.
|
||||
*/
|
||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||
return this.manager.getRepository(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity class or name.
|
||||
* Only tree-type entities can have a TreeRepository, like ones decorated with @Tree decorator.
|
||||
*/
|
||||
getTreeRepository<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
): TreeRepository<Entity> {
|
||||
return this.manager.getTreeRepository(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets custom entity repository marked with @EntityRepository decorator.
|
||||
*
|
||||
* @deprecated use Repository.extend function to create a custom repository
|
||||
*/
|
||||
getCustomRepository<T>(customRepository: ObjectType<T>): T {
|
||||
return this.manager.getCustomRepository(customRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) into a transaction.
|
||||
* All database operations must be executed using provided entity manager.
|
||||
*/
|
||||
async transaction<T>(runInTransaction: (entityManager: EntityManager) => Promise<T>): Promise<T>;
|
||||
async transaction<T>(
|
||||
isolationLevel: IsolationLevel,
|
||||
runInTransaction: (entityManager: EntityManager) => Promise<T>,
|
||||
): Promise<T>;
|
||||
async transaction<T>(
|
||||
isolationOrRunInTransaction: IsolationLevel | ((entityManager: EntityManager) => Promise<T>),
|
||||
runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
|
||||
): Promise<any> {
|
||||
return this.manager.transaction(
|
||||
isolationOrRunInTransaction as any,
|
||||
runInTransactionParam as any,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
async query<T = any>(query: string, parameters?: any[], queryRunner?: QueryRunner): Promise<T> {
|
||||
if (queryRunner && queryRunner.isReleased) throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
const usedQueryRunner = queryRunner || this.createQueryRunner();
|
||||
|
||||
try {
|
||||
return await usedQueryRunner.query(query, parameters); // await is needed here because we are using finally
|
||||
} finally {
|
||||
if (!queryRunner) await usedQueryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a SQL query.
|
||||
*/
|
||||
createQueryBuilder<Entity extends ObjectLiteral>(
|
||||
entityClass: EntityTarget<Entity>,
|
||||
alias: string,
|
||||
queryRunner?: QueryRunner,
|
||||
): SelectQueryBuilder<Entity>;
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a SQL query.
|
||||
*/
|
||||
createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>;
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a SQL query.
|
||||
*/
|
||||
createQueryBuilder<Entity extends ObjectLiteral>(
|
||||
entityOrRunner?: EntityTarget<Entity> | QueryRunner,
|
||||
alias?: string,
|
||||
queryRunner?: QueryRunner,
|
||||
): SelectQueryBuilder<Entity> {
|
||||
if (alias) {
|
||||
alias = DriverUtils.buildAlias(this.driver, undefined, alias);
|
||||
const metadata = this.getMetadata(entityOrRunner as EntityTarget<Entity>);
|
||||
return new SelectQueryBuilder(this, queryRunner).select(alias).from(metadata.target, alias);
|
||||
} else {
|
||||
return new SelectQueryBuilder(this, entityOrRunner as QueryRunner | undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query runner used for perform queries on a single database connection.
|
||||
* Using query runners you can control your queries to execute using single database connection and
|
||||
* manually control your database transaction.
|
||||
*
|
||||
* Mode is used in replication mode and indicates whatever you want to connect
|
||||
* to master database or any of slave databases.
|
||||
* If you perform writes you must use master database,
|
||||
* if you perform reads you can use slave databases.
|
||||
*/
|
||||
createQueryRunner(mode: ReplicationMode = 'master'): QueryRunner {
|
||||
const queryRunner = this.driver.createQueryRunner(mode);
|
||||
const manager = this.createEntityManager(queryRunner);
|
||||
Object.assign(queryRunner, { manager: manager });
|
||||
return queryRunner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity metadata of the junction table (many-to-many table).
|
||||
*/
|
||||
getManyToManyMetadata(entityTarget: EntityTarget<any>, relationPropertyPath: string) {
|
||||
const relationMetadata =
|
||||
this.getMetadata(entityTarget).findRelationWithPropertyPath(relationPropertyPath);
|
||||
if (!relationMetadata)
|
||||
throw new TypeORMError(
|
||||
`Relation "${relationPropertyPath}" was not found in ${entityTarget} entity.`,
|
||||
);
|
||||
if (!relationMetadata.isManyToMany)
|
||||
throw new TypeORMError(
|
||||
`Relation "${entityTarget}#${relationPropertyPath}" does not have a many-to-many relationship.` +
|
||||
`You can use this method only on many-to-many relations.`,
|
||||
);
|
||||
|
||||
return relationMetadata.junctionEntityMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Entity Manager for the current connection with the help of the EntityManagerFactory.
|
||||
*/
|
||||
createEntityManager(queryRunner?: QueryRunner): EntityManager {
|
||||
return new EntityManagerFactory().create(this, queryRunner);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Finds exist entity metadata by the given entity class, target name or table name.
|
||||
*/
|
||||
protected findMetadata(target: EntityTarget<any>): EntityMetadata | undefined {
|
||||
const metadataFromMap = this.entityMetadatasMap.get(target);
|
||||
if (metadataFromMap) return metadataFromMap;
|
||||
|
||||
for (let [_, metadata] of this.entityMetadatasMap) {
|
||||
if (InstanceChecker.isEntitySchema(target) && metadata.name === target.options.name) {
|
||||
return metadata;
|
||||
}
|
||||
if (typeof target === 'string') {
|
||||
if (target.indexOf('.') !== -1) {
|
||||
if (metadata.tablePath === target) {
|
||||
return metadata;
|
||||
}
|
||||
} else {
|
||||
if (metadata.name === target || metadata.tableName === target) {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ObjectUtils.isObjectWithName(target) && typeof target.name === 'string') {
|
||||
if (target.name.indexOf('.') !== -1) {
|
||||
if (metadata.tablePath === target.name) {
|
||||
return metadata;
|
||||
}
|
||||
} else {
|
||||
if (metadata.name === target.name || metadata.tableName === target.name) {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds metadatas for all registered classes inside this connection.
|
||||
*/
|
||||
protected async buildMetadatas(): Promise<void> {
|
||||
const connectionMetadataBuilder = new ConnectionMetadataBuilder(this);
|
||||
const entityMetadataValidator = new EntityMetadataValidator();
|
||||
|
||||
// create subscribers instances if they are not disallowed from high-level (for example they can disallowed from migrations run process)
|
||||
const flattenedSubscribers = ObjectUtils.mixedListToArray(this.options.subscribers || []);
|
||||
const subscribers = await connectionMetadataBuilder.buildSubscribers(flattenedSubscribers);
|
||||
ObjectUtils.assign(this, { subscribers: subscribers });
|
||||
|
||||
// build entity metadatas
|
||||
const flattenedEntities = ObjectUtils.mixedListToArray(this.options.entities || []);
|
||||
const entityMetadatas = await connectionMetadataBuilder.buildEntityMetadatas(flattenedEntities);
|
||||
ObjectUtils.assign(this, {
|
||||
entityMetadatas: entityMetadatas,
|
||||
entityMetadatasMap: new Map(entityMetadatas.map((metadata) => [metadata.target, metadata])),
|
||||
});
|
||||
|
||||
// create migration instances
|
||||
const flattenedMigrations = ObjectUtils.mixedListToArray(this.options.migrations || []);
|
||||
const migrations = await connectionMetadataBuilder.buildMigrations(flattenedMigrations);
|
||||
ObjectUtils.assign(this, { migrations: migrations });
|
||||
|
||||
// validate all created entity metadatas to make sure user created entities are valid and correct
|
||||
entityMetadataValidator.validateMany(
|
||||
this.entityMetadatas.filter((metadata) => metadata.tableType !== 'view'),
|
||||
this.driver,
|
||||
);
|
||||
|
||||
// set current data source to the entities
|
||||
for (let entityMetadata of entityMetadatas) {
|
||||
if (InstanceChecker.isBaseEntityConstructor(entityMetadata.target)) {
|
||||
entityMetadata.target.useDataSource(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the replication mode SELECT queries should use for this datasource by default
|
||||
*/
|
||||
defaultReplicationModeForReads(): ReplicationMode {
|
||||
if ('replication' in this.driver.options) {
|
||||
const value = (
|
||||
this.driver.options.replication as {
|
||||
defaultMode?: ReplicationMode;
|
||||
}
|
||||
).defaultMode;
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return 'slave';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { PostgresConnectionOptions } from '../driver/postgres/PostgresConnectionOptions';
|
||||
import { SqliteConnectionOptions } from '../driver/sqlite/SqliteConnectionOptions';
|
||||
import { SqlitePooledConnectionOptions } from '../driver/sqlite-pooled/SqlitePooledConnectionOptions';
|
||||
|
||||
/**
|
||||
* DataSourceOptions is an interface with settings and options for specific DataSource.
|
||||
*/
|
||||
export type DataSourceOptions =
|
||||
| PostgresConnectionOptions
|
||||
| SqliteConnectionOptions
|
||||
| SqlitePooledConnectionOptions;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './DataSource';
|
||||
export * from './DataSourceOptions';
|
||||
@@ -0,0 +1,40 @@
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { CheckMetadataArgs } from '../metadata-args/CheckMetadataArgs';
|
||||
import { TypeORMError } from '../error';
|
||||
|
||||
/**
|
||||
* Creates a database check.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create checks with composite columns when used on entity.
|
||||
*/
|
||||
export function Check(expression: string): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database check.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create checks with composite columns when used on entity.
|
||||
*/
|
||||
export function Check(name: string, expression: string): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database check.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create checks with composite columns when used on entity.
|
||||
*/
|
||||
export function Check(
|
||||
nameOrExpression: string,
|
||||
maybeExpression?: string,
|
||||
): ClassDecorator & PropertyDecorator {
|
||||
const name = maybeExpression ? nameOrExpression : undefined;
|
||||
const expression = maybeExpression ? maybeExpression : nameOrExpression;
|
||||
|
||||
if (!expression) throw new TypeORMError(`Check expression is required`);
|
||||
|
||||
return function (clsOrObject: Function | Object, propertyName?: string | symbol) {
|
||||
getMetadataArgsStorage().checks.push({
|
||||
target: propertyName ? clsOrObject.constructor : (clsOrObject as Function),
|
||||
name: name,
|
||||
expression: expression,
|
||||
} as CheckMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { EntityRepositoryMetadataArgs } from '../metadata-args/EntityRepositoryMetadataArgs';
|
||||
import { EntitySchema } from '../entity-schema/EntitySchema';
|
||||
|
||||
/**
|
||||
* Used to declare a class as a custom repository.
|
||||
* Custom repository can manage some specific entity or just be generic.
|
||||
* Custom repository optionally can extend AbstractRepository, Repository or TreeRepository.
|
||||
*
|
||||
* @deprecated use Repository.extend function to create a custom repository
|
||||
*/
|
||||
export function EntityRepository(entity?: Function | EntitySchema<any>): ClassDecorator {
|
||||
return function (target: Function) {
|
||||
getMetadataArgsStorage().entityRepositories.push({
|
||||
target: target,
|
||||
entity: entity,
|
||||
} as EntityRepositoryMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { ExclusionMetadataArgs } from '../metadata-args/ExclusionMetadataArgs';
|
||||
import { TypeORMError } from '../error';
|
||||
|
||||
/**
|
||||
* Creates a database exclusion.
|
||||
* Can be used on entity.
|
||||
* Can create exclusions with composite columns when used on entity.
|
||||
*/
|
||||
export function Exclusion(expression: string): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database exclusion.
|
||||
* Can be used on entity.
|
||||
* Can create exclusions with composite columns when used on entity.
|
||||
*/
|
||||
export function Exclusion(name: string, expression: string): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database exclusion.
|
||||
* Can be used on entity.
|
||||
* Can create exclusions with composite columns when used on entity.
|
||||
*/
|
||||
export function Exclusion(
|
||||
nameOrExpression: string,
|
||||
maybeExpression?: string,
|
||||
): ClassDecorator & PropertyDecorator {
|
||||
const name = maybeExpression ? nameOrExpression : undefined;
|
||||
const expression = maybeExpression ? maybeExpression : nameOrExpression;
|
||||
|
||||
if (!expression) throw new TypeORMError(`Exclusion expression is required`);
|
||||
|
||||
return function (clsOrObject: Function | Object, propertyName?: string | symbol) {
|
||||
getMetadataArgsStorage().exclusions.push({
|
||||
target: propertyName ? clsOrObject.constructor : (clsOrObject as Function),
|
||||
name: name,
|
||||
expression: expression,
|
||||
} as ExclusionMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { GeneratedMetadataArgs } from '../metadata-args/GeneratedMetadataArgs';
|
||||
|
||||
/**
|
||||
* Marks a column to generate a value on entity insertion.
|
||||
* There are three types of generation strategy - increment, uuid and rowid (cockroachdb only).
|
||||
* Increment uses a number which increases by one on each insertion.
|
||||
* Uuid generates a special UUID token.
|
||||
* Rowid supports only in CockroachDB and uses `unique_rowid()` function
|
||||
*
|
||||
* Note, some databases do not support non-primary generation columns.
|
||||
*/
|
||||
export function Generated(
|
||||
strategy: 'increment' | 'uuid' | 'rowid' = 'increment',
|
||||
): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().generations.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
strategy: strategy,
|
||||
} as GeneratedMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { IndexMetadataArgs } from '../metadata-args/IndexMetadataArgs';
|
||||
import { IndexOptions } from './options/IndexOptions';
|
||||
import { ObjectUtils } from '../util/ObjectUtils';
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(options?: IndexOptions): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(name: string, options?: IndexOptions): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(
|
||||
name: string,
|
||||
options: { synchronize: false },
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(
|
||||
name: string,
|
||||
fields: string[],
|
||||
options?: IndexOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(fields: string[], options?: IndexOptions): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(
|
||||
fields: (object?: any) => any[] | { [key: string]: number },
|
||||
options?: IndexOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(
|
||||
name: string,
|
||||
fields: (object?: any) => any[] | { [key: string]: number },
|
||||
options?: IndexOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Creates a database index.
|
||||
* Can be used on entity property or on entity.
|
||||
* Can create indices with composite columns when used on entity.
|
||||
*/
|
||||
export function Index(
|
||||
nameOrFieldsOrOptions?:
|
||||
| string
|
||||
| string[]
|
||||
| ((object: any) => any[] | { [key: string]: number })
|
||||
| IndexOptions,
|
||||
maybeFieldsOrOptions?:
|
||||
| ((object?: any) => any[] | { [key: string]: number })
|
||||
| IndexOptions
|
||||
| string[]
|
||||
| { synchronize: false },
|
||||
maybeOptions?: IndexOptions,
|
||||
): ClassDecorator & PropertyDecorator {
|
||||
// normalize parameters
|
||||
const name = typeof nameOrFieldsOrOptions === 'string' ? nameOrFieldsOrOptions : undefined;
|
||||
const fields =
|
||||
typeof nameOrFieldsOrOptions === 'string'
|
||||
? <((object?: any) => any[] | { [key: string]: number }) | string[]>maybeFieldsOrOptions
|
||||
: (nameOrFieldsOrOptions as string[]);
|
||||
let options =
|
||||
ObjectUtils.isObject(nameOrFieldsOrOptions) && !Array.isArray(nameOrFieldsOrOptions)
|
||||
? (nameOrFieldsOrOptions as IndexOptions)
|
||||
: maybeOptions;
|
||||
if (!options)
|
||||
options =
|
||||
ObjectUtils.isObject(maybeFieldsOrOptions) && !Array.isArray(maybeFieldsOrOptions)
|
||||
? (maybeFieldsOrOptions as IndexOptions)
|
||||
: maybeOptions;
|
||||
|
||||
return function (clsOrObject: Function | Object, propertyName?: string | symbol) {
|
||||
getMetadataArgsStorage().indices.push({
|
||||
target: propertyName ? clsOrObject.constructor : (clsOrObject as Function),
|
||||
name: name,
|
||||
columns: propertyName ? [propertyName] : fields,
|
||||
synchronize:
|
||||
options && (options as { synchronize: false }).synchronize === false ? false : true,
|
||||
where: options ? options.where : undefined,
|
||||
unique: options && options.unique ? true : false,
|
||||
spatial: options && options.spatial ? true : false,
|
||||
fulltext: options && options.fulltext ? true : false,
|
||||
nullFiltered: options && options.nullFiltered ? true : false,
|
||||
parser: options ? options.parser : undefined,
|
||||
sparse: options && options.sparse ? true : false,
|
||||
background: options && options.background ? true : false,
|
||||
concurrent: options && options.concurrent ? true : false,
|
||||
expireAfterSeconds: options ? options.expireAfterSeconds : undefined,
|
||||
} as IndexMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { getMetadataArgsStorage } from '../globals';
|
||||
import { UniqueMetadataArgs } from '../metadata-args/UniqueMetadataArgs';
|
||||
import { UniqueOptions } from './options/UniqueOptions';
|
||||
import { ObjectUtils } from '../util/ObjectUtils';
|
||||
|
||||
/**
|
||||
* Composite unique constraint must be set on entity classes and must specify entity's fields to be unique.
|
||||
*/
|
||||
export function Unique(
|
||||
name: string,
|
||||
fields: string[],
|
||||
options?: UniqueOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Composite unique constraint must be set on entity classes and must specify entity's fields to be unique.
|
||||
*/
|
||||
export function Unique(
|
||||
fields: string[],
|
||||
options?: UniqueOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Composite unique constraint must be set on entity classes and must specify entity's fields to be unique.
|
||||
*/
|
||||
export function Unique(
|
||||
fields: (object?: any) => any[] | { [key: string]: number },
|
||||
options?: UniqueOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Composite unique constraint must be set on entity classes and must specify entity's fields to be unique.
|
||||
*/
|
||||
export function Unique(
|
||||
name: string,
|
||||
fields: (object?: any) => any[] | { [key: string]: number },
|
||||
options?: UniqueOptions,
|
||||
): ClassDecorator & PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Composite unique constraint must be set on entity classes and must specify entity's fields to be unique.
|
||||
*/
|
||||
export function Unique(
|
||||
nameOrFieldsOrOptions?:
|
||||
| string
|
||||
| string[]
|
||||
| ((object: any) => any[] | { [key: string]: number })
|
||||
| UniqueOptions,
|
||||
maybeFieldsOrOptions?:
|
||||
| ((object?: any) => any[] | { [key: string]: number })
|
||||
| string[]
|
||||
| UniqueOptions,
|
||||
maybeOptions?: UniqueOptions,
|
||||
): ClassDecorator & PropertyDecorator {
|
||||
const name = typeof nameOrFieldsOrOptions === 'string' ? nameOrFieldsOrOptions : undefined;
|
||||
const fields =
|
||||
typeof nameOrFieldsOrOptions === 'string'
|
||||
? <((object?: any) => any[] | { [key: string]: number }) | string[]>maybeFieldsOrOptions
|
||||
: (nameOrFieldsOrOptions as string[]);
|
||||
let options =
|
||||
ObjectUtils.isObject(nameOrFieldsOrOptions) && !Array.isArray(nameOrFieldsOrOptions)
|
||||
? (nameOrFieldsOrOptions as UniqueOptions)
|
||||
: maybeOptions;
|
||||
if (!options)
|
||||
options =
|
||||
ObjectUtils.isObject(nameOrFieldsOrOptions) && !Array.isArray(maybeFieldsOrOptions)
|
||||
? (maybeFieldsOrOptions as UniqueOptions)
|
||||
: maybeOptions;
|
||||
|
||||
return function (clsOrObject: Function | Object, propertyName?: string | symbol) {
|
||||
let columns = fields;
|
||||
|
||||
if (propertyName !== undefined) {
|
||||
switch (typeof propertyName) {
|
||||
case 'string':
|
||||
columns = [propertyName];
|
||||
break;
|
||||
|
||||
case 'symbol':
|
||||
columns = [propertyName.toString()];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const args: UniqueMetadataArgs = {
|
||||
target: propertyName ? clsOrObject.constructor : (clsOrObject as Function),
|
||||
name: name,
|
||||
columns,
|
||||
deferrable: options ? options.deferrable : undefined,
|
||||
};
|
||||
getMetadataArgsStorage().uniques.push(args);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import {
|
||||
ColumnType,
|
||||
SimpleColumnType,
|
||||
SpatialColumnType,
|
||||
WithLengthColumnType,
|
||||
WithPrecisionColumnType,
|
||||
WithWidthColumnType,
|
||||
} from '../../driver/types/ColumnTypes';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ColumnCommonOptions } from '../options/ColumnCommonOptions';
|
||||
import { SpatialColumnOptions } from '../options/SpatialColumnOptions';
|
||||
import { ColumnWithLengthOptions } from '../options/ColumnWithLengthOptions';
|
||||
import { ColumnNumericOptions } from '../options/ColumnNumericOptions';
|
||||
import { ColumnEnumOptions } from '../options/ColumnEnumOptions';
|
||||
import { ColumnEmbeddedOptions } from '../options/ColumnEmbeddedOptions';
|
||||
import { EmbeddedMetadataArgs } from '../../metadata-args/EmbeddedMetadataArgs';
|
||||
import { ColumnTypeUndefinedError } from '../../error/ColumnTypeUndefinedError';
|
||||
import { ColumnHstoreOptions } from '../options/ColumnHstoreOptions';
|
||||
import { ColumnWithWidthOptions } from '../options/ColumnWithWidthOptions';
|
||||
import { GeneratedMetadataArgs } from '../../metadata-args/GeneratedMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column. Only properties decorated with this
|
||||
* decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(options: ColumnOptions): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(type: SimpleColumnType, options?: ColumnCommonOptions): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: SpatialColumnType,
|
||||
options?: ColumnCommonOptions & SpatialColumnOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: WithLengthColumnType,
|
||||
options?: ColumnCommonOptions & ColumnWithLengthOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: WithWidthColumnType,
|
||||
options?: ColumnCommonOptions & ColumnWithWidthOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: WithPrecisionColumnType,
|
||||
options?: ColumnCommonOptions & ColumnNumericOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: 'enum',
|
||||
options?: ColumnCommonOptions & ColumnEnumOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: 'simple-enum',
|
||||
options?: ColumnCommonOptions & ColumnEnumOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: 'set',
|
||||
options?: ColumnCommonOptions & ColumnEnumOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
type: 'hstore',
|
||||
options?: ColumnCommonOptions & ColumnHstoreOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*
|
||||
* Property in entity can be marked as Embedded, and on persist all columns from the embedded are mapped to the
|
||||
* single table of the entity where Embedded is used. And on hydration all columns which supposed to be in the
|
||||
* embedded will be mapped to it from the single table.
|
||||
*/
|
||||
export function Column(
|
||||
type: (type?: any) => Function,
|
||||
options?: ColumnEmbeddedOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
*/
|
||||
export function Column(
|
||||
typeOrOptions?: ((type?: any) => Function) | ColumnType | (ColumnOptions & ColumnEmbeddedOptions),
|
||||
options?: ColumnOptions & ColumnEmbeddedOptions,
|
||||
): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
// normalize parameters
|
||||
let type: ColumnType | undefined;
|
||||
if (typeof typeOrOptions === 'string' || typeof typeOrOptions === 'function') {
|
||||
type = <ColumnType>typeOrOptions;
|
||||
} else if (typeOrOptions) {
|
||||
options = <ColumnOptions>typeOrOptions;
|
||||
type = typeOrOptions.type;
|
||||
}
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
|
||||
// if type is not given explicitly then try to guess it
|
||||
const reflectMetadataType =
|
||||
Reflect && (Reflect as any).getMetadata
|
||||
? (Reflect as any).getMetadata('design:type', object, propertyName)
|
||||
: undefined;
|
||||
if (!type && reflectMetadataType)
|
||||
// if type is not given explicitly then try to guess it
|
||||
type = reflectMetadataType;
|
||||
|
||||
// check if there is no type in column options then set type from first function argument, or guessed one
|
||||
if (!options.type && type) options.type = type;
|
||||
|
||||
// specify HSTORE type if column is HSTORE
|
||||
if (options.type === 'hstore' && !options.hstoreType)
|
||||
options.hstoreType = reflectMetadataType === Object ? 'object' : 'string';
|
||||
|
||||
if (typeof typeOrOptions === 'function') {
|
||||
// register an embedded
|
||||
getMetadataArgsStorage().embeddeds.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
isArray: reflectMetadataType === Array || options.array === true,
|
||||
prefix: options.prefix !== undefined ? options.prefix : undefined,
|
||||
type: typeOrOptions as (type?: any) => Function,
|
||||
} as EmbeddedMetadataArgs);
|
||||
} else {
|
||||
// register a regular column
|
||||
|
||||
// if we still don't have a type then we need to give error to user that type is required
|
||||
if (!options.type) throw new ColumnTypeUndefinedError(object, propertyName);
|
||||
|
||||
// create unique
|
||||
if (options.unique === true)
|
||||
getMetadataArgsStorage().uniques.push({
|
||||
target: object.constructor,
|
||||
columns: [propertyName],
|
||||
});
|
||||
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'regular',
|
||||
options: options,
|
||||
} as ColumnMetadataArgs);
|
||||
|
||||
if (options.generated) {
|
||||
getMetadataArgsStorage().generations.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
strategy: typeof options.generated === 'string' ? options.generated : 'increment',
|
||||
} as GeneratedMetadataArgs);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* This column will store a creation date of the inserted object.
|
||||
* Creation date is generated and inserted only once,
|
||||
* at the first time when you create an object, the value is inserted into the table, and is never touched again.
|
||||
*/
|
||||
export function CreateDateColumn(options?: ColumnOptions): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'createDate',
|
||||
options: options || {},
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* This column will store a delete date of the soft-deleted object.
|
||||
* This date is being updated each time you soft-delete the object.
|
||||
*/
|
||||
export function DeleteDateColumn(options?: ColumnOptions): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'deleteDate',
|
||||
options: options || {},
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* Special type of column that is available only for MongoDB database.
|
||||
* Marks your entity's column to be an object id.
|
||||
*/
|
||||
export function ObjectIdColumn(options?: ColumnOptions): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
options.primary = true;
|
||||
if (!options.name) options.name = '_id';
|
||||
|
||||
// create and register a new column metadata
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'objectId',
|
||||
options: options,
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnTypeUndefinedError } from '../../error/ColumnTypeUndefinedError';
|
||||
import { PrimaryColumnCannotBeNullableError } from '../../error/PrimaryColumnCannotBeNullableError';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { GeneratedMetadataArgs } from '../../metadata-args/GeneratedMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
import { ColumnType } from '../../driver/types/ColumnTypes';
|
||||
|
||||
/**
|
||||
* Describes all primary key column's options.
|
||||
* If specified, the nullable field must be set to false.
|
||||
*/
|
||||
export type PrimaryColumnOptions = ColumnOptions & { nullable?: false };
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
* Primary columns also creates a PRIMARY KEY for this column in a db.
|
||||
*/
|
||||
export function PrimaryColumn(options?: PrimaryColumnOptions): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
* Primary columns also creates a PRIMARY KEY for this column in a db.
|
||||
*/
|
||||
export function PrimaryColumn(type?: ColumnType, options?: PrimaryColumnOptions): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
* Primary columns also creates a PRIMARY KEY for this column in a db.
|
||||
*/
|
||||
export function PrimaryColumn(
|
||||
typeOrOptions?: ColumnType | PrimaryColumnOptions,
|
||||
options?: PrimaryColumnOptions,
|
||||
): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
// normalize parameters
|
||||
let type: ColumnType | undefined;
|
||||
if (
|
||||
typeof typeOrOptions === 'string' ||
|
||||
typeOrOptions === String ||
|
||||
typeOrOptions === Boolean ||
|
||||
typeOrOptions === Number
|
||||
) {
|
||||
type = typeOrOptions as ColumnType;
|
||||
} else {
|
||||
options = Object.assign({}, <PrimaryColumnOptions>typeOrOptions);
|
||||
}
|
||||
if (!options) options = {} as PrimaryColumnOptions;
|
||||
|
||||
// if type is not given explicitly then try to guess it
|
||||
const reflectMetadataType =
|
||||
Reflect && (Reflect as any).getMetadata
|
||||
? (Reflect as any).getMetadata('design:type', object, propertyName)
|
||||
: undefined;
|
||||
if (!type && reflectMetadataType) type = reflectMetadataType;
|
||||
|
||||
// check if there is no type in column options then set type from first function argument, or guessed one
|
||||
if (!options.type && type) options.type = type;
|
||||
|
||||
// if we still don't have a type then we need to give error to user that type is required
|
||||
if (!options.type) throw new ColumnTypeUndefinedError(object, propertyName);
|
||||
|
||||
// check if column is not nullable, because we cannot allow a primary key to be nullable
|
||||
if (options.nullable) throw new PrimaryColumnCannotBeNullableError(object, propertyName);
|
||||
|
||||
// explicitly set a primary to column options
|
||||
options.primary = true;
|
||||
|
||||
// create and register a new column metadata
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'regular',
|
||||
options: options,
|
||||
} as ColumnMetadataArgs);
|
||||
|
||||
if (options.generated) {
|
||||
getMetadataArgsStorage().generations.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
strategy: typeof options.generated === 'string' ? options.generated : 'increment',
|
||||
} as GeneratedMetadataArgs);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { PrimaryGeneratedColumnNumericOptions } from '../options/PrimaryGeneratedColumnNumericOptions';
|
||||
import { PrimaryGeneratedColumnUUIDOptions } from '../options/PrimaryGeneratedColumnUUIDOptions';
|
||||
import { GeneratedMetadataArgs } from '../../metadata-args/GeneratedMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
import { PrimaryGeneratedColumnIdentityOptions } from '../options/PrimaryGeneratedColumnIdentityOptions';
|
||||
import { ObjectUtils } from '../../util/ObjectUtils';
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
*/
|
||||
export function PrimaryGeneratedColumn(): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
*/
|
||||
export function PrimaryGeneratedColumn(
|
||||
options: PrimaryGeneratedColumnNumericOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
*/
|
||||
export function PrimaryGeneratedColumn(
|
||||
strategy: 'increment',
|
||||
options?: PrimaryGeneratedColumnNumericOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
*/
|
||||
export function PrimaryGeneratedColumn(
|
||||
strategy: 'uuid',
|
||||
options?: PrimaryGeneratedColumnUUIDOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
*/
|
||||
export function PrimaryGeneratedColumn(
|
||||
strategy: 'rowid',
|
||||
options?: PrimaryGeneratedColumnUUIDOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
export function PrimaryGeneratedColumn(
|
||||
strategy: 'identity',
|
||||
options?: PrimaryGeneratedColumnIdentityOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
|
||||
* This column creates an integer PRIMARY COLUMN with generated set to true.
|
||||
*/
|
||||
export function PrimaryGeneratedColumn(
|
||||
strategyOrOptions?:
|
||||
| 'increment'
|
||||
| 'uuid'
|
||||
| 'rowid'
|
||||
| 'identity'
|
||||
| PrimaryGeneratedColumnNumericOptions
|
||||
| PrimaryGeneratedColumnUUIDOptions
|
||||
| PrimaryGeneratedColumnIdentityOptions,
|
||||
maybeOptions?:
|
||||
| PrimaryGeneratedColumnNumericOptions
|
||||
| PrimaryGeneratedColumnUUIDOptions
|
||||
| PrimaryGeneratedColumnIdentityOptions,
|
||||
): PropertyDecorator {
|
||||
// normalize parameters
|
||||
const options: ColumnOptions = {};
|
||||
let strategy: 'increment' | 'uuid' | 'rowid' | 'identity';
|
||||
if (strategyOrOptions) {
|
||||
if (typeof strategyOrOptions === 'string')
|
||||
strategy = strategyOrOptions as 'increment' | 'uuid' | 'rowid' | 'identity';
|
||||
|
||||
if (ObjectUtils.isObject(strategyOrOptions)) {
|
||||
strategy = 'increment';
|
||||
Object.assign(options, strategyOrOptions);
|
||||
}
|
||||
} else {
|
||||
strategy = 'increment';
|
||||
}
|
||||
if (ObjectUtils.isObject(maybeOptions)) Object.assign(options, maybeOptions);
|
||||
|
||||
return function (object: Object, propertyName: string) {
|
||||
// if column type is not explicitly set then determine it based on generation strategy
|
||||
if (!options.type) {
|
||||
if (strategy === 'increment' || strategy === 'identity') {
|
||||
options.type = Number;
|
||||
} else if (strategy === 'uuid') {
|
||||
options.type = 'uuid';
|
||||
} else if (strategy === 'rowid') {
|
||||
options.type = 'int';
|
||||
}
|
||||
}
|
||||
|
||||
// explicitly set a primary and generated to column options
|
||||
options.primary = true;
|
||||
|
||||
// register column metadata args
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'regular',
|
||||
options: options,
|
||||
});
|
||||
|
||||
// register generated metadata args
|
||||
getMetadataArgsStorage().generations.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
strategy: strategy,
|
||||
} as GeneratedMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* This column will store an update date of the updated object.
|
||||
* This date is being updated each time you persist the object.
|
||||
*/
|
||||
export function UpdateDateColumn(options?: ColumnOptions): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'updateDate',
|
||||
options: options ? options : {},
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* This column will store a number - version of the entity.
|
||||
* Every time your entity will be persisted, this number will be increased by one -
|
||||
* so you can organize visioning and update strategies of your entity.
|
||||
*/
|
||||
export function VersionColumn(options?: ColumnOptions): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'version',
|
||||
options: options || {},
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { ViewColumnOptions } from '../options/ViewColumnOptions';
|
||||
|
||||
/**
|
||||
* ViewColumn decorator is used to mark a specific class property as a view column.
|
||||
*/
|
||||
export function ViewColumn(options?: ViewColumnOptions): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'regular',
|
||||
options: options || {},
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { ColumnType } from '../../driver/types/ColumnTypes';
|
||||
import { ColumnTypeUndefinedError } from '../../error';
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { ColumnMetadataArgs } from '../../metadata-args/ColumnMetadataArgs';
|
||||
import { VirtualColumnOptions } from '../options/VirtualColumnOptions';
|
||||
/**
|
||||
* VirtualColumn decorator is used to mark a specific class property as a Virtual column.
|
||||
*/
|
||||
export function VirtualColumn(options: VirtualColumnOptions): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* VirtualColumn decorator is used to mark a specific class property as a Virtual column.
|
||||
*/
|
||||
export function VirtualColumn(
|
||||
typeOrOptions: ColumnType,
|
||||
options: VirtualColumnOptions,
|
||||
): PropertyDecorator;
|
||||
|
||||
/**
|
||||
* VirtualColumn decorator is used to mark a specific class property as a Virtual column.
|
||||
*/
|
||||
export function VirtualColumn(
|
||||
typeOrOptions?: ColumnType | VirtualColumnOptions,
|
||||
options?: VirtualColumnOptions,
|
||||
): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
// normalize parameters
|
||||
let type: ColumnType | undefined;
|
||||
if (typeof typeOrOptions === 'string') {
|
||||
type = <ColumnType>typeOrOptions;
|
||||
} else {
|
||||
options = <VirtualColumnOptions>typeOrOptions;
|
||||
type = options.type;
|
||||
}
|
||||
|
||||
if (!options?.query) {
|
||||
throw new Error('Column options must be defined for calculated columns.');
|
||||
}
|
||||
|
||||
// if type is not given explicitly then try to guess it
|
||||
const reflectMetadataType =
|
||||
Reflect && (Reflect as any).getMetadata
|
||||
? (Reflect as any).getMetadata('design:type', object, propertyName)
|
||||
: undefined;
|
||||
if (!type && reflectMetadataType)
|
||||
// if type is not given explicitly then try to guess it
|
||||
type = reflectMetadataType;
|
||||
|
||||
// check if there is no type in column options then set type from first function argument, or guessed one
|
||||
if (type) options.type = type;
|
||||
|
||||
// specify HSTORE type if column is HSTORE
|
||||
if (options.type === 'hstore' && !options.hstoreType)
|
||||
options.hstoreType = reflectMetadataType === Object ? 'object' : 'string';
|
||||
|
||||
// if we still don't have a type then we need to give error to user that type is required
|
||||
if (!options.type) throw new ColumnTypeUndefinedError(object, propertyName);
|
||||
|
||||
getMetadataArgsStorage().columns.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: 'virtual-property',
|
||||
options: options || {},
|
||||
} as ColumnMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { TableMetadataArgs } from '../../metadata-args/TableMetadataArgs';
|
||||
import { ViewEntityOptions } from '../options/ViewEntityOptions';
|
||||
import { ObjectUtils } from '../../util/ObjectUtils';
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be an entity view.
|
||||
* Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function ViewEntity(options?: ViewEntityOptions): ClassDecorator;
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be an entity view.
|
||||
* Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function ViewEntity(name?: string, options?: ViewEntityOptions): ClassDecorator;
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be an entity view.
|
||||
* Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function ViewEntity(
|
||||
nameOrOptions?: string | ViewEntityOptions,
|
||||
maybeOptions?: ViewEntityOptions,
|
||||
): ClassDecorator {
|
||||
const options =
|
||||
(ObjectUtils.isObject(nameOrOptions) ? (nameOrOptions as ViewEntityOptions) : maybeOptions) ||
|
||||
{};
|
||||
const name = typeof nameOrOptions === 'string' ? nameOrOptions : options.name;
|
||||
|
||||
return function (target: Function) {
|
||||
getMetadataArgsStorage().tables.push({
|
||||
target: target,
|
||||
name: name,
|
||||
expression: options.expression,
|
||||
dependsOn: options.dependsOn ? new Set(options.dependsOn) : undefined,
|
||||
type: 'view',
|
||||
database: options.database ? options.database : undefined,
|
||||
schema: options.schema ? options.schema : undefined,
|
||||
synchronize: options.synchronize === false ? false : true,
|
||||
materialized: !!options.materialized,
|
||||
} as TableMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { TableMetadataArgs } from '../../metadata-args/TableMetadataArgs';
|
||||
import { DiscriminatorValueMetadataArgs } from '../../metadata-args/DiscriminatorValueMetadataArgs';
|
||||
|
||||
/**
|
||||
* Special type of the table used in the single-table inherited tables.
|
||||
*/
|
||||
export function ChildEntity(discriminatorValue?: any): ClassDecorator {
|
||||
return function (target: Function) {
|
||||
// register a table metadata
|
||||
getMetadataArgsStorage().tables.push({
|
||||
target: target,
|
||||
type: 'entity-child',
|
||||
} as TableMetadataArgs);
|
||||
|
||||
// register discriminator value if it was provided
|
||||
if (typeof discriminatorValue !== 'undefined') {
|
||||
getMetadataArgsStorage().discriminatorValues.push({
|
||||
target: target,
|
||||
value: discriminatorValue,
|
||||
} as DiscriminatorValueMetadataArgs);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { TableMetadataArgs } from '../../metadata-args/TableMetadataArgs';
|
||||
import { EntityOptions } from '../options/EntityOptions';
|
||||
import { ObjectUtils } from '../../util/ObjectUtils';
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be an entity (table or document depend on database type).
|
||||
* Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function Entity(options?: EntityOptions): ClassDecorator;
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be an entity (table or document depend on database type).
|
||||
* Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function Entity(name?: string, options?: EntityOptions): ClassDecorator;
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be an entity (table or document depend on database type).
|
||||
* Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function Entity(
|
||||
nameOrOptions?: string | EntityOptions,
|
||||
maybeOptions?: EntityOptions,
|
||||
): ClassDecorator {
|
||||
const options =
|
||||
(ObjectUtils.isObject(nameOrOptions) ? (nameOrOptions as EntityOptions) : maybeOptions) || {};
|
||||
const name = typeof nameOrOptions === 'string' ? nameOrOptions : options.name;
|
||||
|
||||
return function (target) {
|
||||
getMetadataArgsStorage().tables.push({
|
||||
target: target,
|
||||
name: name,
|
||||
type: 'regular',
|
||||
orderBy: options.orderBy ? options.orderBy : undefined,
|
||||
engine: options.engine ? options.engine : undefined,
|
||||
database: options.database ? options.database : undefined,
|
||||
schema: options.schema ? options.schema : undefined,
|
||||
synchronize: options.synchronize,
|
||||
withoutRowid: options.withoutRowid,
|
||||
comment: options.comment ? options.comment : undefined,
|
||||
} as TableMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { InheritanceMetadataArgs } from '../../metadata-args/InheritanceMetadataArgs';
|
||||
import { ColumnOptions } from '../options/ColumnOptions';
|
||||
|
||||
/**
|
||||
* Sets for entity to use table inheritance pattern.
|
||||
*/
|
||||
export function TableInheritance(options?: {
|
||||
pattern?: 'STI' /*|"CTI"*/;
|
||||
column?: string | ColumnOptions;
|
||||
}): ClassDecorator {
|
||||
return function (target: Function) {
|
||||
getMetadataArgsStorage().inheritances.push({
|
||||
target: target,
|
||||
pattern: options && options.pattern ? options.pattern : 'STI',
|
||||
column:
|
||||
options && options.column
|
||||
? typeof options.column === 'string'
|
||||
? { name: options.column }
|
||||
: options.column
|
||||
: undefined,
|
||||
} as InheritanceMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after this entity insertion.
|
||||
*/
|
||||
export function AfterInsert(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_INSERT,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after entity is loaded.
|
||||
*/
|
||||
export function AfterLoad(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_LOAD,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity soft removal.
|
||||
*/
|
||||
export function AfterRecover(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_RECOVER,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after this entity removal.
|
||||
*/
|
||||
export function AfterRemove(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_REMOVE,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity soft removal.
|
||||
*/
|
||||
export function AfterSoftRemove(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_SOFT_REMOVE,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after this entity update.
|
||||
*/
|
||||
export function AfterUpdate(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_UPDATE,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity insertion.
|
||||
*/
|
||||
export function BeforeInsert(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_INSERT,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity soft removal.
|
||||
*/
|
||||
export function BeforeRecover(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_RECOVER,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity removal.
|
||||
*/
|
||||
export function BeforeRemove(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_REMOVE,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity soft removal.
|
||||
*/
|
||||
export function BeforeSoftRemove(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_SOFT_REMOVE,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EventListenerTypes } from '../../metadata/types/EventListenerTypes';
|
||||
import { EntityListenerMetadataArgs } from '../../metadata-args/EntityListenerMetadataArgs';
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity update.
|
||||
*/
|
||||
export function BeforeUpdate(): PropertyDecorator {
|
||||
return function (object: Object, propertyName: string) {
|
||||
getMetadataArgsStorage().entityListeners.push({
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_UPDATE,
|
||||
} as EntityListenerMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { getMetadataArgsStorage } from '../../globals';
|
||||
import { EntitySubscriberMetadataArgs } from '../../metadata-args/EntitySubscriberMetadataArgs';
|
||||
|
||||
/**
|
||||
* Classes decorated with this decorator will listen to ORM events and their methods will be triggered when event
|
||||
* occurs. Those classes must implement EventSubscriberInterface interface.
|
||||
*/
|
||||
export function EventSubscriber(): ClassDecorator {
|
||||
return function (target: Function) {
|
||||
getMetadataArgsStorage().entitySubscribers.push({
|
||||
target: target,
|
||||
} as EntitySubscriberMetadataArgs);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ValueTransformer } from './ValueTransformer';
|
||||
|
||||
/**
|
||||
* Column options specific to all column types.
|
||||
*/
|
||||
export interface ColumnCommonOptions {
|
||||
/**
|
||||
* Indicates if column is always selected by QueryBuilder and find operations.
|
||||
* Default value is "true".
|
||||
*/
|
||||
select?: boolean;
|
||||
|
||||
/**
|
||||
* Column name in the database.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Indicates if this column is a primary key.
|
||||
* Same can be achieved when @PrimaryColumn decorator is used.
|
||||
*/
|
||||
primary?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies if this column will use auto increment (sequence, generated identity, rowid).
|
||||
* Note that in some databases only one column in entity can be marked as generated, and it must be a primary column.
|
||||
*/
|
||||
generated?: boolean | 'increment' | 'uuid' | 'rowid' | 'identity';
|
||||
|
||||
/**
|
||||
* Specifies if column's value must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column's value can be set to NULL.
|
||||
*/
|
||||
nullable?: boolean;
|
||||
|
||||
/**
|
||||
* Default database value.
|
||||
* Note that default value is not supported when column type is 'json' of mysql.
|
||||
*/
|
||||
default?: any;
|
||||
|
||||
/**
|
||||
* ON UPDATE trigger. Works only for MySQL.
|
||||
*/
|
||||
onUpdate?: string;
|
||||
|
||||
/**
|
||||
* Column comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
|
||||
/**
|
||||
* Indicates if this column is an array.
|
||||
* Can be simply set to true or array length can be specified.
|
||||
* Supported only by postgres.
|
||||
*/
|
||||
array?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies a value transformer that is to be used to (un)marshal
|
||||
* this column when reading or writing to the database.
|
||||
*/
|
||||
transformer?: ValueTransformer | ValueTransformer[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Column options specific to embedded column.
|
||||
*/
|
||||
export interface ColumnEmbeddedOptions {
|
||||
/**
|
||||
* Embedded column prefix.
|
||||
* If set to empty string or false, then prefix is not set at all.
|
||||
*/
|
||||
prefix?: string | boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this embedded is in array mode.
|
||||
*
|
||||
* This option works only in mongodb.
|
||||
*/
|
||||
array?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Column options for enum-typed columns.
|
||||
*/
|
||||
export interface ColumnEnumOptions {
|
||||
/**
|
||||
* Array of possible enumerated values.
|
||||
*/
|
||||
enum?: any[] | Object;
|
||||
/**
|
||||
* Exact name of enum
|
||||
*/
|
||||
enumName?: string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Column options for enum-typed columns.
|
||||
*/
|
||||
export interface ColumnHstoreOptions {
|
||||
/**
|
||||
* Return type of HSTORE column.
|
||||
* Returns value as string or as object.
|
||||
*/
|
||||
hstoreType?: string;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Options for numeric column types where user can specify scale and precision.
|
||||
*/
|
||||
export interface ColumnNumericOptions {
|
||||
/**
|
||||
* The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
|
||||
* number of digits that are stored for the values.
|
||||
*/
|
||||
precision?: number;
|
||||
|
||||
/**
|
||||
* The scale for a decimal (exact numeric) column (applies only for decimal column), which represents the number
|
||||
* of digits to the right of the decimal point and must not be greater than precision.
|
||||
*/
|
||||
scale?: number;
|
||||
|
||||
/**
|
||||
* Puts ZEROFILL attribute on to numeric column. Works only for MySQL.
|
||||
* If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column
|
||||
*/
|
||||
zerofill?: boolean;
|
||||
|
||||
/**
|
||||
* Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
|
||||
*/
|
||||
unsigned?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import { ColumnType } from '../../driver/types/ColumnTypes';
|
||||
import { ValueTransformer } from './ValueTransformer';
|
||||
import { ColumnCommonOptions } from './ColumnCommonOptions';
|
||||
|
||||
/**
|
||||
* Describes all column's options.
|
||||
*/
|
||||
export interface ColumnOptions extends ColumnCommonOptions {
|
||||
/**
|
||||
* Column type. Must be one of the value from the ColumnTypes class.
|
||||
*/
|
||||
type?: ColumnType;
|
||||
|
||||
/**
|
||||
* Column name in the database.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Column type's length. Used only on some column types.
|
||||
* For example type = "string" and length = "100" means that ORM will create a column with type varchar(100).
|
||||
*/
|
||||
length?: string | number;
|
||||
|
||||
/**
|
||||
* Column type's display width. Used only on some column types in MySQL.
|
||||
* For example, INT(4) specifies an INT with a display width of four digits.
|
||||
*/
|
||||
width?: number;
|
||||
|
||||
/**
|
||||
* Indicates if column's value can be set to NULL.
|
||||
* Default value is "false".
|
||||
*/
|
||||
nullable?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column value is not updated by "save" operation.
|
||||
* It means you'll be able to write this value only when you first time insert the object.
|
||||
* Default value is "false".
|
||||
*
|
||||
* @deprecated Please use the `update` option instead. Careful, it takes
|
||||
* the opposite value to readonly.
|
||||
*
|
||||
*/
|
||||
readonly?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column value is updated by "save" operation.
|
||||
* If false, you'll be able to write this value only when you first time insert the object.
|
||||
* Default value is "true".
|
||||
*/
|
||||
update?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column is always selected by QueryBuilder and find operations.
|
||||
* Default value is "true".
|
||||
*/
|
||||
select?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column is inserted by default.
|
||||
* Default value is "true".
|
||||
*/
|
||||
insert?: boolean;
|
||||
|
||||
/**
|
||||
* Default database value.
|
||||
*/
|
||||
default?: any;
|
||||
|
||||
/**
|
||||
* ON UPDATE trigger. Works only for MySQL.
|
||||
*/
|
||||
onUpdate?: string;
|
||||
|
||||
/**
|
||||
* Indicates if this column is a primary key.
|
||||
* Same can be achieved when @PrimaryColumn decorator is used.
|
||||
*/
|
||||
primary?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies if column's value must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
|
||||
/**
|
||||
* Column comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
|
||||
/**
|
||||
* The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
|
||||
* number of digits that are stored for the values.
|
||||
*/
|
||||
precision?: number | null;
|
||||
|
||||
/**
|
||||
* The scale for a decimal (exact numeric) column (applies only for decimal column), which represents the number
|
||||
* of digits to the right of the decimal point and must not be greater than precision.
|
||||
*/
|
||||
scale?: number;
|
||||
|
||||
/**
|
||||
* Puts ZEROFILL attribute on to numeric column. Works only for MySQL.
|
||||
* If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to this column
|
||||
*/
|
||||
zerofill?: boolean;
|
||||
|
||||
/**
|
||||
* Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
|
||||
*/
|
||||
unsigned?: boolean;
|
||||
|
||||
/**
|
||||
* Defines a column character set.
|
||||
* Not supported by all database types.
|
||||
*/
|
||||
charset?: string;
|
||||
|
||||
/**
|
||||
* Defines a column collation.
|
||||
*/
|
||||
collation?: string;
|
||||
|
||||
/**
|
||||
* Array of possible enumerated values.
|
||||
*/
|
||||
enum?: (string | number)[] | Object;
|
||||
|
||||
/**
|
||||
* Exact name of enum
|
||||
*/
|
||||
enumName?: string;
|
||||
|
||||
/**
|
||||
* If this column is primary key then this specifies the name for it.
|
||||
*/
|
||||
primaryKeyConstraintName?: string;
|
||||
|
||||
/**
|
||||
* If this column is foreign key then this specifies the name for it.
|
||||
*/
|
||||
foreignKeyConstraintName?: string;
|
||||
|
||||
/**
|
||||
* Generated column expression.
|
||||
*/
|
||||
asExpression?: string;
|
||||
|
||||
/**
|
||||
* Generated column type.
|
||||
*/
|
||||
generatedType?: 'VIRTUAL' | 'STORED';
|
||||
|
||||
/**
|
||||
* Identity column type. Supports only in Postgres 10+.
|
||||
*/
|
||||
generatedIdentity?: 'ALWAYS' | 'BY DEFAULT';
|
||||
|
||||
/**
|
||||
* Return type of HSTORE column.
|
||||
* Returns value as string or as object.
|
||||
*/
|
||||
hstoreType?: 'object' | 'string';
|
||||
|
||||
/**
|
||||
* Indicates if this column is an array.
|
||||
* Can be simply set to true or array length can be specified.
|
||||
* Supported only by postgres.
|
||||
*/
|
||||
array?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies a value transformer that is to be used to (un)marshal
|
||||
* this column when reading or writing to the database.
|
||||
*/
|
||||
transformer?: ValueTransformer | ValueTransformer[];
|
||||
|
||||
/**
|
||||
* Spatial Feature Type (Geometry, Point, Polygon, etc.)
|
||||
*/
|
||||
spatialFeatureType?: string;
|
||||
|
||||
/**
|
||||
* SRID (Spatial Reference ID (EPSG code))
|
||||
*/
|
||||
srid?: number;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Options for columns that can define a length of the column type.
|
||||
*/
|
||||
export interface ColumnWithLengthOptions {
|
||||
/**
|
||||
* Column type's length.
|
||||
* For example type = "varchar" and length = "100" means ORM will create a column with type varchar(100).
|
||||
*/
|
||||
length?: string | number;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Options for columns that can define a length of the column type.
|
||||
*/
|
||||
export interface ColumnWithWidthOptions {
|
||||
/**
|
||||
* Column type's display width. Used only on some column types in MySQL.
|
||||
* For example, INT(4) specifies an INT with a display width of four digits.
|
||||
*/
|
||||
width?: number;
|
||||
|
||||
/**
|
||||
* Puts ZEROFILL attribute on to numeric column. Works only for MySQL.
|
||||
* If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to this column
|
||||
*/
|
||||
zerofill?: boolean;
|
||||
|
||||
/**
|
||||
* Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
|
||||
*/
|
||||
unsigned?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { OrderByCondition } from '../../find-options/OrderByCondition';
|
||||
|
||||
/**
|
||||
* Describes all entity's options.
|
||||
*/
|
||||
export interface EntityOptions {
|
||||
/**
|
||||
* Table name.
|
||||
* If not specified then naming strategy will generate table name from entity name.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Specifies a default order by used for queries from this table when no explicit order by is specified.
|
||||
*/
|
||||
orderBy?: OrderByCondition | ((object: any) => OrderByCondition | any);
|
||||
|
||||
/**
|
||||
* Table's database engine type (like "InnoDB", "MyISAM", etc).
|
||||
* It is used only during table creation.
|
||||
* If you update this value and table is already created, it will not change table's engine type.
|
||||
* Note that not all databases support this option.
|
||||
*/
|
||||
engine?: string;
|
||||
|
||||
/**
|
||||
* Database name. Used in Mysql and Sql Server.
|
||||
*/
|
||||
database?: string;
|
||||
|
||||
/**
|
||||
* Schema name. Used in Postgres and Sql Server.
|
||||
*/
|
||||
schema?: string;
|
||||
|
||||
/**
|
||||
* Indicates if schema synchronization is enabled or disabled for this entity.
|
||||
* If it will be set to false then schema sync will and migrations ignore this entity.
|
||||
* By default schema synchronization is enabled for all entities.
|
||||
*/
|
||||
synchronize?: boolean;
|
||||
|
||||
/**
|
||||
* If set to 'true' this option disables Sqlite's default behaviour of secretly creating
|
||||
* an integer primary key column named 'rowid' on table creation.
|
||||
* @see https://www.sqlite.org/withoutrowid.html.
|
||||
*/
|
||||
withoutRowid?: boolean;
|
||||
|
||||
/**
|
||||
* Table comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Describes all index options.
|
||||
*/
|
||||
export interface IndexOptions {
|
||||
/**
|
||||
* Indicates if this composite index must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
|
||||
/**
|
||||
* The SPATIAL modifier indexes the entire column and does not allow indexed columns to contain NULL values.
|
||||
* Works only in MySQL and PostgreSQL.
|
||||
*/
|
||||
spatial?: boolean;
|
||||
|
||||
/**
|
||||
* The FULLTEXT modifier indexes the entire column and does not allow prefixing.
|
||||
* Works only in MySQL.
|
||||
*/
|
||||
fulltext?: boolean;
|
||||
|
||||
/**
|
||||
* NULL_FILTERED indexes are particularly useful for indexing sparse columns, where most rows contain a NULL value.
|
||||
* In these cases, the NULL_FILTERED index can be considerably smaller and more efficient to maintain than
|
||||
* a normal index that includes NULL values.
|
||||
*
|
||||
* Works only in Spanner.
|
||||
*/
|
||||
nullFiltered?: boolean;
|
||||
|
||||
/**
|
||||
* Fulltext parser.
|
||||
* Works only in MySQL.
|
||||
*/
|
||||
parser?: string;
|
||||
|
||||
/**
|
||||
* Index filter condition.
|
||||
*/
|
||||
where?: string;
|
||||
|
||||
/**
|
||||
* If true, the index only references documents with the specified field.
|
||||
* These indexes use less space but behave differently in some situations (particularly sorts).
|
||||
* This option is only supported for mongodb database.
|
||||
*/
|
||||
sparse?: boolean;
|
||||
|
||||
/**
|
||||
* Builds the index in the background so that building an index an does not block other database activities.
|
||||
* This option is only supported for mongodb database.
|
||||
*/
|
||||
background?: boolean;
|
||||
|
||||
/**
|
||||
* Create the index using the CONCURRENTLY modifier
|
||||
* Works only in postgres.
|
||||
*/
|
||||
concurrent?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies a time to live, in seconds.
|
||||
* This option is only supported for mongodb database.
|
||||
*/
|
||||
expireAfterSeconds?: number;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Describes join column options.
|
||||
*/
|
||||
export interface JoinColumnOptions {
|
||||
/**
|
||||
* Name of the column.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Name of the column in the entity to which this column is referenced.
|
||||
*/
|
||||
referencedColumnName?: string; // TODO rename to referencedColumn
|
||||
|
||||
/**
|
||||
* Name of the foreign key constraint.
|
||||
*/
|
||||
foreignKeyConstraintName?: string;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { JoinColumnOptions } from './JoinColumnOptions';
|
||||
|
||||
/**
|
||||
* Describes all join table with multiple column options.
|
||||
*/
|
||||
export interface JoinTableMultipleColumnsOptions {
|
||||
/**
|
||||
* Name of the table that will be created to store values of the both tables (join table).
|
||||
* By default is auto generated.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* First column of the join table.
|
||||
*/
|
||||
joinColumns?: JoinColumnOptions[];
|
||||
|
||||
/**
|
||||
* Second (inverse) column of the join table.
|
||||
*/
|
||||
inverseJoinColumns?: JoinColumnOptions[];
|
||||
|
||||
/**
|
||||
* Database where join table will be created.
|
||||
* Works only in some databases (like mysql).
|
||||
*/
|
||||
database?: string;
|
||||
|
||||
/**
|
||||
* Schema where join table will be created.
|
||||
* Works only in some databases (like postgres).
|
||||
*/
|
||||
schema?: string;
|
||||
|
||||
/**
|
||||
* Indicates if schema synchronization is enabled or disabled junction table.
|
||||
* If it will be set to false then schema sync will and migrations ignores junction table.
|
||||
* By default schema synchronization is enabled.
|
||||
*/
|
||||
readonly synchronize?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { JoinColumnOptions } from './JoinColumnOptions';
|
||||
|
||||
/**
|
||||
* Describes join table options.
|
||||
*/
|
||||
export interface JoinTableOptions {
|
||||
/**
|
||||
* Name of the table that will be created to store values of the both tables (join table).
|
||||
* By default is auto generated.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* First column of the join table.
|
||||
*/
|
||||
joinColumn?: JoinColumnOptions;
|
||||
|
||||
/**
|
||||
* Second (inverse) column of the join table.
|
||||
*/
|
||||
inverseJoinColumn?: JoinColumnOptions;
|
||||
|
||||
/**
|
||||
* Database where join table will be created.
|
||||
* Works only in some databases (like mysql).
|
||||
*/
|
||||
database?: string;
|
||||
|
||||
/**
|
||||
* Schema where join table will be created.
|
||||
* Works only in some databases (like postgres).
|
||||
*/
|
||||
schema?: string;
|
||||
|
||||
/**
|
||||
* Indicates if schema synchronization is enabled or disabled junction table.
|
||||
* If it will be set to false then schema sync will and migrations ignores junction table.
|
||||
* By default schema synchronization is enabled.
|
||||
*/
|
||||
synchronize?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { PrimaryGeneratedColumnType } from '../../driver/types/ColumnTypes';
|
||||
|
||||
/**
|
||||
* Describes all options for PrimaryGeneratedColumn decorator with identity generation strategy.
|
||||
*/
|
||||
export interface PrimaryGeneratedColumnIdentityOptions {
|
||||
/**
|
||||
* Column type. Must be one of the value from the ColumnTypes class.
|
||||
*/
|
||||
type?: PrimaryGeneratedColumnType;
|
||||
|
||||
/**
|
||||
* Column name in the database.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Column comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
|
||||
/**
|
||||
* Identity column type. Supports only in Postgres 10+.
|
||||
*/
|
||||
generatedIdentity?: 'ALWAYS' | 'BY DEFAULT';
|
||||
|
||||
/**
|
||||
* Name of the primary key constraint.
|
||||
*/
|
||||
primaryKeyConstraintName?: string;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { PrimaryGeneratedColumnType } from '../../driver/types/ColumnTypes';
|
||||
|
||||
/**
|
||||
* Describes all options for PrimaryGeneratedColumn decorator with numeric generation strategy.
|
||||
*/
|
||||
export interface PrimaryGeneratedColumnNumericOptions {
|
||||
/**
|
||||
* Column type. Must be one of the value from the ColumnTypes class.
|
||||
*/
|
||||
type?: PrimaryGeneratedColumnType;
|
||||
|
||||
/**
|
||||
* Column name in the database.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Column comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
|
||||
/**
|
||||
* Puts ZEROFILL attribute on to numeric column. Works only for MySQL.
|
||||
* If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column
|
||||
*/
|
||||
zerofill?: boolean;
|
||||
|
||||
/**
|
||||
* Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
|
||||
*/
|
||||
unsigned?: boolean;
|
||||
|
||||
/**
|
||||
* Name of the primary key constraint.
|
||||
*/
|
||||
primaryKeyConstraintName?: string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Describes all options for PrimaryGeneratedColumn decorator with numeric uuid strategy.
|
||||
*/
|
||||
export interface PrimaryGeneratedColumnUUIDOptions {
|
||||
/**
|
||||
* Column name in the database.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Column comment. Not supported by all database types.
|
||||
*/
|
||||
comment?: string;
|
||||
|
||||
/**
|
||||
* Name of the primary key constraint.
|
||||
*/
|
||||
primaryKeyConstraintName?: string;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user