Skip to content

Commit

Permalink
fix: #487 additional fixes and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ascott18 committed Jan 29, 2025
1 parent ecf28fa commit e36dba3
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext
{
public class OneToOneParent
{
public int Id { get; set; }

// [InverseProperty(nameof(Child1.Parent))]
// [InverseProperty(nameof(Child1.Parent))]
public OneToOneChild1 Child1 { get; set; }

[InverseProperty(nameof(Child2.Parent))] // Can be specified, but not required.
public OneToOneChild2 Child2 { get; set; }

public List<OneToOneManyChildren> ManyChildren { get; set; } = [];
}

public class OneToOneChild1
Expand All @@ -27,4 +35,12 @@ public class OneToOneChild2
public int ParentId { get; set; }
public OneToOneParent Parent { get; set; }
}

public class OneToOneManyChildren
{
public int Id { get; set; }

public int OneToOneParentId { get; set; }
public OneToOneParent OneToOneParent { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class AppDbContext : DbContext
public DbSet<OneToOneParent> OneToOneParents { get; set; }
public DbSet<OneToOneChild1> OneToOneChild1s { get; set; }
public DbSet<OneToOneChild2> OneToOneChild2s { get; set; }
public DbSet<OneToOneManyChildren> OneToOneManyChildren { get; set; }


public AppDbContext() : this(Guid.NewGuid().ToString()) { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
using IntelliTect.Coalesce.TypeDefinition;
using IntelliTect.Coalesce.TypeDefinition.Enums;
using Microsoft.EntityFrameworkCore;
using System.Text;
using Xunit;
using System.ComponentModel;
using static IntelliTect.Coalesce.DataAnnotations.DateTypeAttribute;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace IntelliTect.Coalesce.Tests.TypeDefinition
{
Expand Down Expand Up @@ -298,18 +296,44 @@ public void OneToOne_ParentNavigations_HasCorrectMetadata(PropertyViewModelData
}

[Theory]
[PropertyViewModelData<OneToOneChild1>(nameof(OneToOneChild1.Parent))]
[PropertyViewModelData<OneToOneChild2>(nameof(OneToOneChild2.Parent))]
public void OneToOne_ChildNavigations_HasCorrectMetadata(PropertyViewModelData data)
[PropertyViewModelData<OneToOneChild1>(nameof(OneToOneChild1.Parent), "Child1")]
[PropertyViewModelData<OneToOneChild2>(nameof(OneToOneChild2.Parent), "Child2")]
public void OneToOne_ChildNavigations_HasCorrectMetadata(PropertyViewModelData data, string inverse)
{
PropertyViewModel vm = data;
Assert.Equal(PropertyRole.ReferenceNavigation, vm.Role);
Assert.Equal(vm.Parent.PropertyByName("ParentId"), vm.ForeignKeyProperty);
Assert.Equal(inverse, vm.InverseProperty.Name);
}

[Theory]
[PropertyViewModelData<OneToOneManyChildren>(nameof(OneToOneManyChildren.OneToOneParent))]
[Description("https://github.com/IntelliTect/Coalesce/commit/513db257dda32b99099355f1a6de0f5fbf367f5a")]
public void ReferenceNavigation_HasCorrectFkWhenPrincipalAlsoParticipatesInOneToOne(PropertyViewModelData data)
{
PropertyViewModel prop = data;

// Precodition: Principal participates in a one-to-one
Assert.True(prop.Object!.PrimaryKey.IsForeignKey);

// Correct foreign key here is ParentId, not the child's PK.
Assert.Equal(PropertyRole.ReferenceNavigation, prop.Role);
Assert.Equal(nameof(OneToOneManyChildren.OneToOneParentId), prop.ForeignKeyProperty.Name);
}

[Theory]
[PropertyViewModelData<OneToOneParent>(nameof(OneToOneParent.ManyChildren))]
[Description("https://github.com/IntelliTect/Coalesce/commit/513db257dda32b99099355f1a6de0f5fbf367f5a")]
public void CollectionNavigation_HasCorrectFkWhenPrincipalAlsoParticipatesInOneToOne(PropertyViewModelData data)
{
PropertyViewModel prop = data;

// Precodition: Principal participates in a one-to-one
Assert.True(prop.EffectiveParent.PrimaryKey.IsForeignKey);

// We currently assume that reverence inverses are only ever collection navigations.
// This could change in the future, but at least for now the typescript types don't allow
// for representing this here, which is fine and doesn't currently hurt anything.
Assert.Null(vm.InverseProperty);
// Correct foreign key here is ParentId, not the child's PK.
Assert.Equal(PropertyRole.CollectionNavigation, prop.Role);
Assert.Equal(nameof(OneToOneManyChildren.OneToOneParentId), prop.ForeignKeyProperty.Name);
}

}
Expand Down
30 changes: 27 additions & 3 deletions src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,9 @@ public bool IsPrimaryKey
/// Returns true if this property is a foreign key.
/// Guarantees that <see cref="ForeignKeyPrincipalType"/> is not null.
/// </summary>
public bool IsForeignKey => ForeignKeyPrincipalType != null;
public bool IsForeignKey =>
ForeignKeyPrincipalType != null ||
EffectiveParent.ClientProperties.Any(p => p != this && p.ForeignKeyProperty == this);


public DatabaseGeneratedOption DatabaseGenerated
Expand Down Expand Up @@ -510,13 +512,28 @@ public PropertyViewModel? ForeignKeyProperty

prop = EffectiveParent.PropertyByName(name);

if (prop == null && InverseProperty?.Role == PropertyRole.ReferenceNavigation)
if (prop == null)
{
// If the other side of the relationship is also a reference navigation,
// this is a 1-to-1. If there's otherwise no FK specified, assume that the PK
// of this type is the FK we're looking for here.
// See test models "OneToOneParent"/"OneToOneChild1" for example.
return EffectiveParent.PrimaryKey;

if (this.HasAttribute<InversePropertyAttribute>() &&
InverseProperty?.Role == PropertyRole.ReferenceNavigation)
{
// Only look at `InverseProperty` if explicitly annotated.
// Otherwise, this will cause infinite recursion.
return EffectiveParent.PrimaryKey;
}

if (Object!.ClientProperties.Any(p =>
p.ForeignKeyProperty == Object.PrimaryKey &&
p.Type == this.EffectiveParent.Type
))
{
return EffectiveParent.PrimaryKey;
}
}
}

Expand Down Expand Up @@ -694,6 +711,13 @@ public PropertyViewModel? InverseProperty
return Object.ClientProperties.FirstOrDefault(p =>
p.Role == PropertyRole.CollectionNavigation
&& p.InverseProperty == this
)
// Or, try to find 1-to-1 reference navigations
?? Object.ClientProperties.FirstOrDefault(p =>
p.Role == PropertyRole.ReferenceNavigation &&
p.Type == EffectiveParent.Type &&
p.ForeignKeyProperty?.IsPrimaryKey == true

);
}

Expand Down
5 changes: 4 additions & 1 deletion src/coalesce-vue/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,10 @@ export interface ModelReferenceNavigationProperty
// PK is the foreign key into the parent's type.
readonly foreignKey: ForeignKeyProperty | PrimaryKeyProperty;
readonly principalKey: PrimaryKeyProperty;
readonly inverseNavigation?: ModelCollectionNavigationProperty;
// Note: Can be a `ModelReferenceNavigationProperty` for a 1-to-1
readonly inverseNavigation?:
| ModelReferenceNavigationProperty
| ModelCollectionNavigationProperty;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/test-targets/api-clients.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions src/test-targets/metadata.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions src/test-targets/models.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions src/test-targets/viewmodels.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e36dba3

Please sign in to comment.