chore(utils): DML#hasOne allow mappedBy to not be defined (#10442)
RESOLVES FRMW-2826 **What** We have many places where we define only one to one on one side of the relation. In that case it is a one to one that does not mapped by to the other side relation since it is not defined. This pr allow to create a one to one without mapped by. In order to remove breaking changes, in that case we ask the user to explicitly define the `mappedBy` as `undefined`
This commit is contained in:
1 parent
223bcff379
commit
90d7f4ff39
4 files changed
+114
-12
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
chore(utils): DML#hasOne allow mappedBy to not be defined
|
||||||
@@ -2461,6 +2461,93 @@ describe("Entity builder", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("define custom mappedBy key to undefined to not get the auto generated value", () => {
|
||||||
|
const email = model.define("email", {
|
||||||
|
email: model.text(),
|
||||||
|
isVerified: model.boolean(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = model.define("user", {
|
||||||
|
id: model.number(),
|
||||||
|
username: model.text(),
|
||||||
|
email: model.hasOne(() => email, { mappedBy: undefined }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const User = toMikroORMEntity(user)
|
||||||
|
expectTypeOf(new User()).toMatchTypeOf<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
email: { email: string; isVerified: boolean }
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
|
expect(metaData.className).toEqual("User")
|
||||||
|
expect(metaData.path).toEqual("User")
|
||||||
|
expect(metaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
fieldName: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
columnType: "text",
|
||||||
|
name: "username",
|
||||||
|
fieldName: "username",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
reference: "1:1",
|
||||||
|
name: "email",
|
||||||
|
entity: "Email",
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
fieldName: "created_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "updated_at",
|
||||||
|
fieldName: "updated_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
onUpdate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
deleted_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "deleted_at",
|
||||||
|
fieldName: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test("define custom mappedBy key for relationship", () => {
|
test("define custom mappedBy key for relationship", () => {
|
||||||
const email = model.define("email", {
|
const email = model.define("email", {
|
||||||
email: model.text(),
|
email: model.text(),
|
||||||
@@ -2899,6 +2986,7 @@ describe("Entity builder", () => {
|
|||||||
username: model.text(),
|
username: model.text(),
|
||||||
email: model.hasOne(() => email, {
|
email: model.hasOne(() => email, {
|
||||||
foreignKey: true,
|
foreignKey: true,
|
||||||
|
mappedBy: undefined,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -3010,6 +3098,7 @@ describe("Entity builder", () => {
|
|||||||
emails: model
|
emails: model
|
||||||
.hasOne(() => email, {
|
.hasOne(() => email, {
|
||||||
foreignKey: true,
|
foreignKey: true,
|
||||||
|
mappedBy: undefined,
|
||||||
})
|
})
|
||||||
.nullable(),
|
.nullable(),
|
||||||
})
|
})
|
||||||
@@ -3265,6 +3354,7 @@ describe("Entity builder", () => {
|
|||||||
entity: "Email",
|
entity: "Email",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
cascade: ["persist", "soft-remove"],
|
cascade: ["persist", "soft-remove"],
|
||||||
|
mappedBy: "user",
|
||||||
},
|
},
|
||||||
email_id: {
|
email_id: {
|
||||||
columnType: "text",
|
columnType: "text",
|
||||||
@@ -3440,6 +3530,7 @@ describe("Entity builder", () => {
|
|||||||
entity: "Email",
|
entity: "Email",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
cascade: ["persist", "soft-remove"],
|
cascade: ["persist", "soft-remove"],
|
||||||
|
mappedBy: "user",
|
||||||
},
|
},
|
||||||
email_id: {
|
email_id: {
|
||||||
columnType: "text",
|
columnType: "text",
|
||||||
|
|||||||
@@ -11,17 +11,18 @@ import {
|
|||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
OneToOne,
|
OneToOne,
|
||||||
|
OneToOneOptions,
|
||||||
OnInit,
|
OnInit,
|
||||||
Property,
|
Property,
|
||||||
rel,
|
rel,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { DmlEntity } from "../../entity"
|
|
||||||
import { HasOne } from "../../relations/has-one"
|
|
||||||
import { HasMany } from "../../relations/has-many"
|
|
||||||
import { parseEntityName } from "./parse-entity-name"
|
|
||||||
import { camelToSnakeCase, pluralize } from "../../../common"
|
import { camelToSnakeCase, pluralize } from "../../../common"
|
||||||
import { applyEntityIndexes } from "../mikro-orm/apply-indexes"
|
import { DmlEntity } from "../../entity"
|
||||||
|
import { HasMany } from "../../relations/has-many"
|
||||||
|
import { HasOne } from "../../relations/has-one"
|
||||||
import { ManyToMany as DmlManyToMany } from "../../relations/many-to-many"
|
import { ManyToMany as DmlManyToMany } from "../../relations/many-to-many"
|
||||||
|
import { applyEntityIndexes } from "../mikro-orm/apply-indexes"
|
||||||
|
import { parseEntityName } from "./parse-entity-name"
|
||||||
import { HasOneWithForeignKey } from "../../relations/has-one-fk"
|
import { HasOneWithForeignKey } from "../../relations/has-one-fk"
|
||||||
|
|
||||||
type Context = {
|
type Context = {
|
||||||
@@ -141,14 +142,19 @@ export function defineHasOneRelationship(
|
|||||||
) {
|
) {
|
||||||
const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name)
|
const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name)
|
||||||
|
|
||||||
|
let mappedBy: string | undefined = camelToSnakeCase(MikroORMEntity.name)
|
||||||
|
if ("mappedBy" in relationship) {
|
||||||
|
mappedBy = relationship.mappedBy
|
||||||
|
}
|
||||||
|
|
||||||
OneToOne({
|
OneToOne({
|
||||||
entity: relatedModelName,
|
entity: relatedModelName,
|
||||||
nullable: relationship.nullable,
|
nullable: relationship.nullable,
|
||||||
mappedBy: relationship.mappedBy || camelToSnakeCase(MikroORMEntity.name),
|
...(mappedBy ? { mappedBy } : {}),
|
||||||
cascade: shouldRemoveRelated
|
cascade: shouldRemoveRelated
|
||||||
? (["persist", "soft-remove"] as any)
|
? (["persist", "soft-remove"] as any)
|
||||||
: undefined,
|
: undefined,
|
||||||
})(MikroORMEntity.prototype, relationship.name)
|
} as OneToOneOptions<any, any>)(MikroORMEntity.prototype, relationship.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,12 +169,10 @@ export function defineHasOneWithFKRelationship(
|
|||||||
) {
|
) {
|
||||||
const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`)
|
const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`)
|
||||||
const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name)
|
const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name)
|
||||||
let mappedBy: string | undefined
|
|
||||||
|
|
||||||
|
let mappedBy: string | undefined = camelToSnakeCase(MikroORMEntity.name)
|
||||||
if ("mappedBy" in relationship) {
|
if ("mappedBy" in relationship) {
|
||||||
mappedBy = relationship.mappedBy
|
mappedBy = relationship.mappedBy
|
||||||
} else {
|
|
||||||
mappedBy = camelToSnakeCase(MikroORMEntity.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OneToOne({
|
OneToOne({
|
||||||
@@ -178,7 +182,7 @@ export function defineHasOneWithFKRelationship(
|
|||||||
cascade: shouldRemoveRelated
|
cascade: shouldRemoveRelated
|
||||||
? (["persist", "soft-remove"] as any)
|
? (["persist", "soft-remove"] as any)
|
||||||
: undefined,
|
: undefined,
|
||||||
} as any)(MikroORMEntity.prototype, relationship.name)
|
} as OneToOneOptions<any, any>)(MikroORMEntity.prototype, relationship.name)
|
||||||
|
|
||||||
Property({
|
Property({
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ export abstract class BaseRelationship<T> implements RelationshipType<T> {
|
|||||||
return {
|
return {
|
||||||
name: relationshipName,
|
name: relationshipName,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
mappedBy: this.options.mappedBy,
|
...("mappedBy" in this.options
|
||||||
|
? { mappedBy: this.options.mappedBy }
|
||||||
|
: {}),
|
||||||
options: this.options,
|
options: this.options,
|
||||||
searchable: this.#searchable,
|
searchable: this.#searchable,
|
||||||
entity: this.#referencedEntity,
|
entity: this.#referencedEntity,
|
||||||
|
|||||||
Reference in new issue
Block a user