Skip to content

Commit 32ee93a

Browse files
authored
PSP-9452: Split map search for PID PIN (#4480)
* Lint fixes, remove dead code * Split map search for PID PIN * Test updates * Update backend filter models * Update EF queries * Test updates * Support adding DB views to PIMS testing DB context * Test updates * Code cleanup * PR feedback * Add proxy microservice to "make infra" command
1 parent c083c47 commit 32ee93a

24 files changed

+964
-277
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ refresh: | down build up ## Recreates local docker environment
180180
.PHONY: infra
181181
infra: ## Starts infrastructure containers (e.g. database, geoserver). Useful for local debugging
182182
@echo "$(P) Starting up infrastructure containers..."
183-
@"$(MAKE)" start n="database geoserver grafana prometheus"
183+
@"$(MAKE)" start n="database geoserver grafana prometheus proxy"
184184

185185
start: ## Starts the local containers (n=service name)
186186
@echo "$(P) Starting client and server containers..."

source/backend/api/Areas/Property/Models/PropertyFilterModel.cs

+14-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ public class PropertyFilterModel : PageFilter
1919
public string Address { get; set; }
2020

2121
/// <summary>
22-
/// get/set - The pin or pid property identifier.
22+
/// get/set - The pid property identifier.
2323
/// </summary>
24-
public string PinOrPid { get; set; }
24+
public string Pid { get; set; }
25+
26+
/// <summary>
27+
/// get/set - The pin property identifier.
28+
/// </summary>
29+
public string Pin { get; set; }
2530

2631
/// <summary>
2732
/// get/set - The property plan number.
@@ -87,7 +92,8 @@ public PropertyFilterModel(Dictionary<string, Microsoft.Extensions.Primitives.St
8792
}
8893
Sort = tempSort.ToArray();
8994

90-
PinOrPid = filter.GetStringValue(nameof(PinOrPid));
95+
Pid = filter.GetStringValue(nameof(Pid));
96+
Pin = filter.GetStringValue(nameof(Pin));
9197
Address = filter.GetStringValue(nameof(Address));
9298
PlanNumber = filter.GetStringValue(nameof(PlanNumber));
9399
Historical = filter.GetStringValue(nameof(Historical));
@@ -98,7 +104,7 @@ public PropertyFilterModel(Dictionary<string, Microsoft.Extensions.Primitives.St
98104
#region Methods
99105

100106
/// <summary>
101-
/// Convert to a ParcelFilter.
107+
/// Convert to a PropertyFilter.
102108
/// </summary>
103109
/// <param name="model"></param>
104110
public static explicit operator PropertyFilter(PropertyFilterModel model)
@@ -109,7 +115,8 @@ public static explicit operator PropertyFilter(PropertyFilterModel model)
109115
Quantity = model.Quantity,
110116
Sort = model.Sort,
111117

112-
PinOrPid = model.PinOrPid,
118+
Pid = model.Pid,
119+
Pin = model.Pin,
113120
Address = model.Address,
114121
PlanNumber = model.PlanNumber,
115122
Historical = model.Historical,
@@ -126,7 +133,8 @@ public static explicit operator PropertyFilter(PropertyFilterModel model)
126133
public override bool IsValid()
127134
{
128135
return base.IsValid()
129-
|| !string.IsNullOrWhiteSpace(PinOrPid)
136+
|| !string.IsNullOrWhiteSpace(Pid)
137+
|| !string.IsNullOrWhiteSpace(Pin)
130138
|| !string.IsNullOrWhiteSpace(Historical)
131139
|| !string.IsNullOrWhiteSpace(Address);
132140
}

source/backend/dal/Helpers/Extensions/PropertyViewExtensions.cs

+15-16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
using System;
32
using System.Linq;
43
using System.Security.Claims;
@@ -7,8 +6,6 @@
76
using Microsoft.EntityFrameworkCore;
87
using Pims.Core.Extensions;
98
using Pims.Dal.Entities;
10-
using Pims.Core.Security;
11-
using Entity = Pims.Dal.Entities;
129

1310
namespace Pims.Dal.Helpers.Extensions
1411
{
@@ -25,18 +22,18 @@ public static class PropertyViewExtensions
2522
/// <param name="user"></param>
2623
/// <param name="filter"></param>
2724
/// <returns></returns>
28-
public static IQueryable<Entity.PimsPropertyVw> GeneratePropertyQuery(this PimsContext context, ClaimsPrincipal user, Entity.Models.PropertyFilter filter)
25+
public static IQueryable<PimsPropertyVw> GeneratePropertyQuery(this PimsContext context, ClaimsPrincipal user, Entities.Models.PropertyFilter filter)
2926
{
30-
filter.ThrowIfNull(nameof(filter));
31-
filter.ThrowIfNull(nameof(user));
27+
ArgumentNullException.ThrowIfNull(filter, nameof(filter));
28+
ArgumentNullException.ThrowIfNull(user, nameof(user));
3229

3330
var query = context.PimsPropertyVws
3431
.AsNoTracking();
3532

3633
var predicate = GenerateCommonPropertyQuery(context, user, filter);
3734
query = query.Where(predicate);
3835

39-
if (filter.Sort?.Any() == true)
36+
if (filter.Sort is not null && filter.Sort.Length > 0)
4037
{
4138
query = query.OrderByProperty(true, filter.Sort);
4239
}
@@ -54,22 +51,24 @@ public static class PropertyViewExtensions
5451
/// <param name="user"></param>
5552
/// <param name="filter"></param>
5653
/// <returns></returns>
57-
private static ExpressionStarter<PimsPropertyVw> GenerateCommonPropertyQuery(PimsContext context, ClaimsPrincipal user, Entity.Models.PropertyFilter filter)
54+
private static ExpressionStarter<PimsPropertyVw> GenerateCommonPropertyQuery(PimsContext context, ClaimsPrincipal user, Entities.Models.PropertyFilter filter)
5855
{
59-
filter.ThrowIfNull(nameof(filter));
60-
filter.ThrowIfNull(nameof(user));
61-
62-
// Check if user has the ability to view sensitive properties.
63-
var viewSensitive = user.HasPermission(Permissions.SensitiveView);
56+
ArgumentNullException.ThrowIfNull(filter, nameof(filter));
57+
ArgumentNullException.ThrowIfNull(user, nameof(user));
6458

6559
var predicateBuilder = PredicateBuilder.New<PimsPropertyVw>(p => true);
6660

67-
if (!string.IsNullOrWhiteSpace(filter.PinOrPid))
61+
if (!string.IsNullOrWhiteSpace(filter.Pid))
6862
{
6963
// note - 2 part search required. all matches found by removing leading 0's, then matches filtered in subsequent step. This is because EF core does not support an lpad method.
7064
Regex nonInteger = new Regex("[^\\d]");
71-
var formattedPidPin = Convert.ToInt32(nonInteger.Replace(filter.PinOrPid, string.Empty)).ToString();
72-
predicateBuilder = predicateBuilder.And(p => EF.Functions.Like(p.Pid.ToString(), $"%{formattedPidPin}%") || EF.Functions.Like(p.Pin.ToString(), $"%{formattedPidPin}%"));
65+
var formattedPid = Convert.ToInt32(nonInteger.Replace(filter.Pid, string.Empty)).ToString();
66+
predicateBuilder = predicateBuilder.And(p => EF.Functions.Like(p.Pid.ToString(), $"%{formattedPid}%"));
67+
}
68+
69+
if (!string.IsNullOrWhiteSpace(filter.Pin))
70+
{
71+
predicateBuilder = predicateBuilder.And(p => EF.Functions.Like(p.Pin.ToString(), $"%{filter.Pin}%"));
7372
}
7473
if (!string.IsNullOrWhiteSpace(filter.Address))
7574
{

source/backend/dal/Models/PropertyFilter.cs

+15-8
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@ public class PropertyFilter : PageFilter
1212
#region Properties
1313

1414
/// <summary>
15-
/// get/set - The pin or pid property.
15+
/// get/set - The pid property identifier.
1616
/// </summary>
17-
public string PinOrPid { get; set; }
17+
public string Pid { get; set; }
18+
19+
/// <summary>
20+
/// get/set - The pin property identifier.
21+
/// </summary>
22+
public string Pin { get; set; }
1823

1924
/// <summary>
2025
/// get/set - The property address.
@@ -58,10 +63,11 @@ public PropertyFilter(Dictionary<string, Microsoft.Extensions.Primitives.StringV
5863
// We want case-insensitive query parameter properties.
5964
var filter = new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>(query, StringComparer.OrdinalIgnoreCase);
6065

61-
this.Address = filter.GetStringValue(nameof(this.Address));
62-
this.PinOrPid = filter.GetStringValue(nameof(this.PinOrPid));
63-
this.PlanNumber = filter.GetStringValue(nameof(this.PlanNumber));
64-
this.Ownership = filter.GetStringArrayValue(nameof(this.Ownership));
66+
Address = filter.GetStringValue(nameof(Address));
67+
Pid = filter.GetStringValue(nameof(Pid));
68+
Pin = filter.GetStringValue(nameof(Pin));
69+
PlanNumber = filter.GetStringValue(nameof(PlanNumber));
70+
Ownership = filter.GetStringArrayValue(nameof(Ownership));
6571
}
6672
#endregion
6773

@@ -74,8 +80,9 @@ public PropertyFilter(Dictionary<string, Microsoft.Extensions.Primitives.StringV
7480
public override bool IsValid()
7581
{
7682
return base.IsValid()
77-
|| !string.IsNullOrWhiteSpace(this.PinOrPid)
78-
|| !string.IsNullOrWhiteSpace(this.Address);
83+
|| !string.IsNullOrWhiteSpace(Pid)
84+
|| !string.IsNullOrWhiteSpace(Pin)
85+
|| !string.IsNullOrWhiteSpace(Address);
7986
}
8087
#endregion
8188
}

source/backend/dal/Repositories/PropertyRepository.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
using Pims.Core.Exceptions;
1111
using Pims.Core.Extensions;
1212
using Pims.Core.Helpers;
13+
using Pims.Core.Security;
1314
using Pims.Dal.Entities;
1415
using Pims.Dal.Entities.Models;
1516
using Pims.Dal.Helpers.Extensions;
16-
using Pims.Core.Security;
1717

1818
namespace Pims.Dal.Repositories
1919
{
@@ -56,25 +56,25 @@ public int Count()
5656
/// <returns></returns>
5757
public Paged<PimsPropertyVw> GetPage(PropertyFilter filter)
5858
{
59-
this.User.ThrowIfNotAuthorized(Permissions.PropertyView);
59+
User.ThrowIfNotAuthorized(Permissions.PropertyView);
6060
filter.ThrowIfNull(nameof(filter));
6161
if (!filter.IsValid())
6262
{
6363
throw new ArgumentException("Argument must have a valid filter", nameof(filter));
6464
}
6565

6666
var skip = (filter.Page - 1) * filter.Quantity;
67-
var query = Context.GeneratePropertyQuery(this.User, filter);
67+
var query = Context.GeneratePropertyQuery(User, filter);
6868
var items = query
6969
.Skip(skip)
7070
.Take(filter.Quantity)
7171
.ToArray();
7272

73-
if (!string.IsNullOrWhiteSpace(filter.PinOrPid))
73+
if (!string.IsNullOrWhiteSpace(filter.Pid))
7474
{
7575
Regex nonInteger = new Regex("[^\\d]");
76-
var formattedPidPin = nonInteger.Replace(filter.PinOrPid, string.Empty);
77-
items = items.Where(i => i.Pid.ToString().PadLeft(9, '0').Contains(formattedPidPin) || i.Pin.ToString().Contains(formattedPidPin)).ToArray();
76+
var formattedPid = nonInteger.Replace(filter.Pid, string.Empty);
77+
items = items.Where(i => i.Pid.ToString().PadLeft(9, '0').Contains(formattedPid)).ToArray();
7878
}
7979

8080
return new Paged<PimsPropertyVw>(items, filter.Page, filter.Quantity, query.Count());

source/backend/tests/core/DatabaseHelper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public static PimsContext CreatePimsContext(this TestHelper helper, string dbNam
9393
var serializerOptions = new Mock<IOptions<JsonSerializerOptions>>();
9494
helper.AddSingleton(serializerOptions);
9595

96-
var context = new PimsContext(options, contextAccessor.Object, serializerOptions.Object);
96+
PimsContext context = new PimsTestContext(options, contextAccessor.Object, serializerOptions.Object);
9797

9898
if (ensureDeleted)
9999
{

source/backend/tests/core/Entities/PropertyHelper.cs

+11
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public static PimsPropertyVw CreatePropertyView(int pid, int? pin = null, PimsPr
7070
var property = new PimsPropertyVw()
7171
{
7272
PropertyId = pid,
73+
Pid = pid,
7374
Pin = pin,
7475
IsRetired = false,
7576
PropertyTypeCode = type.PropertyTypeCode,
@@ -126,9 +127,19 @@ public static PimsProperty CreateProperty(this PimsContext context, int pid, int
126127
public static PimsPropertyVw CreatePropertyView(this PimsContext context, int pid, int? pin = null, PimsPropertyType type = null, PimsAddress address = null, PimsPropertyTenureType tenure = null, PimsAreaUnitType areaUnit = null, PimsDataSourceType dataSource = null, PimsPropertyStatusType status = null, Geometry location = null, bool isRetired = false)
127128
{
128129
type ??= context.PimsPropertyTypes.FirstOrDefault() ?? throw new InvalidOperationException("Unable to find a property type.");
130+
tenure ??= context.PimsPropertyTenureTypes.FirstOrDefault() ?? throw new InvalidOperationException("Unable to find a property tenure type.");
131+
status ??= context.PimsPropertyStatusTypes.FirstOrDefault() ?? throw new InvalidOperationException("Unable to find a property status type.");
132+
dataSource ??= context.PimsDataSourceTypes.FirstOrDefault() ?? throw new InvalidOperationException("Unable to find a property data source type.");
133+
areaUnit ??= context.PimsAreaUnitTypes.FirstOrDefault() ?? throw new InvalidOperationException("Unable to find a property area unit type.");
129134
address ??= context.CreateAddress(pid, "12342 Test Street");
130135
var property = CreatePropertyView(pid, pin, type, address);
136+
property.Location = location;
131137
property.IsRetired = isRetired;
138+
property.PropertyStatusTypeCode = status.PropertyStatusTypeCode;
139+
property.PropertyDataSourceTypeCode = dataSource.DataSourceTypeCode;
140+
property.PropertyTenureTypeCode = tenure.PropertyTenureTypeCode;
141+
property.PropertyAreaUnitTypeCode = areaUnit.AreaUnitTypeCode;
142+
132143
context.PimsPropertyVws.Add(property);
133144
return property;
134145
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Text.Json;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Options;
5+
using Pims.Dal;
6+
using Pims.Dal.Entities;
7+
8+
namespace Pims.Core.Test
9+
{
10+
/// <summary>
11+
/// PimsTestContext class, provides a data context to manage the datasource for the PIMS application (specific to UNIT TESTS).
12+
/// </summary>
13+
public class PimsTestContext : PimsContext
14+
{
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="PimsTestContext"/> class.
17+
/// </summary>
18+
public PimsTestContext()
19+
: base()
20+
{
21+
}
22+
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="PimsTestContext"/> class.
25+
/// </summary>
26+
/// <param name="options">The options.</param>
27+
/// <param name="httpContextAccessor">Provides access to the current IHttpContextAccessor.HttpContext, if one is available.</param>
28+
/// <param name="serializerOptions">The serializer options.</param>
29+
public PimsTestContext(DbContextOptions<PimsContext> options, IHttpContextAccessor httpContextAccessor = null, IOptions<JsonSerializerOptions> serializerOptions = null)
30+
: base(options, httpContextAccessor, serializerOptions)
31+
{
32+
}
33+
34+
protected override void OnModelCreating(ModelBuilder modelBuilder)
35+
{
36+
base.OnModelCreating(modelBuilder);
37+
38+
// This is needed so unit tests for DB Views work with in-memory DB
39+
modelBuilder.Entity<PimsPropertyVw>(entity =>
40+
{
41+
entity.HasKey(x => x.PropertyId);
42+
});
43+
}
44+
}
45+
}

source/backend/tests/unit/api/Controllers/Property/SearchControllerTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class SearchControllerTest
2929
{
3030
new object [] { new PropertyFilterModel() },
3131
new object [] { new PropertyFilterModel() { Address = "Address" } },
32-
new object [] { new PropertyFilterModel() { PinOrPid = "999999" } },
32+
new object [] { new PropertyFilterModel() { Pid = "999999" } },
3333
};
3434

3535
public readonly static IEnumerable<object[]> PropertyQueryFilters = new List<object[]>()

source/backend/tests/unit/api/Controllers/Reports/PropertyControllerTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class PropertyControllerTest
3030
{
3131
new object [] { new PropertyFilterModel() },
3232
new object [] { new PropertyFilterModel() { Address = "Address" } },
33-
new object [] { new PropertyFilterModel() { PinOrPid = "999999" } },
33+
new object [] { new PropertyFilterModel() { Pid = "999999" } },
3434
};
3535

3636
public static IEnumerable<object[]> PropertyQueryFilters = new List<object[]>()

source/backend/tests/unit/dal/Entities/PropertyFilterTest.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,23 @@ public void PropertyFilter_Default_Constructor()
2323
// Assert
2424
filter.Page.Should().Be(1);
2525
filter.Quantity.Should().Be(10);
26-
filter.PinOrPid.Should().BeNull();
26+
filter.Pid.Should().BeNull();
27+
filter.Pin.Should().BeNull();
2728
}
2829

2930
[Fact]
3031
public void PropertyFilter_Constructor_03()
3132
{
3233
// Arrange
33-
var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery("?page=2&quantity=3&pinOrPid=323423&sort=one,two");
34+
var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery("?page=2&quantity=3&pid=323423&sort=one,two");
3435

3536
// Act
3637
var filter = new PropertyFilter(query);
3738

3839
// Assert
3940
filter.Page.Should().Be(2);
4041
filter.Quantity.Should().Be(3);
41-
filter.PinOrPid.Should().Be("323423");
42+
filter.Pid.Should().Be("323423");
4243
filter.Sort.Should().BeEquivalentTo(new[] { "one", "two" });
4344
}
4445

@@ -47,7 +48,7 @@ public void PropertyFilter_Constructor_03()
4748
public void PropertyFilter_IsValid()
4849
{
4950
// Arrange
50-
var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery("?pinOrPid=323423");
51+
var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery("?pid=323423");
5152
var filter = new PropertyFilter(query);
5253

5354
// Act

source/backend/tests/unit/dal/Repositories/PropertyActivityRepositoryTest.cs

-12
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@ public class PropertyActivityRepositoryTest
1818
{
1919
private TestHelper _helper;
2020

21-
#region Data
22-
public static IEnumerable<object[]> AllPropertyFilters =>
23-
new List<object[]>
24-
{
25-
new object[] { new PropertyFilter() { PinOrPid = "111-111-111" }, 1 },
26-
new object[] { new PropertyFilter() { PinOrPid = "111" }, 2 },
27-
new object[] { new PropertyFilter() { Address = "12342 Test Street" }, 5 },
28-
new object[] { new PropertyFilter() { Page = 1, Quantity = 10 }, 6 },
29-
new object[] { new PropertyFilter(), 6 },
30-
};
31-
#endregion
32-
3321
public PropertyActivityRepositoryTest()
3422
{
3523
_helper = new TestHelper();

0 commit comments

Comments
 (0)