• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.67
/src/metadata/RelationMetadata.ts
1
import { RelationType } from "./types/RelationTypes"
2
import { EntityMetadata } from "./EntityMetadata"
1✔
3
import { ForeignKeyMetadata } from "./ForeignKeyMetadata"
4
import { ObjectLiteral } from "../common/ObjectLiteral"
5
import { ColumnMetadata } from "./ColumnMetadata"
6
import { EmbeddedMetadata } from "./EmbeddedMetadata"
7
import { RelationMetadataArgs } from "../metadata-args/RelationMetadataArgs"
8
import { DeferrableType } from "./types/DeferrableType"
9
import { OnUpdateType } from "./types/OnUpdateType"
10
import { OnDeleteType } from "./types/OnDeleteType"
11
import { PropertyTypeFactory } from "./types/PropertyTypeInFunction"
12
import { TypeORMError } from "../error"
1✔
13
import { ObjectUtils } from "../util/ObjectUtils"
1✔
14
import { InstanceChecker } from "../util/InstanceChecker"
1✔
15

16
/**
17
 * Contains all information about some entity's relation.
18
 */
19
export class RelationMetadata {
1✔
20
    // ---------------------------------------------------------------------
21
    // Public Properties
22
    // ---------------------------------------------------------------------
23

24
    /**
25
     * Entity metadata of the entity where this relation is placed.
26
     *
27
     * For example for @ManyToMany(type => Category) in Post, entityMetadata will be metadata of Post entity.
28
     */
29
    entityMetadata: EntityMetadata
30

31
    /**
32
     * Entity metadata of the entity that is targeted by this relation.
33
     *
34
     * For example for @ManyToMany(type => Category) in Post, inverseEntityMetadata will be metadata of Category entity.
35
     */
36
    inverseEntityMetadata: EntityMetadata
37

38
    /**
39
     * Entity metadata of the junction table.
40
     * Junction tables have their own entity metadata objects.
41
     * Defined only for many-to-many relations.
42
     */
43
    junctionEntityMetadata?: EntityMetadata
44

45
    /**
46
     * Embedded metadata where this relation is.
47
     * If this relation is not in embed then this property value is undefined.
48
     */
49
    embeddedMetadata?: EmbeddedMetadata
50

51
    /**
52
     * Relation type, e.g. is it one-to-one, one-to-many, many-to-one or many-to-many.
53
     */
54
    relationType: RelationType
55

56
    /**
57
     * Target entity to which this relation is applied.
58
     * Target IS NOT equal to entityMetadata.target, because relation
59
     *
60
     * For example for @ManyToMany(type => Category) in Post, target will be Post.
61
     * If @ManyToMany(type => Category) is in Counters which is embedded into Post, target will be Counters.
62
     * If @ManyToMany(type => Category) is in abstract class BaseUser which Post extends, target will be BaseUser.
63
     * Target can be string if its defined in entity schema instead of class.
64
     */
65
    target: Function | string
66

67
    /**
68
     * Target's property name to which relation decorator is applied.
69
     */
70
    propertyName: string
71

72
    /**
73
     * Gets full path to this column property (including relation name).
74
     * Full path is relevant when column is used in embeds (one or multiple nested).
75
     * For example it will return "counters.subcounters.likes".
76
     * If property is not in embeds then it returns just property name of the column.
77
     */
78
    propertyPath: string
79

80
    /**
81
     * Indicates if this is a parent (can be only many-to-one relation) relation in the tree tables.
82
     */
83
    isTreeParent: boolean = false
19✔
84

85
    /**
86
     * Indicates if this is a children (can be only one-to-many relation) relation in the tree tables.
87
     */
88
    isTreeChildren: boolean = false
19✔
89

90
    /**
91
     * Indicates if this relation's column is a primary key.
92
     * Can be used only for many-to-one and owner one-to-one relations.
93
     */
94
    isPrimary: boolean = false
19✔
95

96
    /**
97
     * Indicates if this relation is lazily loaded.
98
     */
99
    isLazy: boolean = false
19✔
100

101
    /**
102
     * Indicates if this relation is eagerly loaded.
103
     */
104
    isEager: boolean = false
19✔
105

106
    /**
107
     * Indicates if persistence is enabled for the relation.
108
     * By default its enabled, but if you want to avoid any changes in the relation to be reflected in the database you can disable it.
109
     * If its disabled you can only change a relation from inverse side of a relation or using relation query builder functionality.
110
     * This is useful for performance optimization since its disabling avoid multiple extra queries during entity save.
111
     */
112
    persistenceEnabled: boolean = true
19✔
113

114
    /**
115
     * When a parent is saved (with cascading but) without a child row that still exists in database, this will control what shall happen to them.
116
     * delete will remove these rows from database. nullify will remove the relation key.
117
     * skip will keep the relation intact. Removal of related item is only possible through its own repo.
118
     */
119
    orphanedRowAction?: "nullify" | "delete" | "soft-delete" | "disable"
120

121
    /**
122
     * If set to true then related objects are allowed to be inserted to the database.
123
     */
124
    isCascadeInsert: boolean = false
19✔
125

126
    /**
127
     * If set to true then related objects are allowed to be updated in the database.
128
     */
129
    isCascadeUpdate: boolean = false
19✔
130

131
    /**
132
     * If set to true then related objects are allowed to be remove from the database.
133
     */
134
    isCascadeRemove: boolean = false
19✔
135

136
    /**
137
     * If set to true then related objects are allowed to be soft-removed from the database.
138
     */
139
    isCascadeSoftRemove: boolean = false
19✔
140

141
    /**
142
     * If set to true then related objects are allowed to be recovered from the database.
143
     */
144
    isCascadeRecover: boolean = false
19✔
145

146
    /**
147
     * Indicates if relation column value can be nullable or not.
148
     */
149
    isNullable: boolean = true
19✔
150

151
    /**
152
     * What to do with a relation on deletion of the row containing a foreign key.
153
     */
154
    onDelete?: OnDeleteType
155

156
    /**
157
     * What to do with a relation on update of the row containing a foreign key.
158
     */
159
    onUpdate?: OnUpdateType
160

161
    /**
162
     * What to do with a relation on update of the row containing a foreign key.
163
     */
164
    deferrable?: DeferrableType
165

166
    /**
167
     * Indicates whether foreign key constraints will be created for join columns.
168
     * Can be used only for many-to-one and owner one-to-one relations.
169
     * Defaults to true.
170
     */
171
    createForeignKeyConstraints: boolean = true
19✔
172

173
    /**
174
     * Gets the property's type to which this relation is applied.
175
     *
176
     * For example for @ManyToMany(type => Category) in Post, target will be Category.
177
     */
178
    type: Function | string
179

180
    /**
181
     * Indicates if this side is an owner of this relation.
182
     */
183
    isOwning: boolean = false
19✔
184

185
    /**
186
     * Checks if this relation's type is "one-to-one".
187
     */
188
    isOneToOne: boolean = false
19✔
189

190
    /**
191
     * Checks if this relation is owner side of the "one-to-one" relation.
192
     * Owner side means this side of relation has a join column in the table.
193
     */
194
    isOneToOneOwner: boolean = false
19✔
195

196
    /**
197
     * Checks if this relation has a join column (e.g. is it many-to-one or one-to-one owner side).
198
     */
199
    isWithJoinColumn: boolean = false
19✔
200

201
    /**
202
     * Checks if this relation is NOT owner side of the "one-to-one" relation.
203
     * NOT owner side means this side of relation does not have a join column in the table.
204
     */
205
    isOneToOneNotOwner: boolean = false
19✔
206

207
    /**
208
     * Checks if this relation's type is "one-to-many".
209
     */
210
    isOneToMany: boolean = false
19✔
211

212
    /**
213
     * Checks if this relation's type is "many-to-one".
214
     */
215
    isManyToOne: boolean = false
19✔
216

217
    /**
218
     * Checks if this relation's type is "many-to-many".
219
     */
220
    isManyToMany: boolean = false
19✔
221

222
    /**
223
     * Checks if this relation's type is "many-to-many", and is owner side of the relationship.
224
     * Owner side means this side of relation has a join table.
225
     */
226
    isManyToManyOwner: boolean = false
19✔
227

228
    /**
229
     * Checks if this relation's type is "many-to-many", and is NOT owner side of the relationship.
230
     * Not owner side means this side of relation does not have a join table.
231
     */
232
    isManyToManyNotOwner: boolean = false
19✔
233

234
    /**
235
     * Gets the property path of the inverse side of the relation.
236
     */
237
    inverseSidePropertyPath: string
238

239
    /**
240
     * Inverse side of the relation set by user.
241
     *
242
     * Inverse side set in the relation can be either string - property name of the column on inverse side,
243
     * either can be a function that accepts a map of properties with the object and returns one of them.
244
     * Second approach is used to achieve type-safety.
245
     */
246
    givenInverseSidePropertyFactory: PropertyTypeFactory<any>
247

248
    /**
249
     * Gets the relation metadata of the inverse side of this relation.
250
     */
251
    inverseRelation?: RelationMetadata
252

253
    /**
254
     * Join table name.
255
     */
256
    joinTableName: string
257

258
    /**
259
     * Foreign keys created for this relation.
260
     */
261
    foreignKeys: ForeignKeyMetadata[] = []
19✔
262

263
    /**
264
     * Join table columns.
265
     * Join columns can be obtained only from owner side of the relation.
266
     * From non-owner side of the relation join columns will be empty.
267
     * If this relation is a many-to-one/one-to-one then it takes join columns from the current entity.
268
     * If this relation is many-to-many then it takes all owner join columns from the junction entity.
269
     */
270
    joinColumns: ColumnMetadata[] = []
19✔
271

272
    /**
273
     * Inverse join table columns.
274
     * Inverse join columns are supported only for many-to-many relations
275
     * and can be obtained only from owner side of the relation.
276
     * From non-owner side of the relation join columns will be undefined.
277
     */
278
    inverseJoinColumns: ColumnMetadata[] = []
19✔
279

280
    // ---------------------------------------------------------------------
281
    // Constructor
282
    // ---------------------------------------------------------------------
283

284
    constructor(options: {
285
        entityMetadata: EntityMetadata
286
        embeddedMetadata?: EmbeddedMetadata
287
        args: RelationMetadataArgs
288
    }) {
289
        this.entityMetadata = options.entityMetadata
19✔
290
        this.embeddedMetadata = options.embeddedMetadata!
19✔
291
        const args = options.args
19✔
292
        this.target = args.target
19✔
293
        this.propertyName = args.propertyName
19✔
294
        this.relationType = args.relationType
19✔
295

296
        if (args.inverseSideProperty)
19✔
297
            this.givenInverseSidePropertyFactory = args.inverseSideProperty
12✔
298

299
        this.isLazy = args.isLazy || false
19✔
300
        // this.isCascadeInsert = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("insert") !== -1);
301
        // this.isCascadeUpdate = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("update") !== -1);
302
        // this.isCascadeRemove = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("remove") !== -1);
303
        // this.isCascadeSoftRemove = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("soft-remove") !== -1);
304
        // this.isCascadeRecover = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("recover") !== -1);
305
        this.isCascadeInsert =
19✔
306
            args.options.cascade === true ||
38!
307
            (Array.isArray(args.options.cascade) &&
308
                args.options.cascade.indexOf("insert") !== -1)
309
        this.isCascadeUpdate =
19✔
310
            args.options.cascade === true ||
38!
311
            (Array.isArray(args.options.cascade) &&
312
                args.options.cascade.indexOf("update") !== -1)
313
        this.isCascadeRemove =
19✔
314
            args.options.cascade === true ||
38!
315
            (Array.isArray(args.options.cascade) &&
316
                args.options.cascade.indexOf("remove") !== -1)
317
        this.isCascadeSoftRemove =
19✔
318
            args.options.cascade === true ||
38!
319
            (Array.isArray(args.options.cascade) &&
320
                args.options.cascade.indexOf("soft-remove") !== -1)
321
        this.isCascadeRecover =
19✔
322
            args.options.cascade === true ||
38!
323
            (Array.isArray(args.options.cascade) &&
324
                args.options.cascade.indexOf("recover") !== -1)
325
        // this.isPrimary = args.options.primary || false;
326
        this.isNullable =
19✔
327
            args.options.nullable === false || this.isPrimary ? false : true
53✔
328
        this.onDelete = args.options.onDelete
19✔
329
        this.onUpdate = args.options.onUpdate
19✔
330
        this.deferrable = args.options.deferrable
19✔
331
        this.createForeignKeyConstraints =
19✔
332
            args.options.createForeignKeyConstraints === false ? false : true
19!
333
        this.isEager = args.options.eager || false
19✔
334
        this.persistenceEnabled =
19✔
335
            args.options.persistence === false ? false : true
19✔
336
        this.orphanedRowAction = args.options.orphanedRowAction || "nullify"
19✔
337
        this.isTreeParent = args.isTreeParent || false
19✔
338
        this.isTreeChildren = args.isTreeChildren || false
19✔
339

340
        if (typeof args.type === "function") {
19!
341
            this.type =
19✔
342
                typeof args.type === "function"
19!
343
                    ? (args.type as () => any)()
344
                    : args.type
UNCOV
345
        } else if (InstanceChecker.isEntitySchema(args.type)) {
×
UNCOV
346
            this.type = args.type.options.name
×
UNCOV
347
        } else if (
×
348
            ObjectUtils.isObject(args.type) &&
×
349
            typeof (args.type as any).name === "string"
350
        ) {
351
            this.type = (args.type as any).name
×
352
        } else {
UNCOV
353
            this.type = args.type as string | Function
×
354
        }
355

356
        this.isOneToOne = this.relationType === "one-to-one"
19✔
357
        this.isOneToMany = this.relationType === "one-to-many"
19✔
358
        this.isManyToOne = this.relationType === "many-to-one"
19✔
359
        this.isManyToMany = this.relationType === "many-to-many"
19✔
360
        this.isOneToOneNotOwner = this.isOneToOne ? true : false
19✔
361
        this.isManyToManyNotOwner = this.isManyToMany ? true : false
19✔
362
    }
363

364
    // ---------------------------------------------------------------------
365
    // Public Methods
366
    // ---------------------------------------------------------------------
367

368
    /**
369
     * Creates join column ids map from the given related entity ids array.
370
     */
371
    getRelationIdMap(entity: ObjectLiteral): ObjectLiteral | undefined {
UNCOV
372
        const joinColumns = this.isOwning
×
373
            ? this.joinColumns
374
            : this.inverseRelation!.joinColumns
UNCOV
375
        const referencedColumns = joinColumns.map(
×
UNCOV
376
            (joinColumn) => joinColumn.referencedColumn!,
×
377
        )
378
        // console.log("entity", entity);
379
        // console.log("referencedColumns", referencedColumns);
UNCOV
380
        return EntityMetadata.getValueMap(entity, referencedColumns)
×
381
    }
382

383
    /**
384
     * Ensures that given object is an entity id map.
385
     * If given id is an object then it means its already id map.
386
     * If given id isn't an object then it means its a value of the id column
387
     * and it creates a new id map with this value and name of the primary column.
388
     */
389
    ensureRelationIdMap(id: any): ObjectLiteral {
390
        if (ObjectUtils.isObject(id)) return id
×
391

392
        const joinColumns = this.isOwning
×
393
            ? this.joinColumns
394
            : this.inverseRelation!.joinColumns
395
        const referencedColumns = joinColumns.map(
×
396
            (joinColumn) => joinColumn.referencedColumn!,
×
397
        )
398

399
        if (referencedColumns.length > 1)
×
400
            throw new TypeORMError(
×
401
                `Cannot create relation id map for a single value because relation contains multiple referenced columns.`,
402
            )
403

404
        return referencedColumns[0].createValueMap(id)
×
405
    }
406

407
    /**
408
     * Extracts column value from the given entity.
409
     * If column is in embedded (or recursive embedded) it extracts its value from there.
410
     */
411
    getEntityValue(
412
        entity: ObjectLiteral,
413
        getLazyRelationsPromiseValue: boolean = false,
2✔
414
    ): any | undefined {
415
        if (entity === null || entity === undefined) return undefined
2!
416
        // extract column value from embeddeds of entity if column is in embedded
417
        if (this.embeddedMetadata) {
2!
418
            // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
419
            // we need to get value of "id" column from the post real entity object
420

421
            // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
UNCOV
422
            const propertyNames = [...this.embeddedMetadata.parentPropertyNames]
×
423

424
            // next we need to access post[data][information][counters][this.propertyName] to get column value from the counters
425
            // this recursive function takes array of generated property names and gets the post[data][information][counters] embed
UNCOV
426
            const extractEmbeddedColumnValue = (
×
427
                propertyNames: string[],
428
                value: ObjectLiteral,
429
            ): any => {
UNCOV
430
                const propertyName = propertyNames.shift()
×
UNCOV
431
                if (propertyName) {
×
UNCOV
432
                    if (value[propertyName]) {
×
UNCOV
433
                        return extractEmbeddedColumnValue(
×
434
                            propertyNames,
435
                            value[propertyName],
436
                        )
437
                    }
UNCOV
438
                    return undefined
×
439
                }
UNCOV
440
                return value
×
441
            }
442

443
            // once we get nested embed object we get its column, e.g. post[data][information][counters][this.propertyName]
UNCOV
444
            const embeddedObject = extractEmbeddedColumnValue(
×
445
                propertyNames,
446
                entity,
447
            )
448

UNCOV
449
            if (this.isLazy) {
×
450
                if (
×
451
                    embeddedObject["__" + this.propertyName + "__"] !==
452
                    undefined
453
                )
454
                    return embeddedObject["__" + this.propertyName + "__"]
×
455

456
                if (getLazyRelationsPromiseValue === true)
×
457
                    return embeddedObject[this.propertyName]
×
458

459
                return undefined
×
460
            }
UNCOV
461
            return embeddedObject
×
462
                ? embeddedObject[
463
                      this.isLazy
×
464
                          ? "__" + this.propertyName + "__"
465
                          : this.propertyName
466
                  ]
467
                : undefined
468
        } else {
469
            // no embeds - no problems. Simply return column name by property name of the entity
470
            if (this.isLazy) {
2!
UNCOV
471
                if (entity["__" + this.propertyName + "__"] !== undefined)
×
UNCOV
472
                    return entity["__" + this.propertyName + "__"]
×
473

UNCOV
474
                if (getLazyRelationsPromiseValue === true)
×
UNCOV
475
                    return entity[this.propertyName]
×
476

UNCOV
477
                return undefined
×
478
            }
479
            return entity[this.propertyName]
2✔
480
        }
481
    }
482

483
    /**
484
     * Sets given entity's relation's value.
485
     * Using of this method helps to set entity relation's value of the lazy and non-lazy relations.
486
     *
487
     * If merge is set to true, it merges given value into currently
488
     */
489
    setEntityValue(entity: ObjectLiteral, value: any): void {
UNCOV
490
        const propertyName = this.isLazy
×
491
            ? "__" + this.propertyName + "__"
492
            : this.propertyName
493

UNCOV
494
        if (this.embeddedMetadata) {
×
495
            // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
UNCOV
496
            const extractEmbeddedColumnValue = (
×
497
                embeddedMetadatas: EmbeddedMetadata[],
498
                map: ObjectLiteral,
499
            ): any => {
500
                // if (!object[embeddedMetadata.propertyName])
501
                //     object[embeddedMetadata.propertyName] = embeddedMetadata.create();
502

UNCOV
503
                const embeddedMetadata = embeddedMetadatas.shift()
×
UNCOV
504
                if (embeddedMetadata) {
×
UNCOV
505
                    if (!map[embeddedMetadata.propertyName])
×
UNCOV
506
                        map[embeddedMetadata.propertyName] =
×
507
                            embeddedMetadata.create()
508

UNCOV
509
                    extractEmbeddedColumnValue(
×
510
                        embeddedMetadatas,
511
                        map[embeddedMetadata.propertyName],
512
                    )
UNCOV
513
                    return map
×
514
                }
UNCOV
515
                map[propertyName] = value
×
UNCOV
516
                return map
×
517
            }
UNCOV
518
            return extractEmbeddedColumnValue(
×
519
                [...this.embeddedMetadata.embeddedMetadataTree],
520
                entity,
521
            )
522
        } else {
UNCOV
523
            entity[propertyName] = value
×
524
        }
525
    }
526

527
    /**
528
     * Creates entity id map from the given entity ids array.
529
     */
530
    createValueMap(value: any) {
531
        // extract column value from embeds of entity if column is in embedded
532
        if (this.embeddedMetadata) {
74!
533
            // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
534
            // we need to get value of "id" column from the post real entity object and return it in a
535
            // { data: { information: { counters: { id: ... } } } } format
536

537
            // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
UNCOV
538
            const propertyNames = [...this.embeddedMetadata.parentPropertyNames]
×
539

540
            // now need to access post[data][information][counters] to get column value from the counters
541
            // and on each step we need to create complex literal object, e.g. first { data },
542
            // then { data: { information } }, then { data: { information: { counters } } },
543
            // then { data: { information: { counters: [this.propertyName]: entity[data][information][counters][this.propertyName] } } }
544
            // this recursive function helps doing that
UNCOV
545
            const extractEmbeddedColumnValue = (
×
546
                propertyNames: string[],
547
                map: ObjectLiteral,
548
            ): any => {
UNCOV
549
                const propertyName = propertyNames.shift()
×
UNCOV
550
                if (propertyName) {
×
UNCOV
551
                    map[propertyName] = {}
×
UNCOV
552
                    extractEmbeddedColumnValue(propertyNames, map[propertyName])
×
UNCOV
553
                    return map
×
554
                }
UNCOV
555
                map[this.propertyName] = value
×
UNCOV
556
                return map
×
557
            }
UNCOV
558
            return extractEmbeddedColumnValue(propertyNames, {})
×
559
        } else {
560
            // no embeds - no problems. Simply return column property name and its value of the entity
561
            return { [this.propertyName]: value }
74✔
562
        }
563
    }
564

565
    // ---------------------------------------------------------------------
566
    // Builder Methods
567
    // ---------------------------------------------------------------------
568

569
    /**
570
     * Builds some depend relation metadata properties.
571
     * This builder method should be used only after embedded metadata tree was build.
572
     */
573
    build() {
574
        this.propertyPath = this.buildPropertyPath()
55✔
575
    }
576

577
    /**
578
     * Registers given foreign keys in the relation.
579
     * This builder method should be used to register foreign key in the relation.
580
     */
581
    registerForeignKeys(...foreignKeys: ForeignKeyMetadata[]) {
582
        this.foreignKeys.push(...foreignKeys)
11✔
583
    }
584

585
    /**
586
     * Registers given join columns in the relation.
587
     * This builder method should be used to register join column in the relation.
588
     */
589
    registerJoinColumns(
590
        joinColumns: ColumnMetadata[] = [],
×
591
        inverseJoinColumns: ColumnMetadata[] = [],
15✔
592
    ) {
593
        this.joinColumns = joinColumns
17✔
594
        this.inverseJoinColumns = inverseJoinColumns
17✔
595
        this.isOwning =
17✔
596
            this.isManyToOne ||
60✔
597
            ((this.isManyToMany || this.isOneToOne) &&
598
                this.joinColumns.length > 0)
599
        this.isOneToOneOwner = this.isOneToOne && this.isOwning
17✔
600
        this.isOneToOneNotOwner = this.isOneToOne && !this.isOwning
17✔
601
        this.isManyToManyOwner = this.isManyToMany && this.isOwning
17✔
602
        this.isManyToManyNotOwner = this.isManyToMany && !this.isOwning
17✔
603
        this.isWithJoinColumn = this.isManyToOne || this.isOneToOneOwner
17✔
604
    }
605

606
    /**
607
     * Registers a given junction entity metadata.
608
     * This builder method can be called after junction entity metadata for the many-to-many relation was created.
609
     */
610
    registerJunctionEntityMetadata(junctionEntityMetadata: EntityMetadata) {
611
        this.junctionEntityMetadata = junctionEntityMetadata
2✔
612
        this.joinTableName = junctionEntityMetadata.tableName
2✔
613
        if (this.inverseRelation) {
2!
UNCOV
614
            this.inverseRelation.junctionEntityMetadata = junctionEntityMetadata
×
UNCOV
615
            this.joinTableName = junctionEntityMetadata.tableName
×
616
        }
617
    }
618

619
    /**
620
     * Builds inverse side property path based on given inverse side property factory.
621
     * This builder method should be used only after properties map of the inverse entity metadata was build.
622
     */
623
    buildInverseSidePropertyPath(): string {
624
        if (this.givenInverseSidePropertyFactory) {
19✔
625
            const ownerEntityPropertiesMap =
626
                this.inverseEntityMetadata.propertiesMap
12✔
627
            if (typeof this.givenInverseSidePropertyFactory === "function")
12✔
628
                return this.givenInverseSidePropertyFactory(
12✔
629
                    ownerEntityPropertiesMap,
630
                )
631

UNCOV
632
            if (typeof this.givenInverseSidePropertyFactory === "string")
×
UNCOV
633
                return this.givenInverseSidePropertyFactory
×
634
        } else if (
7!
635
            this.isTreeParent &&
7!
636
            this.entityMetadata.treeChildrenRelation
637
        ) {
UNCOV
638
            return this.entityMetadata.treeChildrenRelation.propertyName
×
639
        } else if (
7!
640
            this.isTreeChildren &&
7!
641
            this.entityMetadata.treeParentRelation
642
        ) {
UNCOV
643
            return this.entityMetadata.treeParentRelation.propertyName
×
644
        }
645

646
        return ""
7✔
647
    }
648

649
    /**
650
     * Builds relation's property path based on its embedded tree.
651
     */
652
    buildPropertyPath(): string {
653
        if (
55✔
654
            !this.embeddedMetadata ||
55!
655
            !this.embeddedMetadata.parentPropertyNames.length
656
        )
657
            return this.propertyName
55✔
658

UNCOV
659
        return (
×
660
            this.embeddedMetadata.parentPropertyNames.join(".") +
661
            "." +
662
            this.propertyName
663
        )
664
    }
665
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc