fix: do not rely on model loading order to find an implicit owner (#10360)
This commit is contained in:
1 parent
ef6dc53b87
commit
194361b95a
5 files changed
+131
-89
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: do not rely on model loading order to find an implicit owner
|
||||||
@@ -5149,9 +5149,9 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "teams",
|
name: "teams",
|
||||||
entity: "Team",
|
entity: "Team",
|
||||||
owner: true,
|
owner: false,
|
||||||
pivotTable: "team_users",
|
pivotTable: "team_users",
|
||||||
inversedBy: "users",
|
mappedBy: "users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -5217,9 +5217,9 @@ describe("Entity builder", () => {
|
|||||||
users: {
|
users: {
|
||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "users",
|
name: "users",
|
||||||
mappedBy: "teams",
|
inversedBy: "teams",
|
||||||
entity: "User",
|
entity: "User",
|
||||||
owner: false,
|
owner: true,
|
||||||
pivotTable: "team_users",
|
pivotTable: "team_users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
@@ -5330,9 +5330,9 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "teams",
|
name: "teams",
|
||||||
entity: "Team",
|
entity: "Team",
|
||||||
owner: true,
|
owner: false,
|
||||||
pivotTable: "team_users",
|
pivotTable: "team_users",
|
||||||
inversedBy: "users",
|
mappedBy: "users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -5399,8 +5399,8 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "users",
|
name: "users",
|
||||||
entity: "User",
|
entity: "User",
|
||||||
owner: false,
|
owner: true,
|
||||||
mappedBy: "teams",
|
inversedBy: "teams",
|
||||||
pivotTable: "team_users",
|
pivotTable: "team_users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
@@ -5545,9 +5545,9 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "teams",
|
name: "teams",
|
||||||
entity: "Team",
|
entity: "Team",
|
||||||
owner: true,
|
owner: false,
|
||||||
pivotTable: "team_users",
|
pivotTable: "team_users",
|
||||||
inversedBy: "users",
|
mappedBy: "users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -5614,9 +5614,9 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "users",
|
name: "users",
|
||||||
entity: "User",
|
entity: "User",
|
||||||
owner: false,
|
owner: true,
|
||||||
pivotTable: "team_users",
|
pivotTable: "team_users",
|
||||||
mappedBy: "teams",
|
inversedBy: "teams",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -6125,9 +6125,9 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "teams",
|
name: "teams",
|
||||||
entity: "Team",
|
entity: "Team",
|
||||||
owner: true,
|
owner: false,
|
||||||
pivotTable: "platform.team_users",
|
pivotTable: "platform.team_users",
|
||||||
inversedBy: "users",
|
mappedBy: "users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -6195,8 +6195,8 @@ describe("Entity builder", () => {
|
|||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "users",
|
name: "users",
|
||||||
entity: "User",
|
entity: "User",
|
||||||
owner: false,
|
owner: true,
|
||||||
mappedBy: "teams",
|
inversedBy: "teams",
|
||||||
pivotTable: "platform.team_users",
|
pivotTable: "platform.team_users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
@@ -6719,10 +6719,10 @@ describe("Entity builder", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
||||||
expect(metaData.className).toEqual("User")
|
expect(teamMetaData.className).toEqual("Team")
|
||||||
expect(metaData.path).toEqual("User")
|
expect(teamMetaData.path).toEqual("Team")
|
||||||
expect(metaData.properties).toEqual({
|
expect(teamMetaData.properties).toEqual({
|
||||||
id: {
|
id: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
type: "number",
|
type: "number",
|
||||||
@@ -6733,23 +6733,23 @@ describe("Entity builder", () => {
|
|||||||
getter: false,
|
getter: false,
|
||||||
setter: false,
|
setter: false,
|
||||||
},
|
},
|
||||||
username: {
|
name: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
type: "string",
|
type: "string",
|
||||||
columnType: "text",
|
columnType: "text",
|
||||||
name: "username",
|
name: "name",
|
||||||
fieldName: "username",
|
fieldName: "name",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
getter: false,
|
getter: false,
|
||||||
setter: false,
|
setter: false,
|
||||||
},
|
},
|
||||||
teams: {
|
users: {
|
||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "teams",
|
name: "users",
|
||||||
entity: "Team",
|
entity: "User",
|
||||||
owner: true,
|
owner: true,
|
||||||
|
inversedBy: "teams",
|
||||||
pivotEntity: "TeamUsers",
|
pivotEntity: "TeamUsers",
|
||||||
inversedBy: "users",
|
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -6788,10 +6788,10 @@ describe("Entity builder", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
expect(teamMetaData.className).toEqual("Team")
|
expect(metaData.className).toEqual("User")
|
||||||
expect(teamMetaData.path).toEqual("Team")
|
expect(metaData.path).toEqual("User")
|
||||||
expect(teamMetaData.properties).toEqual({
|
expect(metaData.properties).toEqual({
|
||||||
id: {
|
id: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
type: "number",
|
type: "number",
|
||||||
@@ -6802,23 +6802,23 @@ describe("Entity builder", () => {
|
|||||||
getter: false,
|
getter: false,
|
||||||
setter: false,
|
setter: false,
|
||||||
},
|
},
|
||||||
name: {
|
username: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
type: "string",
|
type: "string",
|
||||||
columnType: "text",
|
columnType: "text",
|
||||||
name: "name",
|
name: "username",
|
||||||
fieldName: "name",
|
fieldName: "username",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
getter: false,
|
getter: false,
|
||||||
setter: false,
|
setter: false,
|
||||||
},
|
},
|
||||||
users: {
|
teams: {
|
||||||
reference: "m:n",
|
reference: "m:n",
|
||||||
name: "users",
|
name: "teams",
|
||||||
entity: "User",
|
entity: "Team",
|
||||||
owner: false,
|
owner: false,
|
||||||
mappedBy: "teams",
|
|
||||||
pivotEntity: "TeamUsers",
|
pivotEntity: "TeamUsers",
|
||||||
|
mappedBy: "users",
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
reference: "scalar",
|
reference: "scalar",
|
||||||
@@ -6857,5 +6857,28 @@ describe("Entity builder", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("throw error when both sides of relationship defines the pivot table", () => {
|
||||||
|
const team = model.define("team", {
|
||||||
|
id: model.number(),
|
||||||
|
name: model.text(),
|
||||||
|
users: model.manyToMany(() => user, {
|
||||||
|
pivotTable: "user_teams",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = model.define("user", {
|
||||||
|
id: model.number(),
|
||||||
|
username: model.text(),
|
||||||
|
teams: model.manyToMany(() => team, {
|
||||||
|
pivotTable: "team_users",
|
||||||
|
mappedBy: "users",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(() => toMikroORMEntity(user)).toThrow(
|
||||||
|
`Invalid relationship reference for "User.teams". Define "pivotTable", "joinColumn", or "inverseJoinColumn" on only one side of the relationship`
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -15,13 +15,13 @@ import {
|
|||||||
Property,
|
Property,
|
||||||
rel,
|
rel,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { camelToSnakeCase, pluralize } from "../../../common"
|
|
||||||
import { DmlEntity } from "../../entity"
|
import { DmlEntity } from "../../entity"
|
||||||
import { HasMany } from "../../relations/has-many"
|
|
||||||
import { HasOne } from "../../relations/has-one"
|
import { HasOne } from "../../relations/has-one"
|
||||||
import { ManyToMany as DmlManyToMany } from "../../relations/many-to-many"
|
import { HasMany } from "../../relations/has-many"
|
||||||
import { applyEntityIndexes } from "../mikro-orm/apply-indexes"
|
|
||||||
import { parseEntityName } from "./parse-entity-name"
|
import { parseEntityName } from "./parse-entity-name"
|
||||||
|
import { camelToSnakeCase, pluralize } from "../../../common"
|
||||||
|
import { applyEntityIndexes } from "../mikro-orm/apply-indexes"
|
||||||
|
import { ManyToMany as DmlManyToMany } from "../../relations/many-to-many"
|
||||||
|
|
||||||
type Context = {
|
type Context = {
|
||||||
MANY_TO_MANY_TRACKED_RELATIONS: Record<string, boolean>
|
MANY_TO_MANY_TRACKED_RELATIONS: Record<string, boolean>
|
||||||
@@ -375,12 +375,10 @@ export function defineManyToManyRelationship(
|
|||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
relatedModelName,
|
relatedModelName,
|
||||||
relatedTableName,
|
|
||||||
pgSchema,
|
pgSchema,
|
||||||
}: {
|
}: {
|
||||||
relatedModelName: string
|
relatedModelName: string
|
||||||
pgSchema: string | undefined
|
pgSchema: string | undefined
|
||||||
relatedTableName: string
|
|
||||||
},
|
},
|
||||||
{ MANY_TO_MANY_TRACKED_RELATIONS }: Context
|
{ MANY_TO_MANY_TRACKED_RELATIONS }: Context
|
||||||
) {
|
) {
|
||||||
@@ -450,11 +448,12 @@ export function defineManyToManyRelationship(
|
|||||||
pivotEntityName = parseEntityName(pivotEntity).modelName
|
pivotEntityName = parseEntityName(pivotEntity).modelName
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pivotEntityName) {
|
const tableName = parseEntityName(entity).tableNameWithoutSchema
|
||||||
const { tableName } = parseEntityName(entity)
|
const relatedTableName = parseEntityName(relatedEntity).tableNameWithoutSchema
|
||||||
let tableNameWithoutSchema: string
|
const sortedTableNames = [tableName, relatedTableName].sort()
|
||||||
let relatedTableNameWithoutSchema: string
|
const otherSideRelationOptions = otherSideRelationship.parse("").options
|
||||||
|
|
||||||
|
if (!pivotEntityName) {
|
||||||
/**
|
/**
|
||||||
* Pivot table name is created as follows (when not explicitly provided)
|
* Pivot table name is created as follows (when not explicitly provided)
|
||||||
*
|
*
|
||||||
@@ -464,25 +463,10 @@ export function defineManyToManyRelationship(
|
|||||||
* - And finally pluralizing the second entity name.
|
* - And finally pluralizing the second entity name.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let [schema, ...tableTokens] = tableName.split(".")
|
|
||||||
if (!tableTokens.length) {
|
|
||||||
tableNameWithoutSchema = schema
|
|
||||||
} else {
|
|
||||||
tableNameWithoutSchema = tableTokens.join(".")
|
|
||||||
}
|
|
||||||
|
|
||||||
const [relatedSchema, ...relatedTableTokens] = relatedTableName.split(".")
|
|
||||||
if (!relatedTableTokens.length) {
|
|
||||||
relatedTableNameWithoutSchema = relatedSchema
|
|
||||||
} else {
|
|
||||||
relatedTableNameWithoutSchema = relatedTableTokens.join(".")
|
|
||||||
}
|
|
||||||
|
|
||||||
pivotTableName =
|
pivotTableName =
|
||||||
relationship.options.pivotTable ??
|
relationship.options.pivotTable ??
|
||||||
otherSideRelationship.parse("").options.pivotTable ??
|
otherSideRelationship.parse("").options.pivotTable ??
|
||||||
[tableNameWithoutSchema, relatedTableNameWithoutSchema]
|
sortedTableNames
|
||||||
.sort()
|
|
||||||
.map((token, index) => {
|
.map((token, index) => {
|
||||||
if (index === 1) {
|
if (index === 1) {
|
||||||
return pluralize(token)
|
return pluralize(token)
|
||||||
@@ -492,22 +476,51 @@ export function defineManyToManyRelationship(
|
|||||||
.join("_")
|
.join("_")
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherSideRelationOptions = otherSideRelationship.parse("").options
|
let isOwner: boolean | undefined = undefined
|
||||||
|
|
||||||
const isOwner =
|
const configuresRelationship = !!(
|
||||||
!!joinColumn ||
|
joinColumn ||
|
||||||
!!inverseJoinColumn ||
|
inverseJoinColumn ||
|
||||||
!!relationship.options.pivotTable ||
|
relationship.options.pivotTable
|
||||||
/**
|
)
|
||||||
* We can't infer it from the current entity so lets
|
const relatedOneConfiguresRelationship = !!(
|
||||||
* look at the otherside configuration as well to make a choice
|
otherSideRelationOptions.pivotTable ||
|
||||||
*/
|
otherSideRelationOptions.joinColumn ||
|
||||||
(!otherSideRelationOptions.pivotTable &&
|
otherSideRelationOptions.inverseJoinColumn
|
||||||
!otherSideRelationOptions.joinColumn &&
|
)
|
||||||
!otherSideRelationOptions.inverseJoinColumn &&
|
|
||||||
!MANY_TO_MANY_TRACKED_RELATIONS[
|
/**
|
||||||
`${relatedModelName}.${otherSideRelationshipProperty}`
|
* Both sides are configuring the properties that must be on one
|
||||||
])
|
* side only
|
||||||
|
*/
|
||||||
|
if (configuresRelationship && relatedOneConfiguresRelationship) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid relationship reference for "${MikroORMEntity.name}.${relationship.name}". Define "pivotTable", "joinColumn", or "inverseJoinColumn" on only one side of the relationship`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If any of the following properties are provided, we consider
|
||||||
|
* the current side to be the owner
|
||||||
|
*/
|
||||||
|
if (configuresRelationship) {
|
||||||
|
isOwner = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If any of the properties are provided on the other side,
|
||||||
|
* then we do not expect the current side to be the owner
|
||||||
|
*/
|
||||||
|
if (isOwner === undefined && relatedOneConfiguresRelationship) {
|
||||||
|
isOwner = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finally, we consider the current side as owner, if it is
|
||||||
|
* the first one in alphabetical order. The same logic is
|
||||||
|
* applied to pivot table name as well.
|
||||||
|
*/
|
||||||
|
isOwner ??= sortedTableNames[0] === tableName
|
||||||
|
|
||||||
const mappedByProp = isOwner ? "inversedBy" : "mappedBy"
|
const mappedByProp = isOwner ? "inversedBy" : "mappedBy"
|
||||||
const mappedByPropValue =
|
const mappedByPropValue =
|
||||||
|
|||||||
@@ -25,5 +25,6 @@ export function parseEntityName(entity: DmlEntity<any, any>) {
|
|||||||
tableName,
|
tableName,
|
||||||
modelName: upperCaseFirst(toCamelCase(parsedEntity.name)),
|
modelName: upperCaseFirst(toCamelCase(parsedEntity.name)),
|
||||||
pgSchema: rest.length ? pgSchema : undefined,
|
pgSchema: rest.length ? pgSchema : undefined,
|
||||||
|
tableNameWithoutSchema: rest.length ? rest.join(".") : pgSchema,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,13 +217,13 @@ describe("manyToMany - manyToMany", () => {
|
|||||||
;[User, Team] = toMikroOrmEntities([user, team])
|
;[User, Team] = toMikroOrmEntities([user, team])
|
||||||
|
|
||||||
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
||||||
expect((teamMetaData.properties as any).users.mappedBy).toBe("squads")
|
expect((teamMetaData.properties as any).users.inversedBy).toBe("squads")
|
||||||
expect((teamMetaData.properties as any).users.owner).toBe(false)
|
expect((teamMetaData.properties as any).users.owner).toBe(true)
|
||||||
|
|
||||||
const userMetaData = MetadataStorage.getMetadataFromDecorator(User)
|
const userMetaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
expect((userMetaData.properties as any).squads.mappedBy).not.toBeDefined()
|
expect((userMetaData.properties as any).squads.inversedBy).not.toBeDefined()
|
||||||
expect((userMetaData.properties as any).squads.inversedBy).toBe("users")
|
expect((userMetaData.properties as any).squads.mappedBy).toBe("users")
|
||||||
expect((userMetaData.properties as any).squads.owner).toBe(true)
|
expect((userMetaData.properties as any).squads.owner).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`should load the dml's correclty when both side of the relation are specifying the mappedBy options without pivot table`, () => {
|
it(`should load the dml's correclty when both side of the relation are specifying the mappedBy options without pivot table`, () => {
|
||||||
@@ -248,14 +248,14 @@ describe("manyToMany - manyToMany", () => {
|
|||||||
let [User, Team] = toMikroOrmEntities([user, team])
|
let [User, Team] = toMikroOrmEntities([user, team])
|
||||||
|
|
||||||
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
||||||
expect((teamMetaData.properties as any).users.mappedBy).toBe("squads")
|
expect((teamMetaData.properties as any).users.inversedBy).toBe("squads")
|
||||||
expect((teamMetaData.properties as any).users.owner).toBe(false)
|
expect((teamMetaData.properties as any).users.owner).toBe(true)
|
||||||
expect((teamMetaData.properties as any).users.pivotTable).toBe("team_users")
|
expect((teamMetaData.properties as any).users.pivotTable).toBe("team_users")
|
||||||
|
|
||||||
const userMetaData = MetadataStorage.getMetadataFromDecorator(User)
|
const userMetaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
expect((userMetaData.properties as any).squads.mappedBy).not.toBeDefined()
|
expect((userMetaData.properties as any).squads.inversedBy).not.toBeDefined()
|
||||||
expect((userMetaData.properties as any).squads.inversedBy).toBe("users")
|
expect((userMetaData.properties as any).squads.mappedBy).toBe("users")
|
||||||
expect((userMetaData.properties as any).squads.owner).toBe(true)
|
expect((userMetaData.properties as any).squads.owner).toBe(false)
|
||||||
expect((userMetaData.properties as any).squads.pivotTable).toBe(
|
expect((userMetaData.properties as any).squads.pivotTable).toBe(
|
||||||
"team_users"
|
"team_users"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in new issue
Block a user