Skip to content

Commit

Permalink
fix(core): respect @Index and @Unique decorators on embeddables (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
B4nan committed Nov 5, 2023
1 parent 82c8629 commit c3d7717
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 4 deletions.
18 changes: 18 additions & 0 deletions packages/core/src/metadata/MetadataDiscovery.ts
Expand Up @@ -922,6 +922,24 @@ export class MetadataDiscovery {

this.initEmbeddables(meta, meta.properties[name], visited);
}

for (const index of embeddable.indexes) {
meta.indexes.push({
...index,
properties: Utils.asArray(index.properties).map(p => {
return embeddedProp.embeddedProps[p].name;
}),
});
}

for (const unique of embeddable.uniques) {
meta.uniques.push({
...unique,
properties: Utils.asArray(unique.properties).map(p => {
return embeddedProp.embeddedProps[p].name;
}),
});
}
}

private initSingleTableInheritance(meta: EntityMetadata, metadata: EntityMetadata[]): void {
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/metadata/MetadataValidator.ts
Expand Up @@ -203,8 +203,7 @@ export class MetadataValidator {
private validateIndexes(meta: EntityMetadata, indexes: { properties: string | string[] }[], type: 'index' | 'unique'): void {
for (const index of indexes) {
for (const prop of Utils.asArray(index.properties)) {
// if (!(prop in meta.properties)) {
if (!meta.props.some(p => prop === p.name || prop.startsWith(p.name + '.'))) {
if (!meta.properties[prop] && !meta.props.some(p => prop.startsWith(p.name + '.'))) {
throw MetadataError.unknownIndexProperty(meta, prop, type);
}
}
Expand Down
Expand Up @@ -123,11 +123,38 @@ exports[`embedded entities in postgres diffing 1`] = `

exports[`embedded entities in postgres schema: nested embeddables 1 1`] = `
"create table "user" ("id" serial primary key, "name" varchar(255) not null, "profile1_username" varchar(255) not null, "profile1_identity_email" varchar(255) not null, "profile1_identity_meta_foo" varchar(255) null, "profile1_identity_links" jsonb not null, "profile1_identity_meta_bar" varchar(255) null, "profile2" jsonb not null);
create index "user_profile1_identity_meta_bar_index" on "user" ("profile1_identity_meta_bar");
create index "user_profile1_identity_links_meta_bar_index" on "user" (("profile1_identity_links"->'meta'->>'bar'));
create index "user_profile1_identity_links_metas_bar_index" on "user" (("profile1_identity_links"->'metas'->>'bar'));
create index "user_profile2_identity_meta_bar_index" on "user" (("profile2"->'identity'->'meta'->>'bar'));
create index "user_profile2_identity_links_meta_bar_index" on "user" (("profile2"->'identity'->'links'->'meta'->>'bar'));
create index "user_profile2_identity_links_metas_bar_index" on "user" (("profile2"->'identity'->'links'->'metas'->>'bar'));
alter table "user" add constraint "user_profile1_identity_email_unique" unique ("profile1_identity_email");
alter table "user" add constraint "user_profile1_username_unique" unique ("profile1_username");
create unique index "user_profile2_identity_email_unique" on "user" (("profile2"->'identity'->>'email'));
create unique index "user_profile2_username_unique" on "user" (("profile2"->>'username'));
"
`;

exports[`embedded entities in postgres schema: nested embeddables 2 1`] = `""`;
exports[`embedded entities in postgres schema: nested embeddables 2 1`] = `
"drop index "user_profile1_identity_links_meta_bar_index";
drop index "user_profile1_identity_links_metas_bar_index";
drop index "user_profile2_identity_email_unique";
drop index "user_profile2_identity_links_meta_bar_index";
drop index "user_profile2_identity_links_metas_bar_index";
drop index "user_profile2_identity_meta_bar_index";
drop index "user_profile2_username_unique";
create index "user_profile1_identity_links_meta_bar_index" on "user" (("profile1_identity_links"->'meta'->>'bar'));
create index "user_profile1_identity_links_metas_bar_index" on "user" (("profile1_identity_links"->'metas'->>'bar'));
create unique index "user_profile2_identity_email_unique" on "user" (("profile2"->'identity'->>'email'));
create index "user_profile2_identity_links_meta_bar_index" on "user" (("profile2"->'identity'->'links'->'meta'->>'bar'));
create index "user_profile2_identity_links_metas_bar_index" on "user" (("profile2"->'identity'->'links'->'metas'->>'bar'));
create index "user_profile2_identity_meta_bar_index" on "user" (("profile2"->'identity'->'meta'->>'bar'));
create unique index "user_profile2_username_unique" on "user" (("profile2"->>'username'));
"
`;

exports[`embedded entities in postgres schema: nested embeddables 3 1`] = `
"drop table if exists "user" cascade;
Expand Down
23 changes: 22 additions & 1 deletion tests/features/embeddables/nested-embeddables.postgres.test.ts
@@ -1,4 +1,4 @@
import { Embeddable, Embedded, Entity, PrimaryKey, Property, wrap } from '@mikro-orm/core';
import { Embeddable, Embedded, Entity, Index, PrimaryKey, Property, Unique, wrap } from '@mikro-orm/core';
import { MikroORM, PostgreSqlDriver } from '@mikro-orm/postgresql';
import { mockLogger } from '../../helpers';

Expand All @@ -9,6 +9,7 @@ class IdentityMeta {
foo?: string;

@Property()
@Index()
bar?: string;

constructor(foo?: string, bar?: string) {
Expand Down Expand Up @@ -47,6 +48,7 @@ class IdentityLink {
class Identity {

@Property()
@Unique()
email: string;

@Embedded(() => IdentityMeta, { nullable: true })
Expand All @@ -65,6 +67,7 @@ class Identity {
@Embeddable()
class Profile {

@Unique()
@Property()
username: string;

Expand Down Expand Up @@ -108,6 +111,10 @@ describe('embedded entities in postgres', () => {
await orm.schema.refreshDatabase();
});

beforeEach(async () => {
await orm.schema.clearDatabase();
});

afterAll(async () => {
await orm.close(true);
});
Expand Down Expand Up @@ -413,4 +420,18 @@ describe('embedded entities in postgres', () => {
expect(mock.mock.calls[3][0]).toMatch(`select "u0"."id", "u0"."profile2" from "user" as "u0" where "u0"."profile2"->'identity'->'links'->>'url' = $1 limit $2`);
});

test('unique constraints', async () => {
const user1 = new User();
user1.name = 'Uwe';
user1.profile1 = new Profile('u1', new Identity('e1', new IdentityMeta('f1', 'b1')));
user1.profile2 = new Profile('u2', new Identity('e2', new IdentityMeta('f2', 'b2')));

const user2 = new User();
user2.name = 'Uschi';
user2.profile1 = new Profile('u1', new Identity('e3'));
user2.profile2 = new Profile('u4', new Identity('e4', new IdentityMeta('f4')));

await expect(orm.em.persistAndFlush([user1, user2])).rejects.toThrowError(/duplicate key value violates unique constraint "user_profile1_username_unique"/);
});

});

0 comments on commit c3d7717

Please sign in to comment.