Skip to content

Commit

Permalink
feat: Add synchronize to @jointable (#9442)
Browse files Browse the repository at this point in the history
* feat: Add synchronize option to @jointable

Add synchronize option to @jointable. It allows to ignore JoinTable when syncing and generating migrations.

Closes #3443

* removed only

* lint fix

Co-authored-by: Bartlomiej Rutkowski <brutkowski@tilt.app>
Co-authored-by: Umed Khudoiberdiev <pleerock.me@gmail.com>
Co-authored-by: Alex Messer <dmzt08@gmail.com>
  • Loading branch information
4 people committed Dec 3, 2022
1 parent 598e269 commit 93e14a9
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 19 deletions.
2 changes: 2 additions & 0 deletions docs/decorator-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ Used for `many-to-many` relations and describes join columns of the "junction" t
Junction table is a special, separate table created automatically by TypeORM with columns referenced to the related entities.
You can change the name of the generated "junction" table, the column names inside the junction table, their referenced
columns with the `joinColumn`- and `inverseJoinColumn` attributes, and the created foreign keys names.
You can also set parameter `synchronize` to false to skip schema update(same way as in @Entity)

Example:

Expand All @@ -558,6 +559,7 @@ export class Post {
referencedColumnName: "id",
foreignKeyConstraintName: "fk_question_categories_categoryId"
},
synchronize: false,
})
categories: Category[]
}
Expand Down
7 changes: 7 additions & 0 deletions src/decorator/options/JoinTableMultipleColumnsOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,11 @@ export interface JoinTableMultipleColumnsOptions {
* Works only in some databases (like postgres and mssql).
*/
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
}
7 changes: 7 additions & 0 deletions src/decorator/options/JoinTableOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,11 @@ export interface JoinTableOptions {
* Works only in some databases (like postgres and mssql).
*/
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
}
1 change: 1 addition & 0 deletions src/decorator/relations/JoinTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function JoinTable(
schema: options && options.schema ? options.schema : undefined,
database:
options && options.database ? options.database : undefined,
synchronize: !(options && options.synchronize === false),
} as JoinTableMetadataArgs)
}
}
7 changes: 7 additions & 0 deletions src/metadata-args/JoinTableMetadataArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,11 @@ export interface JoinTableMetadataArgs {
* Works only in some databases (like postgres and mssql).
*/
readonly 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
}
1 change: 1 addition & 0 deletions src/metadata-builder/JunctionEntityMetadataBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class JunctionEntityMetadataBuilder {
database:
joinTable.database || relation.entityMetadata.database,
schema: joinTable.schema || relation.entityMetadata.schema,
synchronize: joinTable.synchronize,
},
})
entityMetadata.build()
Expand Down
11 changes: 11 additions & 0 deletions test/github-issues/3443/entity/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Entity, ManyToMany, PrimaryGeneratedColumn } from "../../../../src"
import { Product } from "./product"

@Entity({ name: "category" })
export class Category {
@PrimaryGeneratedColumn()
id: string

@ManyToMany(() => Product, (product) => product.categories)
products: Product[]
}
17 changes: 17 additions & 0 deletions test/github-issues/3443/entity/product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
Entity,
JoinTable,
ManyToMany,
PrimaryGeneratedColumn,
} from "../../../../src"
import { Category } from "./category"

@Entity({ name: "product" })
export class Product {
@PrimaryGeneratedColumn()
id: string

@ManyToMany(() => Category, (category) => category.products)
@JoinTable({ name: "product_category", synchronize: false })
categories: Category[]
}
47 changes: 47 additions & 0 deletions test/github-issues/3443/issue-3443.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import "reflect-metadata"
import {
createTestingConnections,
closeTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { expect } from "chai"

describe("github issues > #3443 @JoinTable on entities without synchronization", () => {
let dataSources: DataSource[]
before(
async () =>
(dataSources = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchema: true,
})),
)
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))

it("Should set synchronize: false for @JoinTable when passed to options", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const PRODUCT_TABLE_NAME = "product"
const CATEGORY_TABLE_NAME = "category"
const PRODUCT_CATEGORY_TABLE_NAME = "product_category"

expect(() =>
dataSource.getMetadata(PRODUCT_TABLE_NAME),
).not.to.throw()
expect(() =>
dataSource.getMetadata(CATEGORY_TABLE_NAME),
).not.to.throw()
expect(() =>
dataSource.getMetadata(PRODUCT_CATEGORY_TABLE_NAME),
).not.to.throw()
expect(
dataSource.getMetadata(PRODUCT_CATEGORY_TABLE_NAME)
.synchronize,
).to.equal(false)
}),
))

// you can add additional tests if needed
})
4 changes: 2 additions & 2 deletions test/github-issues/9412/entity/Post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export class Post {
@PrimaryGeneratedColumn()
id: number

@Column({unique: true})
@Column({ unique: true })
title: string

@Column({unique: true})
@Column({ unique: true })
author: string
}
40 changes: 23 additions & 17 deletions test/github-issues/9412/issue-9412.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,51 @@
import 'reflect-metadata'
import "reflect-metadata"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from '../../utils/test-utils'
import { DataSource } from '../../../src/data-source/DataSource'
import { Post } from './entity/Post'
import { expect } from 'chai'
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { Post } from "./entity/Post"
import { expect } from "chai"

describe('github issues > #9365 ', () => {
describe("github issues > #9365 ", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [ __dirname + '/entity/*{.js,.ts}' ],
enabledDrivers: [ 'postgres' ],
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["postgres"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))

it('should work with conflict path', () =>
it("should work with conflict path", () =>
Promise.all(
connections.map(async (connection) => {
const postRepository = connection.getRepository(Post)

const post1 = new Post()
post1.title = 'Test1'
post1.author = 'Test1'
post1.title = "Test1"
post1.author = "Test1"
await postRepository.save(post1)

const post2 = new Post()
post2.title = 'Test'
post2.author = 'Test2'
post2.title = "Test"
post2.author = "Test2"

await postRepository.upsert(post2, { conflictPaths: { author: true }, skipUpdateIfNoValuesChanged: true })
const allPostsAfterUpsert1= await postRepository.find()
await postRepository.upsert(post2, {
conflictPaths: { author: true },
skipUpdateIfNoValuesChanged: true,
})
const allPostsAfterUpsert1 = await postRepository.find()
expect(allPostsAfterUpsert1.length).equal(2)

await postRepository.upsert(post2, { conflictPaths: { title: true }, skipUpdateIfNoValuesChanged: true })
const allPostsAfterUpsert2= await postRepository.find()
await postRepository.upsert(post2, {
conflictPaths: { title: true },
skipUpdateIfNoValuesChanged: true,
})
const allPostsAfterUpsert2 = await postRepository.find()
expect(allPostsAfterUpsert2.length).equal(2)
}),
))
Expand Down

0 comments on commit 93e14a9

Please sign in to comment.